Repository: sonicnext-dev/MarathonRecomp Branch: main Commit: 1be15712f64d Files: 586 Total size: 2.4 MB Directory structure: gitextract_3ry7zdqc/ ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── dev_report.yml │ └── workflows/ │ ├── validate-external.yml │ ├── validate-internal.yml │ └── validate.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakePresets.json ├── COPYING ├── MarathonRecomp/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── api/ │ │ ├── Chao/ │ │ │ └── CSD/ │ │ │ ├── Core/ │ │ │ │ ├── csdBase.h │ │ │ │ ├── csdRCObject.h │ │ │ │ ├── csdRCObjectImp.h │ │ │ │ ├── csdRCPtr.h │ │ │ │ ├── csdRCPtrAbs.h │ │ │ │ └── csdTexList.h │ │ │ └── Manager/ │ │ │ ├── csdmMotionPattern.h │ │ │ ├── csdmNode.h │ │ │ ├── csdmNodeObserver.h │ │ │ ├── csdmObserverBase.h │ │ │ ├── csdmProject.h │ │ │ ├── csdmResourceBase.h │ │ │ ├── csdmScene.h │ │ │ ├── csdmSceneObserver.h │ │ │ └── csdmSubjectBase.h │ │ ├── Marathon.h │ │ ├── Marathon.inl │ │ ├── README.md │ │ ├── Sonicteam/ │ │ │ ├── Actor.h │ │ │ ├── ActorManager.h │ │ │ ├── AlertWindowTask.h │ │ │ ├── AppMarathon.h │ │ │ ├── AppMarathon.inl │ │ │ ├── AudioEngineXenon.h │ │ │ ├── ButtonWindowTask.h │ │ │ ├── CObjBalloonIconDrawable.h │ │ │ ├── Camera/ │ │ │ │ ├── CameraMode.h │ │ │ │ ├── CameraModeManager.h │ │ │ │ ├── Cameraman.h │ │ │ │ └── SonicCamera.h │ │ │ ├── CommonObjectHint.h │ │ │ ├── CsdLink.h │ │ │ ├── CsdManager.h │ │ │ ├── CsdObject.h │ │ │ ├── CsdResource.h │ │ │ ├── DocMarathonImp.h │ │ │ ├── DocMarathonState.h │ │ │ ├── Enemy/ │ │ │ │ ├── EnemyShot.h │ │ │ │ ├── EnemyShotNormal.h │ │ │ │ └── EnemyShotPoint.h │ │ │ ├── Fixture.h │ │ │ ├── Game.h │ │ │ ├── GameImp.h │ │ │ ├── GameMode.h │ │ │ ├── Globals.h │ │ │ ├── HUDButtonWindow.h │ │ │ ├── HUDCALLBACK.h │ │ │ ├── HUDGoldMedal.h │ │ │ ├── HUDLimitTime.h │ │ │ ├── HUDLoading.h │ │ │ ├── HUDMainDisplay.h │ │ │ ├── HUDMainMenu.h │ │ │ ├── HUDMessageWindow.h │ │ │ ├── HUDOption.h │ │ │ ├── HUDPopupScreen.h │ │ │ ├── HUDRaderMap.h │ │ │ ├── HUDStageTitle.h │ │ │ ├── HintWindowTask.h │ │ │ ├── HudTextParts.h │ │ │ ├── ImageFilter.h │ │ │ ├── MainDisplayTask.h │ │ │ ├── MainMenuExpositionTask.h │ │ │ ├── MainMenuTask.h │ │ │ ├── MainMode.h │ │ │ ├── Message/ │ │ │ │ ├── Camera/ │ │ │ │ │ └── Cameraman/ │ │ │ │ │ └── MsgChangeMode.h │ │ │ │ ├── HUDButtonWindow/ │ │ │ │ │ └── MsgChangeButtons.h │ │ │ │ ├── HUDGoldMedal/ │ │ │ │ │ └── MsgChangeState.h │ │ │ │ ├── HUDMainMenu/ │ │ │ │ │ ├── MsgChangeState.h │ │ │ │ │ ├── MsgSetCursor.h │ │ │ │ │ └── MsgTransition.h │ │ │ │ ├── Mission/ │ │ │ │ │ └── MsgGetGlobalFlag.h │ │ │ │ ├── ObjJump123/ │ │ │ │ │ └── MsgGetNextPoint.h │ │ │ │ ├── PauseAdapter/ │ │ │ │ │ └── MsgGetText.h │ │ │ │ └── Player/ │ │ │ │ └── MsgSuckPlayer.h │ │ │ ├── MessageWindowTask.h │ │ │ ├── Mission/ │ │ │ │ └── Core.h │ │ │ ├── MovieObject.h │ │ │ ├── MovieObjectWmv.h │ │ │ ├── MovieTask.h │ │ │ ├── MyCue.h │ │ │ ├── MyCueAdx.h │ │ │ ├── MyCueAttenuate.h │ │ │ ├── MyGraphicsDevice.h │ │ │ ├── MyPhantom.h │ │ │ ├── MyRenderProcess.h │ │ │ ├── MyTexture.h │ │ │ ├── MyTransforms.h │ │ │ ├── NamedActor.h │ │ │ ├── NamedTask.h │ │ │ ├── NoSyncThread.h │ │ │ ├── ObjectVehicle.h │ │ │ ├── ObjectVehicleBike.h │ │ │ ├── PauseAdapter.h │ │ │ ├── PauseTask.h │ │ │ ├── Player/ │ │ │ │ ├── GroundRayListener.h │ │ │ │ ├── ICollisionListener.h │ │ │ │ ├── ICollisionListenerTemplate.h │ │ │ │ ├── IDynamicLink.h │ │ │ │ ├── IEventerListener.h │ │ │ │ ├── IExportExternalFlag.h │ │ │ │ ├── IExportPostureRequestFlag.h │ │ │ │ ├── IExportWeaponRequestFlag.h │ │ │ │ ├── IFlagCommunicator.h │ │ │ │ ├── IGauge.h │ │ │ │ ├── INotification.h │ │ │ │ ├── IPlugIn.h │ │ │ │ ├── IPostureControl.h │ │ │ │ ├── IPosturePlugIn.h │ │ │ │ ├── IPostureSupportEdge.h │ │ │ │ ├── IPostureSupportInput.h │ │ │ │ ├── IPostureSupportOttoto.h │ │ │ │ ├── IPostureSupportRayTemplate.h │ │ │ │ ├── IPostureSupportSphere.h │ │ │ │ ├── IScore.h │ │ │ │ ├── IStepable.h │ │ │ │ ├── IVariable.h │ │ │ │ ├── IZock.h │ │ │ │ ├── Input/ │ │ │ │ │ ├── IListener.h │ │ │ │ │ ├── ListenerNormal.h │ │ │ │ │ └── TimedAction.h │ │ │ │ ├── Object.h │ │ │ │ ├── PostureControl.h │ │ │ │ ├── RootFrame.h │ │ │ │ ├── Score.h │ │ │ │ ├── SonicGauge.h │ │ │ │ ├── State/ │ │ │ │ │ ├── CommonContext.h │ │ │ │ │ ├── CommonFall.h │ │ │ │ │ ├── CommonObject.h │ │ │ │ │ ├── ContextSpeedAndJump.h │ │ │ │ │ ├── ICommonContext.h │ │ │ │ │ ├── ICommonContextIF.h │ │ │ │ │ ├── IContext.h │ │ │ │ │ ├── IMachine.h │ │ │ │ │ ├── Machine2.h │ │ │ │ │ ├── Object2.h │ │ │ │ │ ├── SonicContext.h │ │ │ │ │ ├── SonicObject.h │ │ │ │ │ ├── TailsContext.h │ │ │ │ │ └── TailsFlight.h │ │ │ │ ├── Unit/ │ │ │ │ │ └── ITestCase.h │ │ │ │ ├── Weapon/ │ │ │ │ │ └── SonicWeapons.h │ │ │ │ └── Zock.h │ │ │ ├── PropFixture.h │ │ │ ├── RaderMapManager.h │ │ │ ├── RenderAction/ │ │ │ │ ├── ApplyBloom.h │ │ │ │ ├── ApplyDevice.h │ │ │ │ ├── ApplySceneParams.h │ │ │ │ ├── AutoSetAspect.h │ │ │ │ ├── Capture.h │ │ │ │ ├── ClearRenderTarget.h │ │ │ │ ├── ColorFill.h │ │ │ │ ├── CopyTexture.h │ │ │ │ ├── LockBlendMode.h │ │ │ │ ├── LockCullMode.h │ │ │ │ ├── LockZMode.h │ │ │ │ ├── MakeBloom.h │ │ │ │ ├── Movie.h │ │ │ │ ├── PrepareCalculateCSM.h │ │ │ │ ├── Rasterize.h │ │ │ │ ├── RasterizeBurnoutBlur.h │ │ │ │ ├── ResetRenderStates.h │ │ │ │ ├── ResetScissorRect.h │ │ │ │ ├── ResetViewport.h │ │ │ │ ├── Resolve.h │ │ │ │ ├── RunCommandBuffer.h │ │ │ │ ├── SetAutoZPass.h │ │ │ │ ├── SetBackStencilOp.h │ │ │ │ ├── SetBlendMode.h │ │ │ │ ├── SetCSMTextures.h │ │ │ │ ├── SetClip.h │ │ │ │ ├── SetColorWriteEnable.h │ │ │ │ ├── SetConstantShader.h │ │ │ │ ├── SetCullMode.h │ │ │ │ ├── SetCurrentScreen.h │ │ │ │ ├── SetDepthTextures.h │ │ │ │ ├── SetFovY.h │ │ │ │ ├── SetFrameBufferObject.h │ │ │ │ ├── SetReflectionTextures.h │ │ │ │ ├── SetScissorRect.h │ │ │ │ ├── SetScissorTest.h │ │ │ │ ├── SetScreen.h │ │ │ │ ├── SetShaderGPRAllocation.h │ │ │ │ ├── SetStencilEnable.h │ │ │ │ ├── SetStencilFunc.h │ │ │ │ ├── SetStencilOp.h │ │ │ │ ├── SetStencilWriteMask.h │ │ │ │ ├── SetTexture.h │ │ │ │ ├── SetUserClipPlaneEnable.h │ │ │ │ ├── SetViewport.h │ │ │ │ └── SetZMode.h │ │ │ ├── RenderPostprocess.h │ │ │ ├── SaveDataTask.h │ │ │ ├── SaveDataTaskXENON.h │ │ │ ├── SelectWindowTask.h │ │ │ ├── SoX/ │ │ │ │ ├── AI/ │ │ │ │ │ └── StateMachine.h │ │ │ │ ├── ApplicationXenon.h │ │ │ │ ├── ApplicationXenon.inl │ │ │ │ ├── Audio/ │ │ │ │ │ ├── Cue.h │ │ │ │ │ ├── IAudioEngine.h │ │ │ │ │ ├── Player.h │ │ │ │ │ └── PlayerImpl.h │ │ │ │ ├── Component.h │ │ │ │ ├── Engine/ │ │ │ │ │ ├── Application.h │ │ │ │ │ ├── Doc.h │ │ │ │ │ ├── DocMode.h │ │ │ │ │ ├── RenderProcess.h │ │ │ │ │ └── Task.h │ │ │ │ ├── Graphics/ │ │ │ │ │ ├── Device.h │ │ │ │ │ ├── Frame.h │ │ │ │ │ ├── FrameGP.h │ │ │ │ │ ├── FrameObserver.h │ │ │ │ │ ├── Technique.h │ │ │ │ │ ├── TechniqueFXL.h │ │ │ │ │ ├── Texture.h │ │ │ │ │ ├── Transforms.h │ │ │ │ │ ├── Vertex.h │ │ │ │ │ └── Xenon/ │ │ │ │ │ ├── DeviceXenon.h │ │ │ │ │ └── TextureXenon.h │ │ │ │ ├── IResource.h │ │ │ │ ├── IResource2.h │ │ │ │ ├── IResourceMgr.h │ │ │ │ ├── Input/ │ │ │ │ │ ├── Manager.h │ │ │ │ │ └── Manager.inl │ │ │ │ ├── LinkNode.h │ │ │ │ ├── Math/ │ │ │ │ │ ├── Matrix.h │ │ │ │ │ ├── Quaternion.h │ │ │ │ │ └── Vector.h │ │ │ │ ├── Message.h │ │ │ │ ├── MessageReceiver.h │ │ │ │ ├── Object.h │ │ │ │ ├── Physics/ │ │ │ │ │ ├── Entity.h │ │ │ │ │ ├── Havok/ │ │ │ │ │ │ ├── EntityHavok.h │ │ │ │ │ │ ├── EntityHavokImp.h │ │ │ │ │ │ ├── PhantomHavok.h │ │ │ │ │ │ └── WorldHavok.h │ │ │ │ │ ├── IntersectEvent.h │ │ │ │ │ ├── IntersectListener.h │ │ │ │ │ ├── Phantom.h │ │ │ │ │ ├── PhantomListener.h │ │ │ │ │ ├── Shape.h │ │ │ │ │ ├── ShapeCastEvent.h │ │ │ │ │ ├── ShapeCastListener.h │ │ │ │ │ └── World.h │ │ │ │ ├── RefCountObject.h │ │ │ │ ├── RefSharedPointer.h │ │ │ │ ├── Scenery/ │ │ │ │ │ ├── Camera.h │ │ │ │ │ ├── CameraEventCallback.h │ │ │ │ │ ├── CameraImp.h │ │ │ │ │ └── Drawable.h │ │ │ │ └── Thread.h │ │ │ ├── StdImageFilters/ │ │ │ │ ├── BurnoutBlurFilter.h │ │ │ │ └── SingleTechniqueFilter.h │ │ │ ├── System/ │ │ │ │ ├── CreateStatic.h │ │ │ │ ├── Diagnostics/ │ │ │ │ │ └── Performance.h │ │ │ │ └── Singleton.h │ │ │ ├── TextBook.h │ │ │ ├── TextBookMgr.h │ │ │ ├── TextCard.h │ │ │ ├── TextEntity.h │ │ │ ├── TextFontPicture.h │ │ │ ├── TextFontPictureMgr.h │ │ │ ├── TitleTask.h │ │ │ ├── VehicleMissileCtrl.h │ │ │ ├── VehicleMissileCtrlAutomatic.h │ │ │ ├── VehicleMissileCtrlSingle.h │ │ │ ├── WorldRenderProcess.h │ │ │ └── sonicXmaPlayer.h │ │ ├── boost/ │ │ │ └── smart_ptr/ │ │ │ ├── make_shared_object.h │ │ │ └── shared_ptr.h │ │ ├── d3dxb.h │ │ ├── hk330/ │ │ │ ├── hkArray.h │ │ │ ├── hkReferencedObject.h │ │ │ ├── hkpBroadPhaseHandle.h │ │ │ ├── hkpCdBody.h │ │ │ ├── hkpCollidable.h │ │ │ ├── hkpCollidableCollidableFilter.h │ │ │ ├── hkpCollisionFilter.h │ │ │ ├── hkpEntity.h │ │ │ ├── hkpLinkedCollidable.h │ │ │ ├── hkpPhantom.h │ │ │ ├── hkpProperty.h │ │ │ ├── hkpRayCollidableFilter.h │ │ │ ├── hkpRayShapeCollectionFilter.h │ │ │ ├── hkpRigidBody.h │ │ │ ├── hkpShape.h │ │ │ ├── hkpShapeCollectionFilter.h │ │ │ ├── hkpTypedBroadPhaseHandle.h │ │ │ ├── hkpWorld.h │ │ │ └── hkpWorldObject.h │ │ └── stdx/ │ │ ├── string.h │ │ ├── vector.h │ │ └── wstring.h │ ├── app.cpp │ ├── app.h │ ├── apu/ │ │ ├── audio.cpp │ │ ├── audio.h │ │ ├── driver/ │ │ │ └── sdl2_driver.cpp │ │ ├── embedded_player.cpp │ │ ├── embedded_player.h │ │ ├── xma_decoder.cpp │ │ └── xma_decoder.h │ ├── cpu/ │ │ ├── guest_stack_var.h │ │ ├── guest_thread.cpp │ │ ├── guest_thread.h │ │ └── ppc_context.h │ ├── decompressor.h │ ├── exports.cpp │ ├── exports.h │ ├── framework.h │ ├── gpu/ │ │ ├── cache/ │ │ │ ├── pipeline_state_cache.h │ │ │ ├── vertex_declaration_cache.h │ │ │ └── vertex_element_cache.h │ │ ├── imgui/ │ │ │ ├── imgui_common.cpp │ │ │ ├── imgui_common.h │ │ │ ├── imgui_font_builder.cpp │ │ │ ├── imgui_font_builder.h │ │ │ ├── imgui_snapshot.cpp │ │ │ └── imgui_snapshot.h │ │ ├── shader/ │ │ │ ├── hlsl/ │ │ │ │ ├── .gitignore │ │ │ │ ├── blend_color_alpha_ps.hlsl │ │ │ │ ├── conditional_survey_ps.hlsl │ │ │ │ ├── copy_color_ps.hlsl │ │ │ │ ├── copy_common.hlsli │ │ │ │ ├── copy_depth_ps.hlsl │ │ │ │ ├── copy_vs.hlsl │ │ │ │ ├── csd_filter_ps.hlsl │ │ │ │ ├── csd_no_tex_vs.hlsl │ │ │ │ ├── csd_vs.hlsl │ │ │ │ ├── enhanced_burnout_blur_ps.hlsl │ │ │ │ ├── enhanced_burnout_blur_vs.hlsl │ │ │ │ ├── gamma_correction_ps.hlsl │ │ │ │ ├── gaussian_blur.hlsli │ │ │ │ ├── gaussian_blur_3x3.hlsl │ │ │ │ ├── gaussian_blur_5x5.hlsl │ │ │ │ ├── gaussian_blur_7x7.hlsl │ │ │ │ ├── gaussian_blur_9x9.hlsl │ │ │ │ ├── imgui_common.hlsli │ │ │ │ ├── imgui_ps.hlsl │ │ │ │ ├── imgui_vs.hlsl │ │ │ │ ├── resolve_msaa_color.hlsli │ │ │ │ ├── resolve_msaa_color_2x.hlsl │ │ │ │ ├── resolve_msaa_color_4x.hlsl │ │ │ │ ├── resolve_msaa_color_8x.hlsl │ │ │ │ ├── resolve_msaa_depth.hlsli │ │ │ │ ├── resolve_msaa_depth_2x.hlsl │ │ │ │ ├── resolve_msaa_depth_4x.hlsl │ │ │ │ └── resolve_msaa_depth_8x.hlsl │ │ │ └── msl/ │ │ │ ├── .gitignore │ │ │ ├── blend_color_alpha_ps.metal │ │ │ ├── conditional_survey_ps.metal │ │ │ ├── copy_color_ps.metal │ │ │ ├── copy_common.metali │ │ │ ├── copy_depth_ps.metal │ │ │ ├── copy_vs.metal │ │ │ ├── csd_filter_ps.metal │ │ │ ├── csd_no_tex_vs.metal │ │ │ ├── csd_vs.metal │ │ │ ├── enhanced_burnout_blur_ps.metal │ │ │ ├── enhanced_burnout_blur_vs.metal │ │ │ ├── gamma_correction_ps.metal │ │ │ ├── gaussian_blur.metali │ │ │ ├── gaussian_blur_3x3.metal │ │ │ ├── gaussian_blur_5x5.metal │ │ │ ├── gaussian_blur_7x7.metal │ │ │ ├── gaussian_blur_9x9.metal │ │ │ ├── imgui_common.metali │ │ │ ├── imgui_ps.metal │ │ │ ├── imgui_vs.metal │ │ │ ├── resolve_msaa_color.metali │ │ │ ├── resolve_msaa_color_2x.metal │ │ │ ├── resolve_msaa_color_4x.metal │ │ │ ├── resolve_msaa_color_8x.metal │ │ │ ├── resolve_msaa_depth.metali │ │ │ ├── resolve_msaa_depth_2x.metal │ │ │ ├── resolve_msaa_depth_4x.metal │ │ │ └── resolve_msaa_depth_8x.metal │ │ ├── video.cpp │ │ └── video.h │ ├── hid/ │ │ ├── driver/ │ │ │ └── sdl_hid.cpp │ │ ├── hid.cpp │ │ └── hid.h │ ├── install/ │ │ ├── directory_file_system.h │ │ ├── hashes/ │ │ │ ├── episode_amigo.cpp │ │ │ ├── episode_amigo.h │ │ │ ├── episode_shadow.cpp │ │ │ ├── episode_shadow.h │ │ │ ├── episode_silver.cpp │ │ │ ├── episode_silver.h │ │ │ ├── episode_sonic.cpp │ │ │ ├── episode_sonic.h │ │ │ ├── game.cpp │ │ │ ├── game.h │ │ │ ├── mission_shadow.cpp │ │ │ ├── mission_shadow.h │ │ │ ├── mission_silver.cpp │ │ │ ├── mission_silver.h │ │ │ ├── mission_sonic.cpp │ │ │ └── mission_sonic.h │ │ ├── installer.cpp │ │ ├── installer.h │ │ ├── iso_file_system.cpp │ │ ├── iso_file_system.h │ │ ├── update_checker.cpp │ │ ├── update_checker.h │ │ ├── virtual_file_system.h │ │ ├── xcontent_file_system.cpp │ │ └── xcontent_file_system.h │ ├── kernel/ │ │ ├── freelist.h │ │ ├── function.h │ │ ├── heap.cpp │ │ ├── heap.h │ │ ├── imports.cpp │ │ ├── io/ │ │ │ ├── file_system.cpp │ │ │ └── file_system.h │ │ ├── memory.cpp │ │ ├── memory.h │ │ ├── xam.cpp │ │ ├── xam.h │ │ ├── xdbf.h │ │ ├── xdm.cpp │ │ └── xdm.h │ ├── locale/ │ │ ├── achievement_locale.cpp │ │ ├── achievement_locale.h │ │ ├── config_locale.cpp │ │ ├── locale.cpp │ │ └── locale.h │ ├── main.cpp │ ├── misc_impl.cpp │ ├── mod/ │ │ ├── ini_file.h │ │ ├── ini_file.inl │ │ ├── mod_loader.cpp │ │ └── mod_loader.h │ ├── mutex.h │ ├── natvis.natvis │ ├── os/ │ │ ├── .gitignore │ │ ├── linux/ │ │ │ ├── logger_linux.cpp │ │ │ ├── media_linux.cpp │ │ │ ├── process_linux.cpp │ │ │ ├── registry_linux.inl │ │ │ ├── user_linux.cpp │ │ │ └── version_linux.cpp │ │ ├── logger.h │ │ ├── macos/ │ │ │ ├── logger_macos.cpp │ │ │ ├── media_macos.cpp │ │ │ ├── process_macos.cpp │ │ │ ├── registry_macos.inl │ │ │ ├── user_macos.cpp │ │ │ └── version_macos.cpp │ │ ├── media.h │ │ ├── process.h │ │ ├── registry.h │ │ ├── user.h │ │ ├── version.h │ │ └── win32/ │ │ ├── logger_win32.cpp │ │ ├── media_win32.cpp │ │ ├── process_win32.cpp │ │ ├── registry_win32.inl │ │ ├── user_win32.cpp │ │ └── version_win32.cpp │ ├── patches/ │ │ ├── MainMenuTask_patches.cpp │ │ ├── MainMenuTask_patches.h │ │ ├── SaveDataTask_patches.cpp │ │ ├── TitleTask_patches.cpp │ │ ├── aspect_ratio_patches.cpp │ │ ├── aspect_ratio_patches.h │ │ ├── audio_patches.cpp │ │ ├── audio_patches.h │ │ ├── camera_patches.cpp │ │ ├── camera_patches.h │ │ ├── fps_patches.cpp │ │ ├── frontend_listener.cpp │ │ ├── hook_event.h │ │ ├── input_patches.cpp │ │ ├── loading_patches.cpp │ │ ├── loading_patches.h │ │ ├── misc_patches.cpp │ │ ├── patches.h │ │ ├── pause_patches.cpp │ │ ├── player_patches.cpp │ │ ├── text_patches.cpp │ │ ├── text_patches.h │ │ └── video_patches.cpp │ ├── preload_executable.cpp │ ├── preload_executable.h │ ├── res/ │ │ ├── .gitignore │ │ ├── credits.h │ │ ├── macos/ │ │ │ ├── MacOSXBundleInfo.plist.in │ │ │ └── game_icon.icns │ │ ├── version.cpp.template │ │ ├── version.h.template │ │ ├── version.txt │ │ └── win32/ │ │ └── res.rc.template │ ├── sdl_events.h │ ├── sdl_listener.cpp │ ├── sdl_listener.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── ui/ │ │ ├── achievement_menu.cpp │ │ ├── achievement_menu.h │ │ ├── achievement_overlay.cpp │ │ ├── achievement_overlay.h │ │ ├── black_bar.cpp │ │ ├── black_bar.h │ │ ├── button_window.cpp │ │ ├── button_window.h │ │ ├── common_menu.cpp │ │ ├── common_menu.h │ │ ├── fader.cpp │ │ ├── fader.h │ │ ├── game_window.cpp │ │ ├── game_window.h │ │ ├── imgui_utils.cpp │ │ ├── imgui_utils.h │ │ ├── installer_wizard.cpp │ │ ├── installer_wizard.h │ │ ├── message_window.cpp │ │ ├── message_window.h │ │ ├── options_menu.cpp │ │ └── options_menu.h │ ├── user/ │ │ ├── achievement_data.cpp │ │ ├── achievement_data.h │ │ ├── achievement_manager.cpp │ │ ├── achievement_manager.h │ │ ├── config.cpp │ │ ├── config.h │ │ ├── config_def.h │ │ ├── paths.cpp │ │ ├── paths.h │ │ ├── registry.cpp │ │ └── registry.h │ ├── utils/ │ │ ├── bit_stream.cpp │ │ ├── bit_stream.h │ │ ├── ring_buffer.cpp │ │ └── ring_buffer.h │ ├── version.cmake │ └── xxHashMap.h ├── MarathonRecompLib/ │ ├── CMakeLists.txt │ ├── config/ │ │ ├── Marathon.toml │ │ └── switch_table.toml │ ├── ppc/ │ │ └── .gitignore │ ├── private/ │ │ └── .gitignore │ └── shader/ │ ├── .gitignore │ └── shader_cache.h ├── README.md ├── docs/ │ ├── BUILDING.md │ └── DUMPING-en.md ├── flatpak/ │ ├── README.md │ ├── io.github.sonicnext_dev.marathonrecomp.desktop │ ├── io.github.sonicnext_dev.marathonrecomp.json │ └── io.github.sonicnext_dev.marathonrecomp.metainfo.xml ├── thirdparty/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── MoltenVK/ │ │ └── CMakeLists.txt │ └── o1heap/ │ ├── CMakeLists.txt │ ├── o1heap.c │ └── o1heap.h ├── toolchains/ │ └── linux-clang.cmake ├── tools/ │ ├── CMakeLists.txt │ ├── bc_diff/ │ │ ├── CMakeLists.txt │ │ ├── bc_diff.cpp │ │ └── bc_diff.h │ ├── file_to_c/ │ │ ├── CMakeLists.txt │ │ └── file_to_c.cpp │ ├── fshasher/ │ │ ├── CMakeLists.txt │ │ ├── fshasher.cpp │ │ └── plainargs.h │ ├── u8extract/ │ │ ├── CMakeLists.txt │ │ └── u8extract.cpp │ └── x_decompress/ │ ├── CMakeLists.txt │ └── x_decompress.cpp ├── update_submodules.bat └── vcpkg.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 4 insert_final_newline = true end_of_line = lf ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug Report description: Report a bug in Marathon Recompiled type: "Bug" body: - type: checkboxes id: validation attributes: label: Validation options: - label: I have checked the [Issues](https://github.com/sonicnext-dev/MarathonRecomp/issues) page to see if my problem has already been reported required: true - label: I have confirmed that this bug does not occur in the original game running on original Xbox 360 hardware required: true - type: checkboxes id: dlc attributes: label: DLC description: If you have DLC installed, please specify which ones you have. options: - label: Additional Episode "Sonic Boss Attack" - label: Additional Episode "Shadow Boss Attack" - label: Additional Episode "Silver Boss Attack" - label: Additional Episode "Team Attack Amigo" - label: Additional Mission Pack "Sonic/Very Hard" - label: Additional Mission Pack "Shadow/Very Hard" - label: Additional Mission Pack "Silver/Very Hard" - type: textarea id: mods attributes: label: Mods description: Provide a list of your enabled mods in Mod Manager here. You will not receive support for issues *caused* by mods. - type: textarea id: codes attributes: label: Codes description: Provide a list of your enabled codes in Mod Manager here. - type: textarea id: description attributes: label: Describe the Bug description: A clear and concise description of what the bug is. validations: required: true - type: textarea id: reproduction attributes: label: Steps to Reproduce description: Provide steps to reproduce the bug placeholder: | 1. Go to '...' 2. etc. validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: A clear and concise description of what you expected to happen. validations: required: true - type: textarea id: footage attributes: label: Footage description: Attach a screenshot or video of the bug. If possible, please also provide footage of the expected behaviour on original Xbox 360 hardware. - type: textarea id: specifications attributes: label: Specifications description: Fill out the following details value: | - CPU: (e.g. Intel Core [...], AMD Ryzen [...], etc.) - GPU: (e.g. NVIDIA GeForce [...], Radeon HD [...], Intel HD [...], etc.) - GPU Driver: (e.g NVIDIA driver 545.XX, AMD driver 24.X.X, etc.) - OS: (e.g. Windows 10, Windows 11, Linux distro) - Version: (e.g. 1.0.0) validations: required: true - type: textarea id: context attributes: label: Additional Context description: Provide any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false ================================================ FILE: .github/ISSUE_TEMPLATE/dev_report.yml ================================================ name: Dev Report description: For developer use only! body: - type: textarea id: description attributes: label: Description validations: required: true ================================================ FILE: .github/workflows/validate-external.yml ================================================ name: validate-external on: pull_request_target: types: [opened, synchronize] jobs: authorize: if: github.repository != github.event.pull_request.head.repo.full_name environment: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && 'external' || 'internal' }} runs-on: ubuntu-24.04 steps: - run: echo ✓ build: needs: authorize uses: ./.github/workflows/validate.yml secrets: ASSET_REPO: ${{ secrets.ASSET_REPO }} ASSET_REPO_TOKEN: ${{ secrets.ASSET_REPO_TOKEN }} ================================================ FILE: .github/workflows/validate-internal.yml ================================================ name: validate-internal on: push: branches: - main pull_request: types: [opened, synchronize] jobs: build: if: github.event_name == 'push' || github.repository == github.event.pull_request.head.repo.full_name uses: ./.github/workflows/validate.yml secrets: inherit ================================================ FILE: .github/workflows/validate.yml ================================================ name: validate on: workflow_call: secrets: ASSET_REPO: required: true ASSET_REPO_TOKEN: required: true concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-linux: name: Build Linux runs-on: ubuntu-24.04 strategy: matrix: preset: ["linux-debug", "linux-release", "linux-relwithdebinfo"] env: LLVM_VERSION: 18 CMAKE_PRESET: ${{ matrix.preset }} steps: - name: Checkout Repository uses: actions/checkout@v4 with: submodules: recursive - name: Checkout Private Repository uses: actions/checkout@v4 with: repository: ${{ secrets.ASSET_REPO }} token: ${{ secrets.ASSET_REPO_TOKEN }} path: ./private - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: key: ccache-${{ runner.os }}-${{ matrix.preset }} - name: Cache vcpkg uses: actions/cache@v4 with: path: | ./thirdparty/vcpkg/downloads ./thirdparty/vcpkg/packages key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} restore-keys: | vcpkg-${{ runner.os }}- - name: Install Dependencies (Linux) run: |- sudo apt update sudo apt install -y ninja-build llvm-${{ env.LLVM_VERSION }}-dev libgtk-3-dev libasound2-dev libpulse-dev libpipewire-0.3-dev - name: Cache ccache Directory uses: actions/cache@v4 with: path: /tmp/ccache key: ccache-${{ runner.os }}-${{ matrix.preset }} - name: Prepare Project run: cp ./private/* ./MarathonRecompLib/private - name: Configure Project env: CCACHE_DIR: /tmp/ccache run: cmake . --preset ${{ env.CMAKE_PRESET }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache - name: Build Project env: CCACHE_DIR: /tmp/ccache run: cmake --build ./out/build/${{ env.CMAKE_PRESET }} --target MarathonRecomp - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: MarathonRecomp-Linux-${{ env.CMAKE_PRESET }} path: ./out/build/${{ env.CMAKE_PRESET }}/MarathonRecomp/MarathonRecomp build-windows: name: Build Windows runs-on: windows-latest strategy: matrix: preset: ["x64-Clang-Debug", "x64-Clang-Release", "x64-Clang-RelWithDebInfo"] env: CMAKE_PRESET: ${{ matrix.preset }} steps: - name: Checkout repository uses: actions/checkout@v4 with: submodules: recursive - name: Checkout private repository uses: actions/checkout@v4 with: repository: ${{ secrets.ASSET_REPO }} token: ${{ secrets.ASSET_REPO_TOKEN }} path: .\private - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: key: ccache-${{ runner.os }}-${{ matrix.preset }} - name: Cache vcpkg uses: actions/cache@v4 with: path: | ./thirdparty/vcpkg/downloads ./thirdparty/vcpkg/packages key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} restore-keys: | vcpkg-${{ runner.os }}- - name: Install dependencies run: | choco install ninja Remove-Item -Path "C:\ProgramData\Chocolatey\bin\ccache.exe" -Force -ErrorAction SilentlyContinue - name: Configure Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1 - name: Prepare Project run: | $commitMessage = git log -1 --pretty=%s Add-Content -Path $env:GITHUB_ENV -Value "commit_message=$commitMessage" copy .\private\* .\MarathonRecompLib\private - name: Configure Project run: cmake . --preset ${{ env.CMAKE_PRESET }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache - name: Build Project run: cmake --build .\out\build\${{ env.CMAKE_PRESET }} --target MarathonRecomp - name: Pack Release run: | New-Item -ItemType Directory -Path .\release New-Item -ItemType Directory -Path .\release\D3D12 Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\MarathonRecomp\D3D12\D3D12Core.dll" -Destination ".\release\D3D12\D3D12Core.dll" Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\MarathonRecomp\dxcompiler.dll" -Destination ".\release\dxcompiler.dll" Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\MarathonRecomp\dxil.dll" -Destination ".\release\dxil.dll" Move-Item -Path ".\out\build\${{ env.CMAKE_PRESET }}\MarathonRecomp\MarathonRecomp.exe" -Destination ".\release\MarathonRecomp.exe" - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: MarathonRecomp-Windows-${{ env.CMAKE_PRESET }} path: .\release - name: Upload PDB uses: actions/upload-artifact@v4 if: ${{ matrix.preset != 'x64-Clang-Release' }} with: name: MarathonRecomp-Windows-${{ env.CMAKE_PRESET }}-PDB path: .\out\build\${{ env.CMAKE_PRESET }}\MarathonRecomp\MarathonRecomp.pdb build-flatpak: name: Build Flatpak runs-on: ubuntu-24.04 env: FLATPAK_ID: io.github.sonicnext_dev.marathonrecomp FREEDESKTOP_VERSION: 23.08 LLVM_VERSION: 18 steps: - name: Checkout Repository uses: actions/checkout@v4 with: submodules: recursive - name: Checkout Private Repository uses: actions/checkout@v4 with: repository: ${{ secrets.ASSET_REPO }} token: ${{ secrets.ASSET_REPO_TOKEN }} path: ./private - name: Install Dependencies run: |- sudo apt update sudo apt install -y flatpak-builder ccache - name: Setup ccache uses: actions/cache@v4 with: path: /tmp/ccache key: ccache-${{ runner.os }} - name: Prepare Project run: cp ./private/* ./MarathonRecompLib/private - name: Prepare Flatpak run: | flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo flatpak --user install -y flathub org.freedesktop.Sdk//${{ env.FREEDESKTOP_VERSION }} flatpak --user install -y flathub org.freedesktop.Sdk.Extension.llvm${{ env.LLVM_VERSION }}//${{ env.FREEDESKTOP_VERSION }} - name: Build Flatpak run: | echo "commit_message=$(git log -1 --pretty=%s)" >> $GITHUB_ENV export CCACHE_DIR=/tmp/ccache flatpak-builder --user --force-clean --install-deps-from=flathub --repo=repo --ccache builddir ./flatpak/${{ env.FLATPAK_ID }}.json flatpak build-bundle repo ./${{ env.FLATPAK_ID }}.flatpak ${{ env.FLATPAK_ID }} --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: MarathonRecomp-Flatpak path: ./${{ env.FLATPAK_ID }}.flatpak build-macos: name: Build macOS runs-on: macos-15 strategy: matrix: arch: [ "arm64" ] preset: ["macos-debug", "macos-release", "macos-relwithdebinfo"] env: CMAKE_PRESET: ${{ matrix.preset }} steps: - name: Checkout Repository uses: actions/checkout@v4 with: submodules: recursive - name: Checkout Private Repository uses: actions/checkout@v4 with: repository: ${{ secrets.ASSET_REPO }} token: ${{ secrets.ASSET_REPO_TOKEN }} path: ./private - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: key: ccache-${{ runner.os }}-${{ matrix.arch }}-${{ matrix.preset }} - name: Cache vcpkg uses: actions/cache@v4 with: path: | ./thirdparty/vcpkg/downloads ./thirdparty/vcpkg/packages key: vcpkg-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('vcpkg.json') }} restore-keys: | vcpkg-${{ runner.os }}-${{ matrix.arch }}- - name: Install Dependencies (macOS) run: | brew install ninja - name: Cache ccache Directory uses: actions/cache@v4 with: path: /tmp/ccache key: ccache-${{ runner.os }}-${{ matrix.arch }}-${{ matrix.preset }} - name: Prepare Project run: | cp ./private/* ./MarathonRecompLib/private - name: Configure Project env: CCACHE_DIR: /tmp/ccache run: cmake . --preset ${{ env.CMAKE_PRESET }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache - name: Build Project env: CCACHE_DIR: /tmp/ccache run: cmake --build ./out/build/${{ env.CMAKE_PRESET }} --target MarathonRecomp - name: Pack Release run: | codesign --deep -fs - "./out/build/${{ env.CMAKE_PRESET }}/MarathonRecomp/Marathon Recompiled.app" tar -czf MarathonRecomp-macOS-${{ matrix.arch }}-${{ env.CMAKE_PRESET }}.tar.gz -C ./out/build/${{ env.CMAKE_PRESET }}/MarathonRecomp "Marathon Recompiled.app" - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: MarathonRecomp-macOS-${{ matrix.arch }}-${{ env.CMAKE_PRESET }} path: MarathonRecomp-macOS-${{ matrix.arch }}-${{ env.CMAKE_PRESET }}.tar.gz ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ [Oo]ut/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.tlog *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio 6 auto-generated project file (contains which files were open etc.) *.vbp # Visual Studio 6 workspace and project file (working project files containing files to include in project) *.dsw *.dsp # Visual Studio 6 technical files *.ncb *.aps # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # Visual Studio History (VSHistory) files .vshistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd # VS Code files for those working on multiple tools .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.code-workspace # Local History for Visual Studio Code .history/ # Windows Installer files from build outputs *.cab *.msi *.msix *.msm *.msp # JetBrains Rider *.sln.iml .idea # macOS metadata files .DS_Store UnleashedRecompLib/build UnleashedRecompLib/ppc* ================================================ FILE: .gitmodules ================================================ [submodule "tools/XenonRecomp"] path = tools/XenonRecomp url = https://github.com/sonicnext-dev/XenonRecomp.git [submodule "thirdparty/ddspp"] path = thirdparty/ddspp url = https://github.com/redorav/ddspp.git [submodule "tools/XenosRecomp"] path = tools/XenosRecomp url = https://github.com/sonicnext-dev/XenosRecomp.git [submodule "thirdparty/msdf-atlas-gen"] path = thirdparty/msdf-atlas-gen url = https://github.com/Chlumsky/msdf-atlas-gen.git [submodule "thirdparty/vcpkg"] path = thirdparty/vcpkg url = https://github.com/microsoft/vcpkg [submodule "thirdparty/SDL"] path = thirdparty/SDL url = https://github.com/libsdl-org/SDL.git [submodule "thirdparty/stb"] path = thirdparty/stb url = https://github.com/nothings/stb.git [submodule "thirdparty/concurrentqueue"] path = thirdparty/concurrentqueue url = https://github.com/cameron314/concurrentqueue.git [submodule "thirdparty/magic_enum"] path = thirdparty/magic_enum url = https://github.com/Neargye/magic_enum.git [submodule "thirdparty/nativefiledialog-extended"] path = thirdparty/nativefiledialog-extended url = https://github.com/btzy/nativefiledialog-extended.git [submodule "thirdparty/imgui"] path = thirdparty/imgui url = https://github.com/ocornut/imgui.git [submodule "thirdparty/unordered_dense"] path = thirdparty/unordered_dense url = https://github.com/martinus/unordered_dense.git [submodule "thirdparty/SDL_mixer"] path = thirdparty/SDL_mixer url = https://github.com/libsdl-org/SDL_mixer [submodule "thirdparty/implot"] path = thirdparty/implot url = https://github.com/epezent/implot.git [submodule "thirdparty/json"] path = thirdparty/json url = https://github.com/nlohmann/json [submodule "MarathonRecompResources"] path = MarathonRecompResources url = https://github.com/sonicnext-dev/MarathonRecompResources [submodule "thirdparty/MoltenVK/MoltenVK"] path = thirdparty/MoltenVK/MoltenVK url = https://github.com/KhronosGroup/MoltenVK.git [submodule "thirdparty/MoltenVK/SPIRV-Cross"] path = thirdparty/MoltenVK/SPIRV-Cross url = https://github.com/KhronosGroup/SPIRV-Cross.git [submodule "thirdparty/plume"] path = thirdparty/plume url = https://github.com/renderbag/plume [submodule "thirdparty/ffmpeg-core"] path = thirdparty/ffmpeg-core url = https://github.com/sonicnext-dev/ffmpeg-core ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required (VERSION 3.20) if(NOT DEFINED ENV{VCPKG_ROOT}) message(FATAL_ERROR "VCPKG_ROOT is not defined!") endif() set(MARATHON_RECOMP_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty) set(MARATHON_RECOMP_TOOLS_ROOT ${CMAKE_SOURCE_DIR}/tools) set(CMAKE_CXX_STANDARD 20) set(BUILD_SHARED_LIBS OFF) # Enable Hot Reload for MSVC compilers if supported. if (POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") endif() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") project("MarathonRecomp-ALL") if (APPLE) enable_language(OBJC OBJCXX) endif() if (CMAKE_OSX_ARCHITECTURES) set(MARATHON_RECOMP_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES}) elseif(CMAKE_SYSTEM_PROCESSOR) set(MARATHON_RECOMP_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) else() set(MARATHON_RECOMP_ARCHITECTURE ${CMAKE_HOST_SYSTEM_PROCESSOR}) endif() string(TOLOWER "${MARATHON_RECOMP_ARCHITECTURE}" MARATHON_RECOMP_ARCHITECTURE) message(STATUS "Detected architecture: ${MARATHON_RECOMP_ARCHITECTURE}") if (MARATHON_RECOMP_ARCHITECTURE STREQUAL "x86_64" OR MARATHON_RECOMP_ARCHITECTURE STREQUAL "amd64") # Target Sandy Bridge for all projects add_compile_options( -march=sandybridge ) endif() if (CMAKE_BUILD_TYPE STREQUAL "Debug") # Normally only defined by Visual Studio, added for consistency add_compile_definitions(_DEBUG) endif() add_subdirectory(${MARATHON_RECOMP_THIRDPARTY_ROOT}) add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}) # Include sub-projects. add_subdirectory("MarathonRecompLib") add_subdirectory("MarathonRecomp") ================================================ FILE: CMakePresets.json ================================================ { "version": 8, "configurePresets": [ { "name": "windows-base", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/out/build/${presetName}", "installDir": "${sourceDir}/out/install/${presetName}", "cacheVariables": { "CMAKE_C_COMPILER": "clang-cl.exe", "CMAKE_CXX_COMPILER": "clang-cl.exe", "CMAKE_LINKER": "lld-link.exe", "CMAKE_TOOLCHAIN_FILE": { "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "type": "FILEPATH" } }, "environment": { "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" }, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" } }, { "name": "x64-Clang-Debug", "displayName": "Debug", "inherits": "windows-base", "architecture": { "value": "x64", "strategy": "external" }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "VCPKG_TARGET_TRIPLET": { "value": "x64-windows-static", "type": "STRING" } } }, { "name": "x64-Clang-RelWithDebInfo", "displayName": "RelWithDebInfo", "inherits": "x64-Clang-Debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, { "name": "x64-Clang-Release", "displayName": "Release", "inherits": "x64-Clang-Debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true } }, { "name": "linux-base", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/out/build/${presetName}", "installDir": "${sourceDir}/out/install/${presetName}", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": { "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "type": "FILEPATH" }, "VCPKG_TARGET_TRIPLET": { "value": "x64-linux", "type": "STRING" }, "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/toolchains/linux-clang.cmake" }, "environment": { "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" }, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" }, "vendor": { "microsoft.com/VisualStudioRemoteSettings/CMake/2.0": { "remoteSourceRootDir": "$env{HOME}/.vs/$ms{projectDirName}" } } }, { "name": "linux-debug", "displayName": "Linux-Debug", "inherits": "linux-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "linux-relwithdebinfo", "displayName": "Linux-RelWithDebInfo", "inherits": "linux-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, { "name": "linux-release", "displayName": "Linux-Release", "inherits": "linux-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true } }, { "name": "macos-base", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/out/build/${presetName}", "installDir": "${sourceDir}/out/install/${presetName}", "cacheVariables": { "CMAKE_TOOLCHAIN_FILE": { "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "type": "FILEPATH" }, "CMAKE_OSX_DEPLOYMENT_TARGET": "13.0" }, "environment": { "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" }, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Darwin" }, "vendor": { "microsoft.com/VisualStudioRemoteSettings/CMake/2.0": { "remoteSourceRootDir": "$env{HOME}/.vs/$ms{projectDirName}" } } }, { "name": "macos-debug", "displayName": "macOS-Debug", "inherits": "macos-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "macos-relwithdebinfo", "displayName": "macOS-RelWithDebInfo", "inherits": "macos-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" } }, { "name": "macos-release", "displayName": "macOS-Release", "inherits": "macos-base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true } } ] } ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: MarathonRecomp/.gitignore ================================================ /version.h /version.cpp ================================================ FILE: MarathonRecomp/CMakeLists.txt ================================================ project("MarathonRecomp") if (WIN32) option(MARATHON_RECOMP_D3D12 "Add D3D12 support for rendering" ON) endif() if (APPLE) option(MARATHON_RECOMP_METAL "Add Metal support for rendering" ON) endif() if (CMAKE_SYSTEM_NAME MATCHES "Linux") option(MARATHON_RECOMP_FLATPAK "Configure the build for Flatpak compatibility." OFF) endif() function(BIN2C) cmake_parse_arguments(BIN2C_ARGS "" "TARGET_OBJ;SOURCE_FILE;DEST_FILE;ARRAY_NAME;COMPRESSION_TYPE" "" ${ARGN}) if(NOT BIN2C_ARGS_TARGET_OBJ) message(FATAL_ERROR "TARGET_OBJ not specified.") endif() if(NOT BIN2C_ARGS_SOURCE_FILE) message(FATAL_ERROR "SOURCE_FILE not specified.") endif() if(NOT BIN2C_ARGS_DEST_FILE) set(BIN2C_ARGS_DEST_FILE "${BIN2C_ARGS_SOURCE_FILE}") endif() if(NOT BIN2C_ARGS_COMPRESSION_TYPE) set(BIN2C_ARGS_COMPRESSION_TYPE "none") endif() add_custom_command(OUTPUT "${BIN2C_ARGS_DEST_FILE}.c" COMMAND $ "${BIN2C_ARGS_SOURCE_FILE}" "${BIN2C_ARGS_ARRAY_NAME}" "${BIN2C_ARGS_COMPRESSION_TYPE}" "${BIN2C_ARGS_DEST_FILE}.c" "${BIN2C_ARGS_DEST_FILE}.h" DEPENDS "${BIN2C_ARGS_SOURCE_FILE}" BYPRODUCTS "${BIN2C_ARGS_DEST_FILE}.h" COMMENT "Generating binary header for ${BIN2C_ARGS_SOURCE_FILE}..." ) set_source_files_properties(${BIN2C_ARGS_DEST_FILE}.c PROPERTIES SKIP_PRECOMPILE_HEADERS ON) target_sources(${BIN2C_ARGS_TARGET_OBJ} PRIVATE ${BIN2C_ARGS_DEST_FILE}.c) endfunction() add_compile_options( -fno-strict-aliasing -Wno-switch -Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable -Wno-void-pointer-to-int-cast -Wno-int-to-void-pointer-cast -Wno-invalid-offsetof -Wno-null-arithmetic -Wno-null-conversion -Wno-tautological-undefined-compare ) if (WIN32) add_compile_options(/fp:strict) else() add_compile_options(-ffp-model=strict) endif() add_compile_definitions( SDL_MAIN_HANDLED _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR # Microsoft wtf? _CRT_SECURE_NO_WARNINGS) set(MARATHON_RECOMP_PRECOMPILED_HEADERS "stdafx.h" ) set(MARATHON_RECOMP_KERNEL_CXX_SOURCES "kernel/imports.cpp" "kernel/xdm.cpp" "kernel/heap.cpp" "kernel/memory.cpp" "kernel/xam.cpp" "kernel/io/file_system.cpp" ) set(MARATHON_RECOMP_LOCALE_CXX_SOURCES "locale/achievement_locale.cpp" "locale/config_locale.cpp" "locale/locale.cpp" ) if (WIN32) set(MARATHON_RECOMP_OS_CXX_SOURCES "os/win32/logger_win32.cpp" "os/win32/media_win32.cpp" "os/win32/process_win32.cpp" "os/win32/user_win32.cpp" "os/win32/version_win32.cpp" ) elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") set(MARATHON_RECOMP_OS_CXX_SOURCES "os/linux/logger_linux.cpp" "os/linux/media_linux.cpp" "os/linux/process_linux.cpp" "os/linux/user_linux.cpp" "os/linux/version_linux.cpp" ) elseif (APPLE) set(MARATHON_RECOMP_OS_CXX_SOURCES "os/macos/logger_macos.cpp" "os/macos/media_macos.cpp" "os/macos/process_macos.cpp" "os/macos/user_macos.cpp" "os/macos/version_macos.cpp" ) endif() set(MARATHON_RECOMP_CPU_CXX_SOURCES "cpu/guest_thread.cpp" ) set(MARATHON_RECOMP_GPU_CXX_SOURCES "gpu/video.cpp" "gpu/imgui/imgui_common.cpp" "gpu/imgui/imgui_font_builder.cpp" "gpu/imgui/imgui_snapshot.cpp" ) set(MARATHON_RECOMP_APU_CXX_SOURCES "apu/audio.cpp" "apu/xma_decoder.cpp" "apu/embedded_player.cpp" "apu/driver/sdl2_driver.cpp" ) set(MARATHON_RECOMP_HID_CXX_SOURCES "hid/hid.cpp" "hid/driver/sdl_hid.cpp" ) set(MARATHON_RECOMP_PATCHES_CXX_SOURCES "patches/aspect_ratio_patches.cpp" "patches/audio_patches.cpp" "patches/camera_patches.cpp" "patches/fps_patches.cpp" "patches/frontend_listener.cpp" "patches/input_patches.cpp" "patches/loading_patches.cpp" "patches/MainMenuTask_patches.cpp" "patches/misc_patches.cpp" "patches/pause_patches.cpp" "patches/player_patches.cpp" "patches/SaveDataTask_patches.cpp" "patches/text_patches.cpp" "patches/TitleTask_patches.cpp" "patches/video_patches.cpp" ) set(MARATHON_RECOMP_UI_CXX_SOURCES "ui/achievement_menu.cpp" "ui/achievement_overlay.cpp" "ui/black_bar.cpp" "ui/button_window.cpp" "ui/common_menu.cpp" "ui/fader.cpp" "ui/game_window.cpp" "ui/imgui_utils.cpp" "ui/installer_wizard.cpp" "ui/message_window.cpp" "ui/options_menu.cpp" ) set(MARATHON_RECOMP_INSTALL_CXX_SOURCES "install/installer.cpp" "install/iso_file_system.cpp" "install/update_checker.cpp" "install/xcontent_file_system.cpp" "install/hashes/game.cpp" "install/hashes/episode_sonic.cpp" "install/hashes/episode_shadow.cpp" "install/hashes/episode_silver.cpp" "install/hashes/episode_amigo.cpp" "install/hashes/mission_sonic.cpp" "install/hashes/mission_shadow.cpp" "install/hashes/mission_silver.cpp" ) set(MARATHON_RECOMP_USER_CXX_SOURCES "user/achievement_data.cpp" "user/achievement_manager.cpp" "user/config.cpp" "user/registry.cpp" "user/paths.cpp" ) set(MARATHON_RECOMP_MOD_CXX_SOURCES "mod/mod_loader.cpp" ) set(MARATHON_RECOMP_UTILS_CXX_SOURCES "utils/bit_stream.cpp" "utils/ring_buffer.cpp" ) set(MARATHON_RECOMP_THIRDPARTY_SOURCES "${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/backends/imgui_impl_sdl2.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_demo.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_draw.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_tables.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui/imgui_widgets.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/implot/implot.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/implot/implot_demo.cpp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/implot/implot_items.cpp" "${MARATHON_RECOMP_TOOLS_ROOT}/XenosRecomp/thirdparty/smol-v/source/smolv.cpp" ) set(MARATHON_RECOMP_THIRDPARTY_INCLUDES "${MARATHON_RECOMP_THIRDPARTY_ROOT}/concurrentqueue" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/ddspp" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/imgui" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/implot" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/json/include" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/magic_enum/include" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/stb" "${MARATHON_RECOMP_THIRDPARTY_ROOT}/unordered_dense/include" "${MARATHON_RECOMP_TOOLS_ROOT}/bc_diff" "${MARATHON_RECOMP_TOOLS_ROOT}/XenosRecomp/thirdparty/smol-v/source" ) set_source_files_properties(${MARATHON_RECOMP_THIRDPARTY_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON) set(MARATHON_RECOMP_CXX_SOURCES "app.cpp" "exports.cpp" "main.cpp" "misc_impl.cpp" "preload_executable.cpp" "sdl_listener.cpp" "stdafx.cpp" "version.cpp" ${MARATHON_RECOMP_KERNEL_CXX_SOURCES} ${MARATHON_RECOMP_LOCALE_CXX_SOURCES} ${MARATHON_RECOMP_OS_CXX_SOURCES} ${MARATHON_RECOMP_CPU_CXX_SOURCES} ${MARATHON_RECOMP_GPU_CXX_SOURCES} ${MARATHON_RECOMP_APU_CXX_SOURCES} ${MARATHON_RECOMP_HID_CXX_SOURCES} ${MARATHON_RECOMP_PATCHES_CXX_SOURCES} ${MARATHON_RECOMP_UI_CXX_SOURCES} ${MARATHON_RECOMP_INSTALL_CXX_SOURCES} ${MARATHON_RECOMP_USER_CXX_SOURCES} ${MARATHON_RECOMP_UTILS_CXX_SOURCES} ${MARATHON_RECOMP_MOD_CXX_SOURCES} ${MARATHON_RECOMP_THIRDPARTY_SOURCES} ) include("version.cmake") set(VERSION_TXT "${PROJECT_SOURCE_DIR}/res/version.txt") # Only show Git info and build type if not Release. set(SHOW_GIT_INFO_AND_BUILD_TYPE 0) if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Release") set(SHOW_GIT_INFO_AND_BUILD_TYPE 1) endif() if (MARATHON_RECOMP_METAL) set(XCRUN_TOOL "/usr/bin/xcrun") endif() GenerateVersionSources( OUTPUT_DIR ${PROJECT_SOURCE_DIR} VERSION_TXT ${VERSION_TXT} H_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.h.template" CXX_TEMPLATE "${PROJECT_SOURCE_DIR}/res/version.cpp.template" BUILD_TYPE ${CMAKE_BUILD_TYPE} SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE} SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE} ) if (WIN32) # Create binary version number for Win32 integer attributes. CreateVersionString( VERSION_TXT ${VERSION_TXT} OUTPUT_CSV 1 OUTPUT_VAR WIN32_VERSION_BINARY ) # Create string version number for Win32 detailed attributes. CreateVersionString( VERSION_TXT ${VERSION_TXT} BUILD_TYPE ${CMAKE_BUILD_TYPE} SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE} SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE} OUTPUT_VAR WIN32_VERSION_STRING ) # Set Win32 icon path. set(WIN32_ICON_PATH "${PROJECT_SOURCE_DIR}/../MarathonRecompResources/images/game_icon.ico") configure_file("res/win32/res.rc.template" "${CMAKE_BINARY_DIR}/res.rc" @ONLY) add_executable(MarathonRecomp ${MARATHON_RECOMP_CXX_SOURCES} "${CMAKE_BINARY_DIR}/res.rc") # Hide console for release configurations. if (${CMAKE_BUILD_TYPE} MATCHES "Release") target_link_options(MarathonRecomp PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup") endif() elseif (APPLE) # Create version number for app bundle. CreateVersionString( VERSION_TXT ${VERSION_TXT} OUTPUT_VAR MACOS_BUNDLE_VERSION ) add_executable(MarathonRecomp MACOSX_BUNDLE ${MARATHON_RECOMP_CXX_SOURCES} res/macos/game_icon.icns ) set_source_files_properties(res/macos/game_icon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set_target_properties(MarathonRecomp PROPERTIES OUTPUT_NAME "Marathon Recompiled" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/res/macos/MacOSXBundleInfo.plist.in MACOSX_BUNDLE_GUI_IDENTIFIER sonicnext-dev.MarathonRecomp MACOSX_BUNDLE_BUNDLE_NAME "Marathon Recompiled" MACOSX_BUNDLE_BUNDLE_VERSION ${MACOS_BUNDLE_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${MACOS_BUNDLE_VERSION} MACOSX_BUNDLE_ICON_FILE "game_icon.icns" ) # Linking with MoltenVK directly would prevent using the system Vulkan loader to load with debug layers. # Instead, copy the MoltenVK dylib to the app bundle along with an ICD file for the loader to find it. # In the event the loader is not installed, the MoltenVK dylib can still be picked up directly in the app bundle. set(MVK_BUNDLED_PATH "Resources/vulkan/icd.d") set(MVK_DST "${CMAKE_CURRENT_BINARY_DIR}/Marathon Recompiled.app/Contents/${MVK_BUNDLED_PATH}") set_property(TARGET MarathonRecomp APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLED_PATH}") set(MVK_ICD_SRC "${MARATHON_RECOMP_THIRDPARTY_ROOT}/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json") set(MVK_ICD_DST "${MVK_DST}/MoltenVK_icd.json") set(MVK_DYLIB_SRC "${CMAKE_BINARY_DIR}/thirdparty/MoltenVK/libMoltenVK.dylib") set(MVK_DYLIB_DST "${MVK_DST}/libMoltenVK.dylib") add_custom_command( OUTPUT ${MVK_DST} COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST}) add_custom_command( OUTPUT ${MVK_ICD_DST} DEPENDS ${MVK_ICD_SRC} ${MVK_DST} COMMAND ${CMAKE_COMMAND} -E copy ${MVK_ICD_SRC} ${MVK_ICD_DST}) add_custom_command( OUTPUT ${MVK_DYLIB_DST} DEPENDS ${MVK_DYLIB_SRC} ${MVK_DST} COMMAND ${CMAKE_COMMAND} -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST}) add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST}) add_dependencies(CopyMoltenVK MoltenVK) add_dependencies(MarathonRecomp CopyMoltenVK) else() add_executable(MarathonRecomp ${MARATHON_RECOMP_CXX_SOURCES}) endif() if (MARATHON_RECOMP_FLATPAK) target_compile_definitions(MarathonRecomp PRIVATE "MARATHON_RECOMP_FLATPAK" "GAME_INSTALL_DIRECTORY=\"/var/data\"" ) endif() find_package(CURL REQUIRED) if (MARATHON_RECOMP_METAL) target_compile_definitions(MarathonRecomp PRIVATE MARATHON_RECOMP_METAL) endif() if (MARATHON_RECOMP_D3D12) find_package(directx-headers CONFIG REQUIRED) find_package(directx12-agility CONFIG REQUIRED) target_compile_definitions(MarathonRecomp PRIVATE MARATHON_RECOMP_D3D12) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) add_custom_command(TARGET MarathonRecomp POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ $/D3D12 COMMAND ${CMAKE_COMMAND} -E copy $ $/D3D12 COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND_EXPAND_LISTS ) target_link_libraries(MarathonRecomp PRIVATE Microsoft::DirectXShaderCompiler Microsoft::DXIL dxgi ) endif() if (WIN32) target_link_libraries(MarathonRecomp PRIVATE comctl32 dwmapi ntdll Shcore Synchronization winmm windowsapp ) endif() target_link_libraries(MarathonRecomp PRIVATE fmt::fmt libzstd_static msdf-atlas-gen::msdf-atlas-gen nfd::nfd o1heap XenonUtils SDL2::SDL2-static SDL2_mixer tomlplusplus::tomlplusplus MarathonRecompLib xxHash::xxhash CURL::libcurl plume ) target_link_libraries(MarathonRecomp PRIVATE ffmpeg) target_include_directories(MarathonRecomp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/api" ${MARATHON_RECOMP_THIRDPARTY_INCLUDES} ) if (CMAKE_SYSTEM_NAME MATCHES "Linux") find_package(PkgConfig REQUIRED) find_package(X11 REQUIRED) pkg_search_module(GLIB REQUIRED glib-2.0) pkg_search_module(GIO REQUIRED gio-2.0) target_include_directories(MarathonRecomp PRIVATE ${X11_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS}) target_link_libraries(MarathonRecomp PRIVATE ${X11_LIBRARIES} ${GLIB_LIBRARIES} ${GIO_LIBRARIES}) endif() target_precompile_headers(MarathonRecomp PUBLIC ${MARATHON_RECOMP_PRECOMPILED_HEADERS}) function(compile_shader FILE_PATH TARGET_NAME) set(HLSL_FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gpu/shader/hlsl/${FILE_PATH}.hlsl) set(MSL_FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gpu/shader/msl/${FILE_PATH}.metal) cmake_path(GET HLSL_FILE_PATH STEM HLSL_NAME) cmake_path(GET MSL_FILE_PATH STEM MSL_NAME) if (MARATHON_RECOMP_METAL) add_custom_command( OUTPUT ${MSL_FILE_PATH}.ir COMMAND ${XCRUN_TOOL} -sdk macosx metal -o ${MSL_FILE_PATH}.ir -c ${MSL_FILE_PATH} -D__air__ -frecord-sources -gline-tables-only DEPENDS ${MSL_FILE_PATH} ) add_custom_command( OUTPUT ${MSL_FILE_PATH}.metallib COMMAND ${XCRUN_TOOL} -sdk macosx metallib -o ${MSL_FILE_PATH}.metallib ${MSL_FILE_PATH}.ir DEPENDS ${MSL_FILE_PATH}.ir ) BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${MSL_FILE_PATH}.metallib" DEST_FILE "${MSL_FILE_PATH}.metallib" ARRAY_NAME "g_${MSL_NAME}_air") endif() if (MARATHON_RECOMP_D3D12) add_custom_command( OUTPUT ${HLSL_FILE_PATH}.dxil.h COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -Wno-ignored-attributes -E shaderMain -Fh ${HLSL_FILE_PATH}.dxil.h ${HLSL_FILE_PATH} -Vn g_${HLSL_NAME}_dxil DEPENDS ${HLSL_FILE_PATH} ) target_sources(MarathonRecomp PRIVATE ${HLSL_FILE_PATH}.dxil.h) endif() add_custom_command( OUTPUT ${HLSL_FILE_PATH}.spirv.h COMMAND ${DIRECTX_DXC_TOOL} -T ${TARGET_NAME} -HV 2021 -all-resources-bound -spirv -fvk-use-dx-layout ${ARGN} -E shaderMain -Fh ${HLSL_FILE_PATH}.spirv.h ${HLSL_FILE_PATH} -Vn g_${HLSL_NAME}_spirv DEPENDS ${HLSL_FILE_PATH} ) target_sources(MarathonRecomp PRIVATE ${HLSL_FILE_PATH}.spirv.h) endfunction() function(compile_vertex_shader FILE_PATH) compile_shader(${FILE_PATH} vs_6_0 -fvk-invert-y -DMARATHON_RECOMP) endfunction() function(compile_pixel_shader FILE_PATH) compile_shader(${FILE_PATH} ps_6_0 -DMARATHON_RECOMP) endfunction() compile_pixel_shader(blend_color_alpha_ps) compile_pixel_shader(conditional_survey_ps) compile_vertex_shader(copy_vs) compile_pixel_shader(copy_color_ps) compile_pixel_shader(copy_depth_ps) compile_pixel_shader(csd_filter_ps) compile_vertex_shader(csd_no_tex_vs) compile_vertex_shader(csd_vs) compile_vertex_shader(enhanced_burnout_blur_vs) compile_pixel_shader(enhanced_burnout_blur_ps) compile_pixel_shader(gaussian_blur_3x3) compile_pixel_shader(gaussian_blur_5x5) compile_pixel_shader(gaussian_blur_7x7) compile_pixel_shader(gaussian_blur_9x9) compile_pixel_shader(gamma_correction_ps) compile_pixel_shader(imgui_ps) compile_vertex_shader(imgui_vs) compile_pixel_shader(resolve_msaa_color_2x) compile_pixel_shader(resolve_msaa_color_4x) compile_pixel_shader(resolve_msaa_color_8x) compile_pixel_shader(resolve_msaa_depth_2x) compile_pixel_shader(resolve_msaa_depth_4x) compile_pixel_shader(resolve_msaa_depth_8x) function(generate_aggregate_header INPUT_DIRECTORY OUTPUT_FILE) get_filename_component(ABS_OUTPUT_FILE "${OUTPUT_FILE}" ABSOLUTE) file(GLOB_RECURSE HEADER_FILES "${INPUT_DIRECTORY}/*.h") set(HEADER_CONTENT "#pragma once\n\n") foreach(HEADER_FILE IN LISTS HEADER_FILES) get_filename_component(ABS_HEADER_FILE "${HEADER_FILE}" ABSOLUTE) if (ABS_HEADER_FILE STREQUAL ABS_OUTPUT_FILE) continue() endif() file(RELATIVE_PATH RELATIVE_HEADER_FILE "${INPUT_DIRECTORY}" "${HEADER_FILE}") string(APPEND HEADER_CONTENT "#include \"${RELATIVE_HEADER_FILE}\"\n") endforeach() if (EXISTS "${OUTPUT_FILE}") file(READ "${OUTPUT_FILE}" EXISTING_CONTENT) if (EXISTING_CONTENT STREQUAL HEADER_CONTENT) return() endif() endif() file(WRITE "${OUTPUT_FILE}" "${HEADER_CONTENT}") endfunction() generate_aggregate_header( "${CMAKE_CURRENT_SOURCE_DIR}/api" "${CMAKE_CURRENT_SOURCE_DIR}/api/Marathon.h" ) set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../MarathonRecompResources") set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") ## Miscellaneous ## BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/bc_diff/button_bc_diff.bin" DEST_FILE "${RESOURCES_OUTPUT_PATH}/bc_diff/button_bc_diff.bin" ARRAY_NAME "g_button_bc_diff" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/font/im_font_atlas.bin" DEST_FILE "${RESOURCES_OUTPUT_PATH}/font/im_font_atlas.bin" ARRAY_NAME "g_im_font_atlas" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/font/im_font_atlas.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/font/im_font_atlas.dds" ARRAY_NAME "g_im_font_atlas_texture" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/button_window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/button_window.dds" ARRAY_NAME "g_button_window" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/controller.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/controller.dds" ARRAY_NAME "g_controller" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/sonicnext-dev.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/sonicnext-dev.dds" ARRAY_NAME "g_sonicnextdev" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/kbm.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/kbm.dds" ARRAY_NAME "g_kbm" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/main_menu1.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/main_menu1.dds" ARRAY_NAME "g_main_menu1" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/main_menu7.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/main_menu7.dds" ARRAY_NAME "g_main_menu7" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/main_menu8.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/main_menu8.dds" ARRAY_NAME "g_main_menu8" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/main_menu9.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/main_menu9.dds" ARRAY_NAME "g_main_menu9" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/arrow.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/arrow.dds" ARRAY_NAME "g_arrow" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/window.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/window.dds" ARRAY_NAME "g_window" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_arrow.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_arrow.dds" ARRAY_NAME "g_select_arrow" COMPRESSION_TYPE "zstd") ## Installer ## BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_NAME "g_install_001" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_002.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_002.dds" ARRAY_NAME "g_install_002" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_003.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_003.dds" ARRAY_NAME "g_install_003" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_004.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_004.dds" ARRAY_NAME "g_install_004" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_005.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_005.dds" ARRAY_NAME "g_install_005" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_006.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_006.dds" ARRAY_NAME "g_install_006" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_007.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_007.dds" ARRAY_NAME "g_install_007" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_008.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_008.dds" ARRAY_NAME "g_install_008" COMPRESSION_TYPE "zstd") ## Game Icon ## BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon") ## Audio ## BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/music/installer.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/music/installer.ogg" ARRAY_NAME "g_installer_music") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/window_open.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/window_open.ogg" ARRAY_NAME "g_window_open") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/window_close.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/window_close.ogg" ARRAY_NAME "g_window_close") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/cursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/cursor.ogg" ARRAY_NAME "g_cursor") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/deside.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/deside.ogg" ARRAY_NAME "g_deside") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/move.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/move.ogg" ARRAY_NAME "g_move") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/main_deside.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/main_deside.ogg" ARRAY_NAME "g_main_deside") BIN2C(TARGET_OBJ MarathonRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/cannot_deside.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/cannot_deside.ogg" ARRAY_NAME "g_cannot_deside") ================================================ FILE: MarathonRecomp/api/Chao/CSD/Core/csdBase.h ================================================ #pragma once namespace Chao::CSD { class CBase {}; } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Core/csdRCObject.h ================================================ #pragma once #include namespace Chao::CSD { class RCPtrAbs::RCObject { public: struct Vftable { be fpDestroy; be fpFree; }; xpointer m_pVftable; xpointer m_pMemory; be m_ReferenceCount; xpointer m_Field0C; be m_Field10; }; MARATHON_ASSERT_OFFSETOF(RCPtrAbs::RCObject, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(RCPtrAbs::RCObject, m_pMemory, 0x04); MARATHON_ASSERT_OFFSETOF(RCPtrAbs::RCObject, m_ReferenceCount, 0x08); MARATHON_ASSERT_OFFSETOF(RCPtrAbs::RCObject, m_Field0C, 0x0C); MARATHON_ASSERT_OFFSETOF(RCPtrAbs::RCObject, m_Field10, 0x10); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Core/csdRCObjectImp.h ================================================ #pragma once #include namespace Chao::CSD { template class RCPtr::RCObjectImp : public RCObject {}; } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Core/csdRCPtr.h ================================================ #pragma once #include namespace Chao::CSD { template class RCPtr : RCPtrAbs { public: class RCObjectImp; }; } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Core/csdRCPtrAbs.h ================================================ #pragma once #include namespace Chao::CSD { class RCPtrAbs { public: class RCObject; struct Vftable { be fpDestroy; be fpCreateRCObject; }; xpointer m_pVftable; xpointer m_pObject; }; MARATHON_ASSERT_OFFSETOF(RCPtrAbs, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(RCPtrAbs, m_pObject, 0x04); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Core/csdTexList.h ================================================ #pragma once #include #include namespace Chao::CSD { class CTexList : public CBase { public: struct Vftable { be fpDestroy; }; xpointer m_pVftable; RCPtr m_pRCData; }; MARATHON_ASSERT_OFFSETOF(CTexList, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(CTexList, m_pRCData, 0x04); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmMotionPattern.h ================================================ #pragma once #include namespace Chao::CSD { class CMotionPattern : CBase {}; } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmNode.h ================================================ #pragma once #include #include #include #include #include namespace Chao::CSD { struct Node; class CNode : public CResourceBase, SubjectBase, CBase { public: MARATHON_INSERT_PADDING(0x34); xpointer m_pMotionPattern; MARATHON_INSERT_PADDING(0x18); }; MARATHON_ASSERT_OFFSETOF(CNode, m_pMotionPattern, 0x50); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmNodeObserver.h ================================================ #pragma once #include namespace Chao::CSD { class CNode; class CNodeObserver : public CObserverBase {}; } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmObserverBase.h ================================================ #pragma once #include namespace Chao::CSD { template class CObserverBase { public: struct Vftable { be fpDestroy; }; xpointer m_pVftable; MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_OFFSETOF(CObserverBase, m_pVftable, 0x00); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmProject.h ================================================ #pragma once #include #include #include namespace Chao::CSD { class SceneNode; class CProject; class CScene; class CTexList; struct Project { xpointer pRootNode; }; class CProject : public CResourceBase, CBase { public: MARATHON_INSERT_PADDING(0x1C); RCPtr m_rcTexList; MARATHON_INSERT_PADDING(0x1C); }; MARATHON_ASSERT_OFFSETOF(Project, pRootNode, 0x00); MARATHON_ASSERT_OFFSETOF(CProject, m_rcTexList, 0x28); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmResourceBase.h ================================================ #pragma once #include namespace Chao::CSD { template class CResourceBase { public: struct Vftable { be fpDestroy; be fpCopyResource; }; xpointer m_pVftable; MARATHON_INSERT_PADDING(4); xpointer m_pResource; }; MARATHON_ASSERT_OFFSETOF(CResourceBase, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(CResourceBase, m_pResource, 0x08); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmScene.h ================================================ #pragma once #include #include #include #include #include namespace Chao::CSD { class CScene; class CNode; struct Cast { MARATHON_INSERT_PADDING(0x144); }; struct CastLink { be ChildCastIndex; be SiblingCastIndex; }; struct CastNode { be CastCount; MARATHON_INSERT_PADDING(4); xpointer> pCasts; MARATHON_INSERT_PADDING(4); be RootCastIndex; MARATHON_INSERT_PADDING(4); xpointer pCastLinks; MARATHON_INSERT_PADDING(4); }; struct CastIndex { xpointer pCastName; MARATHON_INSERT_PADDING(4); be CastNodeIndex; be CastIndex; }; struct Scene { MARATHON_INSERT_PADDING(8); be FPS; MARATHON_INSERT_PADDING(0x24); be CastNodeCount; MARATHON_INSERT_PADDING(4); xpointer pCastNodes; MARATHON_INSERT_PADDING(4); be CastCount; MARATHON_INSERT_PADDING(4); xpointer pCastIndices; MARATHON_INSERT_PADDING(4); be AnimationCount; MARATHON_INSERT_PADDING(4); xpointer pAnimationKeyFrameDataList; MARATHON_INSERT_PADDING(4); xpointer pAnimationDictionary; MARATHON_INSERT_PADDING(4); be AspectRatio; MARATHON_INSERT_PADDING(4); xpointer pAnimationFrameDataList; MARATHON_INSERT_PADDING(4); }; struct SceneIndex { xpointer pSceneName; MARATHON_INSERT_PADDING(4); be SceneIndex; MARATHON_INSERT_PADDING(4); }; struct SceneNodeIndex { xpointer pSceneNodeName; be SceneNodeIndex; }; struct SceneNode { be SceneCount; MARATHON_INSERT_PADDING(4); xpointer> pScenes; MARATHON_INSERT_PADDING(4); xpointer pSceneIndices; MARATHON_INSERT_PADDING(4); be SceneNodeCount; MARATHON_INSERT_PADDING(4); xpointer pSceneNodes; MARATHON_INSERT_PADDING(4); xpointer pSceneNodeIndices; }; enum MotionRepeatType : uint32_t { MotionRepeatType_PlayOnce, MotionRepeatType_Loop, MotionRepeatType_PingPong, MotionRepeatType_PlayThenDestroy }; class CScene : public CResourceBase, SubjectBase, CBase { public: MARATHON_INSERT_PADDING(0x60); be m_PrevMotionFrame; be m_MotionFrame; be m_MotionSpeed; be m_MotionStartFrame; be m_MotionEndFrame; MARATHON_INSERT_PADDING(0x0C); be m_MotionDisableFlag; MARATHON_INSERT_PADDING(0x10); be m_MotionRepeatType; MARATHON_INSERT_PADDING(0x2C); }; MARATHON_ASSERT_OFFSETOF(CastLink, ChildCastIndex, 0x00); MARATHON_ASSERT_OFFSETOF(CastLink, SiblingCastIndex, 0x04); MARATHON_ASSERT_OFFSETOF(CastNode, CastCount, 0x00); MARATHON_ASSERT_OFFSETOF(CastNode, pCasts, 0x08); MARATHON_ASSERT_OFFSETOF(CastNode, RootCastIndex, 0x10); MARATHON_ASSERT_OFFSETOF(CastNode, pCastLinks, 0x18); MARATHON_ASSERT_OFFSETOF(CastIndex, pCastName, 0x00); MARATHON_ASSERT_OFFSETOF(CastIndex, CastNodeIndex, 0x08); MARATHON_ASSERT_OFFSETOF(CastIndex, CastIndex, 0x0C); MARATHON_ASSERT_OFFSETOF(Scene, FPS, 0x08); MARATHON_ASSERT_OFFSETOF(Scene, CastNodeCount, 0x30); MARATHON_ASSERT_OFFSETOF(Scene, pCastNodes, 0x38); MARATHON_ASSERT_OFFSETOF(Scene, CastCount, 0x40); MARATHON_ASSERT_OFFSETOF(Scene, pCastIndices, 0x48); MARATHON_ASSERT_OFFSETOF(Scene, AnimationCount, 0x50); MARATHON_ASSERT_OFFSETOF(Scene, pAnimationKeyFrameDataList, 0x58); MARATHON_ASSERT_OFFSETOF(Scene, pAnimationDictionary, 0x60); MARATHON_ASSERT_OFFSETOF(Scene, AspectRatio, 0x68); MARATHON_ASSERT_OFFSETOF(Scene, pAnimationFrameDataList, 0x70); MARATHON_ASSERT_OFFSETOF(SceneIndex, pSceneName, 0x00); MARATHON_ASSERT_OFFSETOF(SceneIndex, SceneIndex, 0x08); MARATHON_ASSERT_OFFSETOF(SceneNodeIndex, pSceneNodeName, 0x00); MARATHON_ASSERT_OFFSETOF(SceneNodeIndex, SceneNodeIndex, 0x04); MARATHON_ASSERT_OFFSETOF(SceneNode, SceneCount, 0x00); MARATHON_ASSERT_OFFSETOF(SceneNode, pScenes, 0x08); MARATHON_ASSERT_OFFSETOF(SceneNode, pSceneIndices, 0x10); MARATHON_ASSERT_OFFSETOF(SceneNode, SceneNodeCount, 0x18); MARATHON_ASSERT_OFFSETOF(SceneNode, pSceneNodes, 0x20); MARATHON_ASSERT_OFFSETOF(SceneNode, pSceneNodeIndices, 0x28); MARATHON_ASSERT_OFFSETOF(CScene, m_PrevMotionFrame, 0x7C); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionFrame, 0x80); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionSpeed, 0x84); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionStartFrame, 0x88); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionEndFrame, 0x8C); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionDisableFlag, 0x9C); MARATHON_ASSERT_OFFSETOF(CScene, m_MotionRepeatType, 0xB0); } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmSceneObserver.h ================================================ #pragma once #include namespace Chao::CSD { class CScene; class CSceneObserver : public CObserverBase {}; } ================================================ FILE: MarathonRecomp/api/Chao/CSD/Manager/csdmSubjectBase.h ================================================ #pragma once #include namespace Chao::CSD { template class SubjectBase { public: struct Vftable { be fpDestroy; be fpGetObservee; }; xpointer m_pVftable; MARATHON_INSERT_PADDING(0x0C); }; static_assert(__builtin_offsetof(SubjectBase, m_pVftable) == 0x00); } ================================================ FILE: MarathonRecomp/api/Marathon.h ================================================ #pragma once #include "Chao/CSD/Core/csdBase.h" #include "Chao/CSD/Core/csdRCObject.h" #include "Chao/CSD/Core/csdRCObjectImp.h" #include "Chao/CSD/Core/csdRCPtr.h" #include "Chao/CSD/Core/csdRCPtrAbs.h" #include "Chao/CSD/Core/csdTexList.h" #include "Chao/CSD/Manager/csdmMotionPattern.h" #include "Chao/CSD/Manager/csdmNode.h" #include "Chao/CSD/Manager/csdmNodeObserver.h" #include "Chao/CSD/Manager/csdmObserverBase.h" #include "Chao/CSD/Manager/csdmProject.h" #include "Chao/CSD/Manager/csdmResourceBase.h" #include "Chao/CSD/Manager/csdmScene.h" #include "Chao/CSD/Manager/csdmSceneObserver.h" #include "Chao/CSD/Manager/csdmSubjectBase.h" #include "Sonicteam/Actor.h" #include "Sonicteam/ActorManager.h" #include "Sonicteam/AlertWindowTask.h" #include "Sonicteam/AppMarathon.h" #include "Sonicteam/AudioEngineXenon.h" #include "Sonicteam/ButtonWindowTask.h" #include "Sonicteam/CObjBalloonIconDrawable.h" #include "Sonicteam/Camera/CameraMode.h" #include "Sonicteam/Camera/CameraModeManager.h" #include "Sonicteam/Camera/Cameraman.h" #include "Sonicteam/Camera/SonicCamera.h" #include "Sonicteam/CommonObjectHint.h" #include "Sonicteam/CsdLink.h" #include "Sonicteam/CsdManager.h" #include "Sonicteam/CsdObject.h" #include "Sonicteam/CsdResource.h" #include "Sonicteam/DocMarathonImp.h" #include "Sonicteam/DocMarathonState.h" #include "Sonicteam/Enemy/EnemyShot.h" #include "Sonicteam/Enemy/EnemyShotNormal.h" #include "Sonicteam/Enemy/EnemyShotPoint.h" #include "Sonicteam/Fixture.h" #include "Sonicteam/Game.h" #include "Sonicteam/GameImp.h" #include "Sonicteam/GameMode.h" #include "Sonicteam/Globals.h" #include "Sonicteam/HUDButtonWindow.h" #include "Sonicteam/HUDCALLBACK.h" #include "Sonicteam/HUDGoldMedal.h" #include "Sonicteam/HUDLimitTime.h" #include "Sonicteam/HUDLoading.h" #include "Sonicteam/HUDMainDisplay.h" #include "Sonicteam/HUDMainMenu.h" #include "Sonicteam/HUDMessageWindow.h" #include "Sonicteam/HUDOption.h" #include "Sonicteam/HUDPopupScreen.h" #include "Sonicteam/HUDRaderMap.h" #include "Sonicteam/HUDStageTitle.h" #include "Sonicteam/HintWindowTask.h" #include "Sonicteam/HudTextParts.h" #include "Sonicteam/ImageFilter.h" #include "Sonicteam/MainDisplayTask.h" #include "Sonicteam/MainMenuExpositionTask.h" #include "Sonicteam/MainMenuTask.h" #include "Sonicteam/MainMode.h" #include "Sonicteam/Message/Camera/Cameraman/MsgChangeMode.h" #include "Sonicteam/Message/HUDButtonWindow/MsgChangeButtons.h" #include "Sonicteam/Message/HUDGoldMedal/MsgChangeState.h" #include "Sonicteam/Message/HUDMainMenu/MsgChangeState.h" #include "Sonicteam/Message/HUDMainMenu/MsgSetCursor.h" #include "Sonicteam/Message/HUDMainMenu/MsgTransition.h" #include "Sonicteam/Message/Mission/MsgGetGlobalFlag.h" #include "Sonicteam/Message/ObjJump123/MsgGetNextPoint.h" #include "Sonicteam/Message/PauseAdapter/MsgGetText.h" #include "Sonicteam/Message/Player/MsgSuckPlayer.h" #include "Sonicteam/MessageWindowTask.h" #include "Sonicteam/Mission/Core.h" #include "Sonicteam/MovieObject.h" #include "Sonicteam/MovieObjectWmv.h" #include "Sonicteam/MovieTask.h" #include "Sonicteam/MyCue.h" #include "Sonicteam/MyCueAdx.h" #include "Sonicteam/MyCueAttenuate.h" #include "Sonicteam/MyGraphicsDevice.h" #include "Sonicteam/MyPhantom.h" #include "Sonicteam/MyRenderProcess.h" #include "Sonicteam/MyTexture.h" #include "Sonicteam/MyTransforms.h" #include "Sonicteam/NamedActor.h" #include "Sonicteam/NamedTask.h" #include "Sonicteam/NoSyncThread.h" #include "Sonicteam/ObjectVehicle.h" #include "Sonicteam/ObjectVehicleBike.h" #include "Sonicteam/PauseAdapter.h" #include "Sonicteam/PauseTask.h" #include "Sonicteam/Player/GroundRayListener.h" #include "Sonicteam/Player/ICollisionListener.h" #include "Sonicteam/Player/ICollisionListenerTemplate.h" #include "Sonicteam/Player/IDynamicLink.h" #include "Sonicteam/Player/IEventerListener.h" #include "Sonicteam/Player/IExportExternalFlag.h" #include "Sonicteam/Player/IExportPostureRequestFlag.h" #include "Sonicteam/Player/IExportWeaponRequestFlag.h" #include "Sonicteam/Player/IFlagCommunicator.h" #include "Sonicteam/Player/IGauge.h" #include "Sonicteam/Player/INotification.h" #include "Sonicteam/Player/IPlugIn.h" #include "Sonicteam/Player/IPostureControl.h" #include "Sonicteam/Player/IPosturePlugIn.h" #include "Sonicteam/Player/IPostureSupportEdge.h" #include "Sonicteam/Player/IPostureSupportInput.h" #include "Sonicteam/Player/IPostureSupportOttoto.h" #include "Sonicteam/Player/IPostureSupportRayTemplate.h" #include "Sonicteam/Player/IPostureSupportSphere.h" #include "Sonicteam/Player/IScore.h" #include "Sonicteam/Player/IStepable.h" #include "Sonicteam/Player/IVariable.h" #include "Sonicteam/Player/IZock.h" #include "Sonicteam/Player/Input/IListener.h" #include "Sonicteam/Player/Input/ListenerNormal.h" #include "Sonicteam/Player/Input/TimedAction.h" #include "Sonicteam/Player/Object.h" #include "Sonicteam/Player/PostureControl.h" #include "Sonicteam/Player/RootFrame.h" #include "Sonicteam/Player/Score.h" #include "Sonicteam/Player/SonicGauge.h" #include "Sonicteam/Player/State/CommonContext.h" #include "Sonicteam/Player/State/CommonFall.h" #include "Sonicteam/Player/State/CommonObject.h" #include "Sonicteam/Player/State/ContextSpeedAndJump.h" #include "Sonicteam/Player/State/ICommonContext.h" #include "Sonicteam/Player/State/ICommonContextIF.h" #include "Sonicteam/Player/State/IContext.h" #include "Sonicteam/Player/State/IMachine.h" #include "Sonicteam/Player/State/Machine2.h" #include "Sonicteam/Player/State/Object2.h" #include "Sonicteam/Player/State/SonicContext.h" #include "Sonicteam/Player/State/SonicObject.h" #include "Sonicteam/Player/State/TailsContext.h" #include "Sonicteam/Player/State/TailsFlight.h" #include "Sonicteam/Player/Unit/ITestCase.h" #include "Sonicteam/Player/Weapon/SonicWeapons.h" #include "Sonicteam/Player/Zock.h" #include "Sonicteam/PropFixture.h" #include "Sonicteam/RaderMapManager.h" #include "Sonicteam/RenderAction/ApplyBloom.h" #include "Sonicteam/RenderAction/ApplyDevice.h" #include "Sonicteam/RenderAction/ApplySceneParams.h" #include "Sonicteam/RenderAction/AutoSetAspect.h" #include "Sonicteam/RenderAction/Capture.h" #include "Sonicteam/RenderAction/ClearRenderTarget.h" #include "Sonicteam/RenderAction/ColorFill.h" #include "Sonicteam/RenderAction/CopyTexture.h" #include "Sonicteam/RenderAction/LockBlendMode.h" #include "Sonicteam/RenderAction/LockCullMode.h" #include "Sonicteam/RenderAction/LockZMode.h" #include "Sonicteam/RenderAction/MakeBloom.h" #include "Sonicteam/RenderAction/Movie.h" #include "Sonicteam/RenderAction/PrepareCalculateCSM.h" #include "Sonicteam/RenderAction/Rasterize.h" #include "Sonicteam/RenderAction/RasterizeBurnoutBlur.h" #include "Sonicteam/RenderAction/ResetRenderStates.h" #include "Sonicteam/RenderAction/ResetScissorRect.h" #include "Sonicteam/RenderAction/ResetViewport.h" #include "Sonicteam/RenderAction/Resolve.h" #include "Sonicteam/RenderAction/RunCommandBuffer.h" #include "Sonicteam/RenderAction/SetAutoZPass.h" #include "Sonicteam/RenderAction/SetBackStencilOp.h" #include "Sonicteam/RenderAction/SetBlendMode.h" #include "Sonicteam/RenderAction/SetCSMTextures.h" #include "Sonicteam/RenderAction/SetClip.h" #include "Sonicteam/RenderAction/SetColorWriteEnable.h" #include "Sonicteam/RenderAction/SetConstantShader.h" #include "Sonicteam/RenderAction/SetCullMode.h" #include "Sonicteam/RenderAction/SetCurrentScreen.h" #include "Sonicteam/RenderAction/SetDepthTextures.h" #include "Sonicteam/RenderAction/SetFovY.h" #include "Sonicteam/RenderAction/SetFrameBufferObject.h" #include "Sonicteam/RenderAction/SetReflectionTextures.h" #include "Sonicteam/RenderAction/SetScissorRect.h" #include "Sonicteam/RenderAction/SetScissorTest.h" #include "Sonicteam/RenderAction/SetScreen.h" #include "Sonicteam/RenderAction/SetShaderGPRAllocation.h" #include "Sonicteam/RenderAction/SetStencilEnable.h" #include "Sonicteam/RenderAction/SetStencilFunc.h" #include "Sonicteam/RenderAction/SetStencilOp.h" #include "Sonicteam/RenderAction/SetStencilWriteMask.h" #include "Sonicteam/RenderAction/SetTexture.h" #include "Sonicteam/RenderAction/SetUserClipPlaneEnable.h" #include "Sonicteam/RenderAction/SetViewport.h" #include "Sonicteam/RenderAction/SetZMode.h" #include "Sonicteam/RenderPostprocess.h" #include "Sonicteam/SaveDataTask.h" #include "Sonicteam/SaveDataTaskXENON.h" #include "Sonicteam/SelectWindowTask.h" #include "Sonicteam/SoX/AI/StateMachine.h" #include "Sonicteam/SoX/ApplicationXenon.h" #include "Sonicteam/SoX/Audio/Cue.h" #include "Sonicteam/SoX/Audio/IAudioEngine.h" #include "Sonicteam/SoX/Audio/Player.h" #include "Sonicteam/SoX/Audio/PlayerImpl.h" #include "Sonicteam/SoX/Component.h" #include "Sonicteam/SoX/Engine/Application.h" #include "Sonicteam/SoX/Engine/Doc.h" #include "Sonicteam/SoX/Engine/DocMode.h" #include "Sonicteam/SoX/Engine/RenderProcess.h" #include "Sonicteam/SoX/Engine/Task.h" #include "Sonicteam/SoX/Graphics/Device.h" #include "Sonicteam/SoX/Graphics/Frame.h" #include "Sonicteam/SoX/Graphics/FrameGP.h" #include "Sonicteam/SoX/Graphics/FrameObserver.h" #include "Sonicteam/SoX/Graphics/Technique.h" #include "Sonicteam/SoX/Graphics/TechniqueFXL.h" #include "Sonicteam/SoX/Graphics/Transforms.h" #include "Sonicteam/SoX/Graphics/Vertex.h" #include "Sonicteam/SoX/Graphics/Xenon/DeviceXenon.h" #include "Sonicteam/SoX/Graphics/Xenon/TextureXenon.h" #include "Sonicteam/SoX/IResource.h" #include "Sonicteam/SoX/IResource2.h" #include "Sonicteam/SoX/IResourceMgr.h" #include "Sonicteam/SoX/Input/Manager.h" #include "Sonicteam/SoX/LinkNode.h" #include "Sonicteam/SoX/Math/Matrix.h" #include "Sonicteam/SoX/Math/Quaternion.h" #include "Sonicteam/SoX/Math/Vector.h" #include "Sonicteam/SoX/Message.h" #include "Sonicteam/SoX/MessageReceiver.h" #include "Sonicteam/SoX/Object.h" #include "Sonicteam/SoX/Physics/Entity.h" #include "Sonicteam/SoX/Physics/Havok/EntityHavok.h" #include "Sonicteam/SoX/Physics/Havok/EntityHavokImp.h" #include "Sonicteam/SoX/Physics/Havok/PhantomHavok.h" #include "Sonicteam/SoX/Physics/Havok/WorldHavok.h" #include "Sonicteam/SoX/Physics/IntersectEvent.h" #include "Sonicteam/SoX/Physics/IntersectListener.h" #include "Sonicteam/SoX/Physics/Phantom.h" #include "Sonicteam/SoX/Physics/PhantomListener.h" #include "Sonicteam/SoX/Physics/Shape.h" #include "Sonicteam/SoX/Physics/ShapeCastEvent.h" #include "Sonicteam/SoX/Physics/ShapeCastListener.h" #include "Sonicteam/SoX/Physics/World.h" #include "Sonicteam/SoX/RefCountObject.h" #include "Sonicteam/SoX/RefSharedPointer.h" #include "Sonicteam/SoX/Scenery/Camera.h" #include "Sonicteam/SoX/Scenery/CameraEventCallback.h" #include "Sonicteam/SoX/Scenery/CameraImp.h" #include "Sonicteam/SoX/Scenery/Drawable.h" #include "Sonicteam/SoX/Thread.h" #include "Sonicteam/StdImageFilters/BurnoutBlurFilter.h" #include "Sonicteam/StdImageFilters/SingleTechniqueFilter.h" #include "Sonicteam/System/CreateStatic.h" #include "Sonicteam/System/Diagnostics/Performance.h" #include "Sonicteam/System/Singleton.h" #include "Sonicteam/TextBook.h" #include "Sonicteam/TextBookMgr.h" #include "Sonicteam/TextCard.h" #include "Sonicteam/TextEntity.h" #include "Sonicteam/TextFontPicture.h" #include "Sonicteam/TextFontPictureMgr.h" #include "Sonicteam/TitleTask.h" #include "Sonicteam/VehicleMissileCtrl.h" #include "Sonicteam/VehicleMissileCtrlAutomatic.h" #include "Sonicteam/VehicleMissileCtrlSingle.h" #include "Sonicteam/WorldRenderProcess.h" #include "Sonicteam/sonicXmaPlayer.h" #include "boost/smart_ptr/make_shared_object.h" #include "boost/smart_ptr/shared_ptr.h" #include "d3dxb.h" #include "hk330/hkArray.h" #include "hk330/hkReferencedObject.h" #include "hk330/hkpBroadPhaseHandle.h" #include "hk330/hkpCdBody.h" #include "hk330/hkpCollidable.h" #include "hk330/hkpCollidableCollidableFilter.h" #include "hk330/hkpCollisionFilter.h" #include "hk330/hkpEntity.h" #include "hk330/hkpLinkedCollidable.h" #include "hk330/hkpPhantom.h" #include "hk330/hkpProperty.h" #include "hk330/hkpRayCollidableFilter.h" #include "hk330/hkpRayShapeCollectionFilter.h" #include "hk330/hkpRigidBody.h" #include "hk330/hkpShape.h" #include "hk330/hkpShapeCollectionFilter.h" #include "hk330/hkpTypedBroadPhaseHandle.h" #include "hk330/hkpWorld.h" #include "hk330/hkpWorldObject.h" #include "stdx/string.h" #include "stdx/vector.h" #include "stdx/wstring.h" ================================================ FILE: MarathonRecomp/api/Marathon.inl ================================================ #pragma once #include #include constexpr float RAD2DEGf = 57.2958f; constexpr float DEG2RADf = 0.0174533f; constexpr double RAD2DEG = 57.29578018188477; constexpr double DEG2RAD = 0.01745329238474369; #define MARATHON_CONCAT2(x, y) x##y #define MARATHON_CONCAT(x, y) MARATHON_CONCAT2(x, y) #define MARATHON_INSERT_PADDING(length) \ uint8_t MARATHON_CONCAT(pad, __LINE__)[length] #define MARATHON_ASSERT_OFFSETOF(type, field, offset) \ static_assert(offsetof(type, field) == offset) #define MARATHON_ASSERT_SIZEOF(type, size) \ static_assert(sizeof(type) == size) #define MARATHON_VIRTUAL_FUNCTION(returnType, virtualIndex, ...) \ GuestToHostFunction(*(be*)(g_memory.Translate(*(be*)(this) + (4 * virtualIndex))), __VA_ARGS__) struct marathon_null_ctor {}; inline std::vector ParseTextVariables(const char* pVariables) { std::vector result{}; if (!pVariables) return result; auto start = pVariables; auto ptr = pVariables; auto depth = 0; while (*ptr) { if (*ptr == '(') { depth++; } else if (*ptr == ')') { depth--; } else if (*ptr == ',' && !depth) { result.emplace_back(start, ptr - start); start = ptr + 1; } ++ptr; } if (ptr != start) result.emplace_back(start, ptr - start); return result; } inline std::vector> MapTextVariables(const char* pVariables) { std::vector> result{}; if (!pVariables) return result; auto variables = ParseTextVariables(pVariables); for (auto& variable : variables) { auto open = variable.find('('); auto close = variable.find(')'); if (open != std::string_view::npos && close != std::string_view::npos && close > open) { auto type = variable.substr(0, open); auto value = variable.substr(open + 1, close - open - 1); result.emplace_back(type, value); } else { result.emplace_back(variable, std::string_view{}); } } return result; } inline size_t strlenU16(const uint16_t* str) { size_t result = 0; uint16_t c = 0xFFFF; while (c != 0) { c = str[result]; result++; } return result; } inline bool strcmpU16(const uint16_t* a, const uint16_t* b, bool endianSwapA = false, bool endianSwapB = false) { for (size_t i = 0; i < strlenU16(a); i += 2) { auto c1 = endianSwapA ? ByteSwap(a[i]) : a[i]; auto c2 = endianSwapB ? ByteSwap(b[i]) : b[i]; if (c1 != 0 && c2 == 0) return false; if (c1 == 0 && c2 != 0) return false; if (c1 != c2) return false; } return true; } inline void printU16(const uint16_t* str, bool endianSwap = false) { for (size_t i = 0; i < strlenU16(str); i++) { auto c0 = endianSwap ? ByteSwap(str[i]) >> 8 : str[i] >> 8; auto c1 = endianSwap ? ByteSwap(str[i]) & 0xFF : str[i] & 0xFF; printf("%c%c", c0, c1); } printf("\n"); } ================================================ FILE: MarathonRecomp/api/README.md ================================================ # Marathon ## Contribution Guide ### Naming Conventions - Use `camelCase` for local variables, `SNAKE_CASE` for preprocessor macros, and `PascalCase` for everything else. Marathon-specific types that don't exist in the game should use `snake_case` for better differentiation. - Class members should be prefixed with `m_`, e.g., `m_Time`. Do not use this prefix for struct members. - Pointers should be prefixed with `p`, e.g., `pSonicContext`. - Shared pointers should be prefixed with `sp`, e.g., `spDatabase`. - References should be prefixed with `r`, e.g., `rMessage`. - Static members outside a class should be prefixed with `g_`, e.g., `g_AllocationTracker`. - Marathon-specific preprocessor macros should start with `MARATHON_`, e.g., `MARATHON_INSERT_PADDING`. - Function pointers should be prefixed with `fp`, e.g., `fpCGameObjectConstructor`. Combine prefixes as necessary, e.g., `m_sp` for a shared pointer as a class member. ### Coding Style - Always place curly brackets on a new line. - Prefer forward declaring types over including their respective headers. - Use <> includes relative to the project's root directory path. - Use C++17's nested namespace feature instead of defining multiple namespaces on separate lines. - Enum classes are prohibited as they were not available when the game was developed. - Avoid placing function definitions in .h files, instead, implement functions in the header's respective .inl file, similar to a .cpp file. - Ensure that all class members are declared as public. Even if you suspect that a class member was private in the original code, having it public is more convenient in a modding API. - Avoid placing multiple class definitions in a single header file unless you have a good reason to do so. - Keep function pointers or addresses outside functions, define them as global variables in the corresponding .inl file. Mark these global variables as `inline` and never nest them within class definitions. You do not need to use the `g_` prefix for function pointers, `fp` is sufficient. - Use primitive types defined in `cstdint` instead of using types that come with the language, e.g., use `uint32_t` instead of `unsigned int`. Using `float`, `double` and `bool` is okay. ### Mapping Rules - Always include the corresponding `offsetof`/`sizeof` assertions for mapped classes/structs. If you are uncertain about the type's size, you can omit the `sizeof` assertion. - Use the exact type name from the game if it's available through RTTI, otherwise, you can look for shared pointers that may reveal the original type name. - If you are unsure about the name of a class/struct member, use `Field` followed by the hexadecimal byte offset (e.g., `m_Field194`). Avoid names like `m_StoresThisThingMaybe`, you can write comments next to the definition for speculations. - If a portion of the byte range is irrelevant to your research or not mapped yet, use the `MARATHON_INSERT_PADDING` macro to align class/struct members correctly. - When the class has a virtual function table, if you don't want to map every function in it, you can map only the virtual destructor. - The original file locations are likely available in the executable file as assertion file paths. If you cannot find the file path, use your intuition to place the file in a sensible place. ================================================ FILE: MarathonRecomp/api/Sonicteam/Actor.h ================================================ #pragma once #include #include namespace Sonicteam { class Actor : public SoX::Engine::Task { public: MARATHON_INSERT_PADDING(8); // boost::weak_ptr GameImp; be m_ActorID; }; MARATHON_ASSERT_OFFSETOF(Actor, m_ActorID, 0x54); MARATHON_ASSERT_SIZEOF(Actor, 0x58); } ================================================ FILE: MarathonRecomp/api/Sonicteam/ActorManager.h ================================================ #pragma once #include #include namespace Sonicteam { class ActorManager { public: be m_aActorIDs[0xFFFF]; xpointer m_aActors[0xFFFF]; be m_LastActorID; be m_LastActorIndex; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(ActorManager, m_aActorIDs, 0x00); MARATHON_ASSERT_OFFSETOF(ActorManager, m_aActors, 0x3FFFC); MARATHON_ASSERT_OFFSETOF(ActorManager, m_LastActorID, 0x7FFF8); MARATHON_ASSERT_OFFSETOF(ActorManager, m_LastActorIndex, 0x7FFFC); MARATHON_ASSERT_SIZEOF(ActorManager, 0x80004); } ================================================ FILE: MarathonRecomp/api/Sonicteam/AlertWindowTask.h ================================================ #pragma once #include #include #include namespace Sonicteam { class AlertWindowTask : public SoX::Engine::Task { public: be m_HasSelectWindow; MARATHON_INSERT_PADDING(0x2C); be m_Operation; MARATHON_INSERT_PADDING(0x14); boost::anonymous_shared_ptr m_aspOptions[3]; MARATHON_INSERT_PADDING(4); be m_OptionCount; xpointer m_pSelectWindowTask; }; MARATHON_ASSERT_OFFSETOF(AlertWindowTask, m_HasSelectWindow, 0x4C); MARATHON_ASSERT_OFFSETOF(AlertWindowTask, m_Operation, 0x7C); MARATHON_ASSERT_OFFSETOF(AlertWindowTask, m_aspOptions, 0x94); MARATHON_ASSERT_OFFSETOF(AlertWindowTask, m_OptionCount, 0xB0); MARATHON_ASSERT_OFFSETOF(AlertWindowTask, m_pSelectWindowTask, 0xB4); MARATHON_ASSERT_SIZEOF(AlertWindowTask, 0xB8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/AppMarathon.h ================================================ #pragma once #include #include #include #include namespace Sonicteam { class AppMarathon : public SoX::ApplicationXenon { public: xpointer m_pDoc; static AppMarathon* GetInstance(); GameImp* GetGame() const { if (auto pGameMode = m_pDoc->GetDocMode()) return pGameMode->GetGame(); return nullptr; } }; MARATHON_ASSERT_OFFSETOF(AppMarathon, m_pDoc, 0x180); } #include ================================================ FILE: MarathonRecomp/api/Sonicteam/AppMarathon.inl ================================================ namespace Sonicteam { inline AppMarathon* AppMarathon::GetInstance() { return *(xpointer*)MmGetHostAddress(0x82D3B348); } } ================================================ FILE: MarathonRecomp/api/Sonicteam/AudioEngineXenon.h ================================================ #pragma once #include #include #include #include #include namespace Sonicteam { class AudioEngineXenon : public SoX::Audio::IAudioEngine, public System::Singleton> { public: MARATHON_INSERT_PADDING(8); be m_MusicVolume; be m_EffectsVolume; MARATHON_INSERT_PADDING(0x18); }; MARATHON_ASSERT_OFFSETOF(AudioEngineXenon, m_MusicVolume, 0x0C); MARATHON_ASSERT_OFFSETOF(AudioEngineXenon, m_EffectsVolume, 0x10); MARATHON_ASSERT_SIZEOF(AudioEngineXenon, 0x2C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/ButtonWindowTask.h ================================================ #pragma once #include #include namespace Sonicteam { class ButtonWindowTask : public SoX::Engine::Task { public: xpointer m_pHUDButtonWindow; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(ButtonWindowTask, m_pHUDButtonWindow, 0x4C); MARATHON_ASSERT_SIZEOF(ButtonWindowTask, 0x58); } ================================================ FILE: MarathonRecomp/api/Sonicteam/CObjBalloonIconDrawable.h ================================================ #pragma once #include #include #include namespace Sonicteam { class CObjBalloonIconDrawable : public SoX::Scenery::Drawable { public: xpointer m_pMyGraphicsDevice; MARATHON_INSERT_PADDING(0x24); SoX::Graphics::Vertex m_aVertices[4]; // BL, TL, BR, TR MARATHON_INSERT_PADDING(0x18); }; MARATHON_ASSERT_OFFSETOF(CObjBalloonIconDrawable, m_pMyGraphicsDevice, 0x70); MARATHON_ASSERT_OFFSETOF(CObjBalloonIconDrawable, m_aVertices, 0x98); MARATHON_ASSERT_SIZEOF(CObjBalloonIconDrawable, 0x1A0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Camera/CameraMode.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::Camera { class CameraMode : public SoX::MessageReceiver { public: boost::shared_ptr m_spGame; xpointer m_pCameraInputListener; MARATHON_INSERT_PADDING(0x18); GameImp* GetGame() const { return (GameImp*)m_spGame.get(); } }; MARATHON_ASSERT_OFFSETOF(CameraMode, m_spGame, 0x04); MARATHON_ASSERT_OFFSETOF(CameraMode, m_pCameraInputListener, 0x0C); MARATHON_ASSERT_SIZEOF(CameraMode, 0x28); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Camera/CameraModeManager.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::Camera { class CameraModeManager { public: // TODO: research these fields (processed by 0x8218C100). struct UnknownStruct { MARATHON_INSERT_PADDING(0x14); }; xpointer m_pCameraman; boost::shared_ptr m_spCameraMode; MARATHON_INSERT_PADDING(8); stdx::vector m_vUnkStructs; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(CameraModeManager, m_pCameraman, 0x00); MARATHON_ASSERT_OFFSETOF(CameraModeManager, m_spCameraMode, 0x04); MARATHON_ASSERT_OFFSETOF(CameraModeManager, m_vUnkStructs, 0x14); MARATHON_ASSERT_SIZEOF(CameraModeManager, 0x28); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Camera/Cameraman.h ================================================ #pragma once #include #include namespace Sonicteam::Camera { class CameraModeManager; class Cameraman : public Actor { public: MARATHON_INSERT_PADDING(0x24); boost::shared_ptr m_spCameraModeManager; MARATHON_INSERT_PADDING(0x2C); be m_FOV; MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_OFFSETOF(Cameraman, m_spCameraModeManager, 0x7C); MARATHON_ASSERT_OFFSETOF(Cameraman, m_FOV, 0xB0); MARATHON_ASSERT_SIZEOF(Cameraman, 0xC0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Camera/SonicCamera.h ================================================ #pragma once #include namespace Sonicteam::Camera { class SonicCamera : public CameraMode { public: MARATHON_INSERT_PADDING(4); xpointer m_pDoc; MARATHON_INSERT_PADDING(0x14); be m_SpringK; be m_DampingK; be m_AzDamping; be m_AltDamping; be m_AzDriveK; be m_AzDampingK; be m_AltDriveK; be m_AltDampingK; MARATHON_INSERT_PADDING(0x3C); be m_FovY; MARATHON_INSERT_PADDING(0x3C); be m_Distance; MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_OFFSETOF(SonicCamera, m_pDoc, 0x2C); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_SpringK, 0x44); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_DampingK, 0x48); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_AzDamping, 0x4C); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_AltDamping, 0x50); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_AzDriveK, 0x54); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_AzDampingK, 0x58); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_AltDriveK, 0x5C); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_AltDampingK, 0x60); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_FovY, 0xA0); MARATHON_ASSERT_OFFSETOF(SonicCamera, m_Distance, 0xE0); MARATHON_ASSERT_SIZEOF(SonicCamera, 0xF0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/CommonObjectHint.h ================================================ #pragma once #include #include namespace Sonicteam { class CommonObjectHint : public PropFixture { public: enum CommonObjectHintType : uint32_t { CommonObjectHintType_HintRing, CommonObjectHintType_HintVolume }; MARATHON_INSERT_PADDING(4); char m_MessageName[20]; MARATHON_INSERT_PADDING(0x40); be m_Type; bool m_IsFastAnim; bool m_IsHitAnim; MARATHON_INSERT_PADDING(2); be m_HitTime; MARATHON_INSERT_PADDING(0x10); }; MARATHON_ASSERT_OFFSETOF(CommonObjectHint, m_MessageName, 0x184); MARATHON_ASSERT_OFFSETOF(CommonObjectHint, m_Type, 0x1D8); MARATHON_ASSERT_OFFSETOF(CommonObjectHint, m_IsFastAnim, 0x1DC); MARATHON_ASSERT_OFFSETOF(CommonObjectHint, m_IsHitAnim, 0x1DD); MARATHON_ASSERT_OFFSETOF(CommonObjectHint, m_HitTime, 0x1E0); MARATHON_ASSERT_SIZEOF(CommonObjectHint, 0x200); } ================================================ FILE: MarathonRecomp/api/Sonicteam/CsdLink.h ================================================ #pragma once #include namespace Sonicteam { class CsdLink { public: struct Vftable { be fpDestroy; MARATHON_INSERT_PADDING(4); be fpUpdate; }; xpointer m_pVftable; xpointer m_pNext; xpointer m_pPrevious; be m_Priority; MARATHON_INSERT_PADDING(8); void* Destroy(uint32_t flags = 1) { return GuestToHostFunction(m_pVftable->fpDestroy.get(), this, flags); } int Update(double deltaTime = 0.0) { return GuestToHostFunction(m_pVftable->fpUpdate.get(), this, deltaTime); } }; MARATHON_ASSERT_OFFSETOF(CsdLink, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(CsdLink, m_pNext, 0x04); MARATHON_ASSERT_OFFSETOF(CsdLink, m_pPrevious, 0x08); MARATHON_ASSERT_OFFSETOF(CsdLink, m_Priority, 0x0C); MARATHON_ASSERT_SIZEOF(CsdLink, 0x18); } ================================================ FILE: MarathonRecomp/api/Sonicteam/CsdManager.h ================================================ #pragma once #include #include #include #include namespace Sonicteam { class CsdManager : public SoX::IResourceMgr, public System::Singleton> { public: MARATHON_INSERT_PADDING(0x1C); }; MARATHON_ASSERT_SIZEOF(CsdManager, 0x2C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/CsdObject.h ================================================ #pragma once #include #include #include #include namespace Sonicteam { class CsdObject : public SoX::RefCountObject, public CsdLink { public: MARATHON_INSERT_PADDING(4); xpointer m_pCsdProject; xpointer m_pCsdResource; }; MARATHON_ASSERT_OFFSETOF(CsdObject, m_pCsdProject, 0x24); MARATHON_ASSERT_OFFSETOF(CsdObject, m_pCsdResource, 0x28); MARATHON_ASSERT_SIZEOF(CsdObject, 0x2C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/CsdResource.h ================================================ #pragma once #include #include #include namespace Sonicteam { class CsdResource : public SoX::IResource2 { public: MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_SIZEOF(CsdResource, 0x68); } ================================================ FILE: MarathonRecomp/api/Sonicteam/DocMarathonImp.h ================================================ #pragma once #include #include #include #include #include namespace Sonicteam { class DocMarathonImp : public SoX::Engine::Doc { public: MARATHON_INSERT_PADDING(0x40); stdx::vector> m_vspInputManager; MARATHON_INSERT_PADDING(0x24); bool m_VFrame; MARATHON_INSERT_PADDING(0x55B58); be m_aPadIDs[4]; MARATHON_INSERT_PADDING(0x2C); }; MARATHON_ASSERT_OFFSETOF(DocMarathonImp, m_vspInputManager, 0x9C); MARATHON_ASSERT_OFFSETOF(DocMarathonImp, m_VFrame, 0xD0); MARATHON_ASSERT_OFFSETOF(DocMarathonImp, m_aPadIDs, 0x55C2C); MARATHON_ASSERT_SIZEOF(DocMarathonImp, 0x55C68); } ================================================ FILE: MarathonRecomp/api/Sonicteam/DocMarathonState.h ================================================ #pragma once #include #include namespace Sonicteam { class DocMarathonState : public DocMarathonImp {}; MARATHON_ASSERT_SIZEOF(DocMarathonState, 0x55C68); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Enemy/EnemyShot.h ================================================ #pragma once #include namespace Sonicteam::Enemy { class EnemyShot : public SoX::Engine::Task { public: MARATHON_INSERT_PADDING(0x1C); }; MARATHON_ASSERT_SIZEOF(EnemyShot, 0x68); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Enemy/EnemyShotNormal.h ================================================ #pragma once #include #include namespace Sonicteam::Enemy { class EnemyShotNormal : public EnemyShotPoint {}; MARATHON_ASSERT_SIZEOF(EnemyShotNormal, 0x160); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Enemy/EnemyShotPoint.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::Enemy { class EnemyShotPoint : public EnemyShot { public: MARATHON_INSERT_PADDING(0x20); SoX::RefSharedPointer m_spPhantom; MARATHON_INSERT_PADDING(0xD4); }; MARATHON_ASSERT_OFFSETOF(EnemyShotPoint, m_spPhantom, 0x88); MARATHON_ASSERT_SIZEOF(EnemyShotPoint, 0x160); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Fixture.h ================================================ #pragma once #include #include #include #include namespace Sonicteam { class Fixture : public Actor { public: SoX::Math::Quaternion m_Rotation; SoX::Math::Vector m_Position; MARATHON_INSERT_PADDING(0xF0); }; MARATHON_ASSERT_OFFSETOF(Fixture, m_Rotation, 0x60); MARATHON_ASSERT_OFFSETOF(Fixture, m_Position, 0x70); MARATHON_ASSERT_SIZEOF(Fixture, 0x170); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Game.h ================================================ #pragma once #include namespace Sonicteam { class Game : public SoX::MessageReceiver {}; MARATHON_ASSERT_SIZEOF(Game, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/GameImp.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace Sonicteam { class ActorManager; class GameScript; class GameImp : public Game { public: enum GameState : uint32_t { GameState_MainMenu, GameState_Stage, GameState_Event, GameState_Movie, GameState_Result, GameState_Message, GameState_6, GameState_Save, GameState_ReturnToMainMenu }; enum GameFlags : uint32_t { GameFlags_RestartArea1 = 1, GameFlags_RestartArea2 = 0x200, GameFlags_IsPaused = 0x1000, GameFlags_LoadArea1 = 0x40000, GameFlags_LoadArea2 = 0x200000 }; struct PlayerData { be ActorID; be RingCount; MARATHON_INSERT_PADDING(4); be LifeCount; be ScoreCount; be AliveTime; be Time; MARATHON_INSERT_PADDING(4); be SectionTime; be GaugeValue; be MaturityLevel; be MaturityValue; MARATHON_INSERT_PADDING(4); be ExtendRingCount; be GemIndex; MARATHON_INSERT_PADDING(0x10); }; MARATHON_INSERT_PADDING(4); be m_State; xpointer m_pDoc; be m_Flags; MARATHON_INSERT_PADDING(0xE2C); PlayerData m_PlayerData[4]; MARATHON_INSERT_PADDING(0x200); bool m_IsStage; MARATHON_INSERT_PADDING(0x0C); be m_Field1180; xpointer m_pGameScript; be m_aObjPlayerActorID[0x0F]; boost::shared_ptr m_spActorManager; xpointer m_pSystemTextBook; MARATHON_INSERT_PADDING(8); stdx::vector>> m_vvspCameras; MARATHON_INSERT_PADDING(0x1B4); xpointer m_pBgmCue; MARATHON_INSERT_PADDING(0x36C); xpointer m_pHintTextBook; MARATHON_INSERT_PADDING(4); xpointer m_pMissionCore; MARATHON_INSERT_PADDING(0x2A4); SoX::RefSharedPointer m_spPhysicsWorld; xpointer m_pMyCollisionFilter; MARATHON_INSERT_PADDING(0x0C); int PlayerActorIDToIndex(int32_t actorId) const { for (int i = 0; i < 4; i++) { if (m_PlayerData[i].ActorID == actorId) return i; } return -1; } SoX::Scenery::CameraImp* GetCamera(const char* pName, int which = 0) { if (m_vvspCameras.empty()) return nullptr; for (auto& spCamera : m_vvspCameras[which]) { auto pCameraImp = (SoX::Scenery::CameraImp*)spCamera.get(); if (pCameraImp->m_Name == pName) return pCameraImp; } return nullptr; } template T* GetBgmCue() const { return (T*)m_pBgmCue.get(); } template T* GetPhysicsWorld() const { return (T*)m_spPhysicsWorld.get(); } }; MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, ActorID, 0x00); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, RingCount, 0x04); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, LifeCount, 0x0C); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, ScoreCount, 0x10); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, AliveTime, 0x14); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, Time, 0x18); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, SectionTime, 0x20); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, GaugeValue, 0x24); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, MaturityLevel, 0x28); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, MaturityValue, 0x2C); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, ExtendRingCount, 0x34); MARATHON_ASSERT_OFFSETOF(GameImp::PlayerData, GemIndex, 0x38); MARATHON_ASSERT_SIZEOF(GameImp::PlayerData, 0x4C); MARATHON_ASSERT_OFFSETOF(GameImp, m_State, 0x08); MARATHON_ASSERT_OFFSETOF(GameImp, m_pDoc, 0x0C); MARATHON_ASSERT_OFFSETOF(GameImp, m_Flags, 0x10); MARATHON_ASSERT_OFFSETOF(GameImp, m_PlayerData, 0xE40); MARATHON_ASSERT_OFFSETOF(GameImp, m_IsStage, 0x1170); MARATHON_ASSERT_OFFSETOF(GameImp, m_Field1180, 0x1180); MARATHON_ASSERT_OFFSETOF(GameImp, m_pGameScript, 0x1184); MARATHON_ASSERT_OFFSETOF(GameImp, m_aObjPlayerActorID, 0x1188); MARATHON_ASSERT_OFFSETOF(GameImp, m_spActorManager, 0x11C4); MARATHON_ASSERT_OFFSETOF(GameImp, m_pSystemTextBook, 0x11CC); MARATHON_ASSERT_OFFSETOF(GameImp, m_vvspCameras, 0x11D8); MARATHON_ASSERT_OFFSETOF(GameImp, m_pBgmCue, 0x139C); MARATHON_ASSERT_OFFSETOF(GameImp, m_pHintTextBook, 0x170C); MARATHON_ASSERT_OFFSETOF(GameImp, m_pMissionCore, 0x1714); MARATHON_ASSERT_OFFSETOF(GameImp, m_spPhysicsWorld, 0x19BC); MARATHON_ASSERT_OFFSETOF(GameImp, m_pMyCollisionFilter, 0x19C0); MARATHON_ASSERT_SIZEOF(GameImp, 0x19D0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/GameMode.h ================================================ #pragma once #include #include namespace Sonicteam { class GameMode : public SoX::Engine::DocMode { public: MARATHON_INSERT_PADDING(0x1C); xpointer m_pGame; MARATHON_INSERT_PADDING(0x1C); GameImp* GetGame() const { return (GameImp*)m_pGame.get(); } }; MARATHON_ASSERT_OFFSETOF(GameMode, m_pGame, 0x6C); MARATHON_ASSERT_SIZEOF(GameMode, 0x8C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Globals.h ================================================ #pragma once #include namespace Sonicteam { enum Character : uint32_t { Character_Sonic, Character_Shadow, Character_Silver, Character_Tails, Character_Amy, Character_Knuckles, Character_Omega, Character_Rouge, Character_Blaze }; struct Globals { static inline be* ms_MainDisplayColours[9]; static void Init() { for (int i = 0; i < 9; i++) ms_MainDisplayColours[i] = (be*)MmGetHostAddress(0x82036BE4 + (i * 4)); } }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDButtonWindow.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Sonicteam { class HUDButtonWindow; class HUDButtonWindowNextObserver : public SoX::RefCountObject, public Chao::CSD::CSceneObserver { public: MARATHON_INSERT_PADDING(4); xpointer m_pHUDButtonWindow; MARATHON_INSERT_PADDING(4); }; class HUDButtonWindow : public SoX::RefCountObject, public SoX::Engine::Task { public: xpointer m_pTextBook; xpointer m_pCsdResource; HUDButtonWindowNextObserver m_NextObserver; be m_Field80; boost::shared_ptr m_spTextCard; boost::shared_ptr m_spTextEntity; xpointer m_pHudTextParts; be m_Field98; bool m_IsIntroAnimStarted; }; MARATHON_ASSERT_OFFSETOF(HUDButtonWindowNextObserver, m_pHUDButtonWindow, 0x1C); MARATHON_ASSERT_SIZEOF(HUDButtonWindowNextObserver, 0x24); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_pTextBook, 0x54); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_pCsdResource, 0x58); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_NextObserver, 0x5C); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_Field80, 0x80); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_spTextCard, 0x84); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_spTextEntity, 0x8C); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_pHudTextParts, 0x94); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_Field98, 0x98); MARATHON_ASSERT_OFFSETOF(HUDButtonWindow, m_IsIntroAnimStarted, 0x9C); MARATHON_ASSERT_SIZEOF(HUDButtonWindow, 0xA0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDCALLBACK.h ================================================ #pragma once namespace Sonicteam { class HUDCALLBACK { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(HUDCALLBACK, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(HUDCALLBACK, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDGoldMedal.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include namespace Sonicteam { class HUDGoldMedal : public SoX::RefCountObject, public SoX::Engine::Task { public: xpointer m_pCsdObject; xpointer m_pMedalCountText; xpointer m_pDoc; MARATHON_INSERT_PADDING(0x3C); be m_EpisodeIndex; be m_ScrollIndex; be m_MedalCount; boost::shared_ptr m_spMedalCountTextCard; MARATHON_INSERT_PADDING(0x10); boost::shared_ptr m_aspTextCards[5]; boost::shared_ptr m_aspTextEntities[5]; }; MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_pCsdObject, 0x54); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_pMedalCountText, 0x58); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_pDoc, 0x5C); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_EpisodeIndex, 0x9C); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_ScrollIndex, 0xA0); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_MedalCount, 0xA4); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_spMedalCountTextCard, 0xA8); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_aspTextCards, 0xC0); MARATHON_ASSERT_OFFSETOF(HUDGoldMedal, m_aspTextEntities, 0xE8); MARATHON_ASSERT_SIZEOF(HUDGoldMedal, 0x110); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDLimitTime.h ================================================ #pragma once #include namespace Sonicteam { class HUDLimitTime : public SoX::RefCountObject, public SoX::Engine::Task { public: static constexpr float ms_AlertThreshold = 10.0f; xpointer m_pCsdObject; be m_X; be m_Y; MARATHON_INSERT_PADDING(0x30); be m_Time; bool m_IsAboveAlertThreshold; }; MARATHON_ASSERT_OFFSETOF(HUDLimitTime, m_pCsdObject, 0x54); MARATHON_ASSERT_OFFSETOF(HUDLimitTime, m_X, 0x58); MARATHON_ASSERT_OFFSETOF(HUDLimitTime, m_Y, 0x5C); MARATHON_ASSERT_OFFSETOF(HUDLimitTime, m_Time, 0x90); MARATHON_ASSERT_OFFSETOF(HUDLimitTime, m_IsAboveAlertThreshold, 0x94); MARATHON_ASSERT_SIZEOF(HUDLimitTime, 0x98); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDLoading.h ================================================ #pragma once #include namespace Sonicteam { class HUDLoading : public SoX::RefCountObject, public SoX::Engine::Task { public: enum HUDLoadingFlags { HUDLoadingFlags_Finished = 6, HUDLoadingFlags_Open = 0x200, HUDLoadingFlags_End = 0x400 }; MARATHON_INSERT_PADDING(0x5C); be m_Flags; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(HUDLoading, m_Flags, 0xB0); MARATHON_ASSERT_SIZEOF(HUDLoading, 0xBC); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDMainDisplay.h ================================================ #pragma once #include namespace Sonicteam { class HUDMainDisplay : public SoX::RefCountObject, public SoX::Engine::Task { public: MARATHON_INSERT_PADDING(0x24); be m_Character; MARATHON_INSERT_PADDING(0x108); boost::shared_ptr m_Field184; boost::shared_ptr m_spTrickPointText; boost::shared_ptr m_Field194; MARATHON_INSERT_PADDING(0x10); boost::shared_ptr m_spSavePointTimeText; MARATHON_INSERT_PADDING(0x2C); }; MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Character, 0x78); MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Field184, 0x184); MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_spTrickPointText, 0x18C); MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_Field194, 0x194); MARATHON_ASSERT_OFFSETOF(HUDMainDisplay, m_spSavePointTimeText, 0x1AC); MARATHON_ASSERT_SIZEOF(HUDMainDisplay, 0x1E0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDMainMenu.h ================================================ #pragma once #include #include #include namespace Sonicteam { class HUDMainMenu : public SoX::RefCountObject, public SoX::Engine::Task { public: enum HUDMainMenuState : uint32_t { HUDMainMenuState_OptionsOutro = 5, HUDMainMenuState_SinglePlayerTextOutro = 8, HUDMainMenuState_SinglePlayerCursorIntro = 86, HUDMainMenuState_OptionsIntro = 90, HUDMainMenuState_MainCursorIntro = 98, HUDMainMenuState_MainCursorOutro = 99, HUDMainMenuState_GoldMedalResultsCursorOutro = 101 }; MARATHON_INSERT_PADDING(0x20); xpointer m_pCsdObject; MARATHON_INSERT_PADDING(0x1B0); be m_CursorFlags; MARATHON_INSERT_PADDING(0x32C); xpointer m_pHudTextRoot; MARATHON_INSERT_PADDING(0x48C); }; MARATHON_ASSERT_OFFSETOF(HUDMainMenu, m_pCsdObject, 0x74); MARATHON_ASSERT_OFFSETOF(HUDMainMenu, m_CursorFlags, 0x228); MARATHON_ASSERT_OFFSETOF(HUDMainMenu, m_pHudTextRoot, 0x558); MARATHON_ASSERT_SIZEOF(HUDMainMenu, 0x9E8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDMessageWindow.h ================================================ #pragma once #include #include namespace Sonicteam { class HUDMessageWindow : public SoX::RefCountObject, public SoX::Engine::Task { public: xpointer m_pCsdObject; MARATHON_INSERT_PADDING(0x1C); xpointer m_pParent; MARATHON_INSERT_PADDING(0x14); xpointer m_pHintWindowTask; }; MARATHON_ASSERT_OFFSETOF(HUDMessageWindow, m_pCsdObject, 0x54); MARATHON_ASSERT_OFFSETOF(HUDMessageWindow, m_pParent, 0x74); MARATHON_ASSERT_OFFSETOF(HUDMessageWindow, m_pHintWindowTask, 0x8C); MARATHON_ASSERT_SIZEOF(HUDMessageWindow, 0x90); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDOption.h ================================================ #pragma once #include #include namespace Sonicteam { class HUDOption : public SoX::RefCountObject, public SoX::Engine::Task { public: MARATHON_INSERT_PADDING(0x188); xpointer m_pHudTextRoot; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(HUDOption, m_pHudTextRoot, 0x1DC); MARATHON_ASSERT_SIZEOF(HUDOption, 0x1E8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDPopupScreen.h ================================================ #pragma once #include #include #include #include namespace Sonicteam { class HUDPopupScreen : public SoX::Engine::Task { public: enum HUDPopupScreenState : uint32_t { HUDPopupScreenState_Opening = 1, HUDPopupScreenState_Idle, HUDPopupScreenState_Closing }; xpointer m_pCsdObject; xpointer m_pMainTexture; xpointer m_pMaskTexture; xpointer m_pTechnique; stdx::string m_SceneName; stdx::string m_SpriteName; be m_State; MARATHON_INSERT_PADDING(1); bool m_IsClosing; MARATHON_INSERT_PADDING(2); be m_ClosingTime; be m_X; be m_Y; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_pCsdObject, 0x4C); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_pMainTexture, 0x50); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_pMaskTexture, 0x54); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_pTechnique, 0x58); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_SceneName, 0x5C); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_SpriteName, 0x78); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_State, 0x94); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_IsClosing, 0x99); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_ClosingTime, 0x9C); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_X, 0xA0); MARATHON_ASSERT_OFFSETOF(HUDPopupScreen, m_Y, 0xA4); MARATHON_ASSERT_SIZEOF(HUDPopupScreen, 0xB0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDRaderMap.h ================================================ #pragma once #include namespace Sonicteam { class HUDRaderMap : public HUDPopupScreen {}; MARATHON_ASSERT_SIZEOF(HUDRaderMap, 0xB0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HUDStageTitle.h ================================================ #pragma once #include namespace Sonicteam { class HUDStageTitle : public SoX::Engine::Task { public: MARATHON_INSERT_PADDING(8); xpointer m_pHudTextParts; }; MARATHON_ASSERT_OFFSETOF(HUDStageTitle, m_pHudTextParts, 0x54); MARATHON_ASSERT_SIZEOF(HUDStageTitle, 0x58); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HintWindowTask.h ================================================ #pragma once #include namespace Sonicteam { class HintWindowTask : public SoX::Engine::Task { public: MARATHON_INSERT_PADDING(0x40); boost::shared_ptr m_Field8C; boost::shared_ptr m_Field94; MARATHON_INSERT_PADDING(0x60); }; MARATHON_ASSERT_OFFSETOF(HintWindowTask, m_Field8C, 0x8C); MARATHON_ASSERT_OFFSETOF(HintWindowTask, m_Field94, 0x94); MARATHON_ASSERT_SIZEOF(HintWindowTask, 0xFC); } ================================================ FILE: MarathonRecomp/api/Sonicteam/HudTextParts.h ================================================ #pragma once #include #include #include #include #include #include namespace Sonicteam { class HudTextParts : public SoX::RefCountObject { public: xpointer m_pNext; xpointer m_pDoc; boost::shared_ptr m_spTextCard; boost::shared_ptr m_spTextEntity; xpointer m_pCsdObject; stdx::string m_SceneName; stdx::string m_CastName; be m_Priority; be m_OffsetX; be m_OffsetY; MARATHON_INSERT_PADDING(4); be m_AlignmentFlags; HudTextParts* Find(const char* pSceneName) { auto pRoot = this; while (pRoot) { if (pRoot->m_SceneName == pSceneName) return pRoot; pRoot = pRoot->m_pNext; } return nullptr; } HudTextParts* Find(const char* pSceneName, const char* pCastName) { auto pRoot = this; while (pRoot) { if (pRoot->m_SceneName == pSceneName && pRoot->m_CastName == pCastName) return pRoot; pRoot = pRoot->m_pNext; } return nullptr; } }; MARATHON_ASSERT_OFFSETOF(HudTextParts, m_pNext, 0x08); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_pDoc, 0x0C); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_spTextCard, 0x10); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_spTextEntity, 0x18); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_pCsdObject, 0x20); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_SceneName, 0x24); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_CastName, 0x40); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_Priority, 0x5C); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_OffsetX, 0x60); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_OffsetY, 0x64); MARATHON_ASSERT_OFFSETOF(HudTextParts, m_AlignmentFlags, 0x6C); MARATHON_ASSERT_SIZEOF(HudTextParts, 0x70); } ================================================ FILE: MarathonRecomp/api/Sonicteam/ImageFilter.h ================================================ #pragma once #include namespace Sonicteam { class ImageFilter { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(ImageFilter, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(ImageFilter, 8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MainDisplayTask.h ================================================ #pragma once #include namespace Sonicteam { class MainDisplayTask : public SoX::Engine::Task { public: xpointer m_pHUDMainDisplay; MARATHON_INSERT_PADDING(0x20); }; MARATHON_ASSERT_OFFSETOF(MainDisplayTask, m_pHUDMainDisplay, 0x4C); MARATHON_ASSERT_SIZEOF(MainDisplayTask, 0x70); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MainMenuExpositionTask.h ================================================ #pragma once #include #include namespace Sonicteam { class MainMenuExpositionTask : public SoX::RefCountObject, public SoX::Engine::Task { public: be m_TextMotionState; }; MARATHON_ASSERT_OFFSETOF(MainMenuExpositionTask, m_TextMotionState, 0x54); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MainMenuTask.h ================================================ #pragma once #include #include #include #include #include #include namespace Sonicteam { class MainMenuTask : public SoX::Engine::Task { public: enum MainMenuState : uint32_t { MainMenuState_MainMenuBack = 1, MainMenuState_MainMenu = 2, MainMenuState_ExitPrompt = 4, MainMenuState_SinglePlayer = 6, MainMenuState_EpisodeSelect = 9, MainMenuState_TrialSelect = 0x0D, MainMenuState_SelectCharacter = 0x0F, MainMenuState_ActTrial = 0x11, MainMenuState_TownTrial = 0x15, MainMenuState_Multiplayer = 0x17, MainMenuState_Extras = 0x1C, MainMenuState_Tag = 0x1E, MainMenuState_Tag1PSelect = 0x1F, MainMenuState_Battle = 0x22, MainMenuState_GoldMedalResultsOpen = 0x26, MainMenuState_GoldMedalResults = 0x27, MainMenuState_AudioRoom = 0x2F, MainMenuState_TheaterRoom = 0x31, MainMenuState_Options = 0x33, MainMenuState_ExitToStage = 0x3B, MainMenuState_ExitToTitle = 0x3C }; be m_State; MARATHON_INSERT_PADDING(0x24); xpointer m_pHUDMainMenu; MARATHON_INSERT_PADDING(8); xpointer m_pHUDGoldMedal; MARATHON_INSERT_PADDING(0x14); xpointer m_pButtonWindowTask; SoX::RefSharedPointer m_spMainMenuExpositionTask; be m_MainMenuSelectedIndex; be m_SinglePlayerSelectedIndex; MARATHON_INSERT_PADDING(0x14); be m_FieldBC; MARATHON_INSERT_PADDING(0x60); be m_GoldMedalEpisodeIndex; MARATHON_INSERT_PADDING(4); be m_GoldMedalScrollIndex; MARATHON_INSERT_PADDING(0x144); be m_IsChangingState; MARATHON_INSERT_PADDING(8); be m_PressedButtons; MARATHON_INSERT_PADDING(0x18); xpointer m_Field298; xpointer m_apSelectCharacters[9]; MARATHON_INSERT_PADDING(0x38); }; MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_State, 0x4C); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_pHUDMainMenu, 0x74); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_pHUDGoldMedal, 0x80); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_pButtonWindowTask, 0x98); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_spMainMenuExpositionTask, 0x9C); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_MainMenuSelectedIndex, 0xA0); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_SinglePlayerSelectedIndex, 0xA4); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_FieldBC, 0xBC); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_GoldMedalEpisodeIndex, 0x120); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_GoldMedalScrollIndex, 0x128); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_IsChangingState, 0x270); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_PressedButtons, 0x27C); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_Field298, 0x298); MARATHON_ASSERT_OFFSETOF(MainMenuTask, m_apSelectCharacters, 0x29C); MARATHON_ASSERT_SIZEOF(MainMenuTask, 0x2F8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MainMode.h ================================================ #pragma once #include #include #include namespace Sonicteam { class MainMode : public SoX::Engine::DocMode { public: MARATHON_INSERT_PADDING(0x24); boost::shared_ptr m_spSelectCamera; xpointer m_pFrameGP; MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_OFFSETOF(MainMode, m_spSelectCamera, 0x74); MARATHON_ASSERT_OFFSETOF(MainMode, m_pFrameGP, 0x7C); MARATHON_ASSERT_SIZEOF(MainMode, 0x8C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/Camera/Cameraman/MsgChangeMode.h ================================================ #pragma once #include #include #include namespace Sonicteam::Message::Camera::Cameraman { struct MsgChangeMode : SoX::Message<0x14007> { be PadID{}; be TargetActorID{}; bool IsDemoCamera{}; }; MARATHON_ASSERT_OFFSETOF(MsgChangeMode, PadID, 0x04); MARATHON_ASSERT_OFFSETOF(MsgChangeMode, TargetActorID, 0x08); MARATHON_ASSERT_OFFSETOF(MsgChangeMode, IsDemoCamera, 0x0C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/HUDButtonWindow/MsgChangeButtons.h ================================================ #pragma once #include #include namespace Sonicteam::Message::HUDButtonWindow { struct MsgChangeButtons : SoX::Message<0x1B05B> { be ButtonType{}; MsgChangeButtons(uint32_t buttonType = 0) : ButtonType(buttonType) {} }; MARATHON_ASSERT_OFFSETOF(MsgChangeButtons, ButtonType, 0x04); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/HUDGoldMedal/MsgChangeState.h ================================================ #pragma once #include #include namespace Sonicteam::Message::HUDGoldMedal { struct MsgChangeState : SoX::Message<0x1B058> { be State{}; be Field08{}; uint8_t Field0C{}; be EpisodeIndex{}; MsgChangeState() {} MsgChangeState(uint32_t state, uint32_t episodeIndex = 0) : State(state), EpisodeIndex(episodeIndex) {} }; MARATHON_ASSERT_OFFSETOF(MsgChangeState, State, 0x04); MARATHON_ASSERT_OFFSETOF(MsgChangeState, Field08, 0x08); MARATHON_ASSERT_OFFSETOF(MsgChangeState, Field0C, 0x0C); MARATHON_ASSERT_OFFSETOF(MsgChangeState, EpisodeIndex, 0x10); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/HUDMainMenu/MsgChangeState.h ================================================ #pragma once #include #include namespace Sonicteam::Message::HUDMainMenu { struct MsgChangeState : SoX::Message<0x1B053> { be State{}; }; MARATHON_ASSERT_OFFSETOF(MsgChangeState, State, 0x04); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/HUDMainMenu/MsgSetCursor.h ================================================ #pragma once #include #include namespace Sonicteam::Message::HUDMainMenu { struct MsgSetCursor : MsgChangeState { be CursorIndex{}; MsgSetCursor() {} MsgSetCursor(uint32_t state) { State = state; } MsgSetCursor(uint32_t state, uint32_t cursorIndex) { State = state; CursorIndex = cursorIndex; } }; MARATHON_ASSERT_OFFSETOF(MsgSetCursor, CursorIndex, 0x08); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/HUDMainMenu/MsgTransition.h ================================================ #pragma once #include #include namespace Sonicteam::Message::HUDMainMenu { struct MsgTransition : MsgChangeState { be Flags{}; MsgTransition() {} MsgTransition(uint32_t state) { State = state; } MsgTransition(uint32_t state, uint32_t flags) { State = state; Flags = flags; } }; MARATHON_ASSERT_OFFSETOF(MsgTransition, Flags, 0x08); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/Mission/MsgGetGlobalFlag.h ================================================ #pragma once #include namespace Sonicteam::Message::Mission { struct MsgGetGlobalFlag : SoX::Message<0x1E004> { be FlagID{}; be FlagValue{}; MsgGetGlobalFlag(uint32_t flagId) : FlagID(flagId) {} }; MARATHON_ASSERT_OFFSETOF(MsgGetGlobalFlag, FlagID, 0x04); MARATHON_ASSERT_OFFSETOF(MsgGetGlobalFlag, FlagValue, 0x08); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/ObjJump123/MsgGetNextPoint.h ================================================ #pragma once #include #include #include namespace Sonicteam::Message::ObjJump123 { struct MsgGetNextPoint : SoX::Message<0x10007> { SoX::Math::Quaternion Rotation{}; SoX::Math::Vector Position{}; }; MARATHON_ASSERT_OFFSETOF(MsgGetNextPoint, Rotation, 0x10); MARATHON_ASSERT_OFFSETOF(MsgGetNextPoint, Position, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/PauseAdapter/MsgGetText.h ================================================ #pragma once #include #include #include namespace Sonicteam::Message::PauseAdapter { struct MsgGetText : SoX::Message<0x1C003> { stdx::string PauseName{}; stdx::string SelectedName{}; }; MARATHON_ASSERT_OFFSETOF(MsgGetText, PauseName, 0x04); MARATHON_ASSERT_OFFSETOF(MsgGetText, SelectedName, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Message/Player/MsgSuckPlayer.h ================================================ #pragma once #include #include #include namespace Sonicteam::Message::Player { struct MsgSuckPlayer : SoX::Message<0x11034A> { SoX::Math::Vector Point{}; }; MARATHON_ASSERT_OFFSETOF(MsgSuckPlayer, Point, 0x10); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MessageWindowTask.h ================================================ #pragma once #include namespace Sonicteam { class MessageWindowTask : public SoX::Engine::Task { public: MARATHON_INSERT_PADDING(0x44); boost::shared_ptr m_Field90; boost::shared_ptr m_Field98; MARATHON_INSERT_PADDING(0x64); }; MARATHON_ASSERT_OFFSETOF(MessageWindowTask, m_Field90, 0x90); MARATHON_ASSERT_OFFSETOF(MessageWindowTask, m_Field98, 0x98); MARATHON_ASSERT_SIZEOF(MessageWindowTask, 0x104); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Mission/Core.h ================================================ #pragma once #include namespace Sonicteam::Mission { class Core : public SoX::MessageReceiver { public: MARATHON_INSERT_PADDING(0xC81C); }; MARATHON_ASSERT_SIZEOF(Core, 0xC820); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MovieObject.h ================================================ #pragma once #include #include namespace Sonicteam { class MovieObject : public SoX::RefCountObject { public: MARATHON_INSERT_PADDING(4); xpointer m_pMovieObjectWmv; stdx::string m_Language; xpointer m_pMyGraphicsDevice; MARATHON_INSERT_PADDING(8); be m_Width; be m_Height; bool m_Field40; }; MARATHON_ASSERT_OFFSETOF(MovieObject, m_pMovieObjectWmv, 0x0C); MARATHON_ASSERT_OFFSETOF(MovieObject, m_Language, 0x10); MARATHON_ASSERT_OFFSETOF(MovieObject, m_pMyGraphicsDevice, 0x2C); MARATHON_ASSERT_OFFSETOF(MovieObject, m_Width, 0x38); MARATHON_ASSERT_OFFSETOF(MovieObject, m_Height, 0x3C); MARATHON_ASSERT_OFFSETOF(MovieObject, m_Field40, 0x40); MARATHON_ASSERT_SIZEOF(MovieObject, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MovieObjectWmv.h ================================================ #pragma once #include #include #include #include namespace Sonicteam { class MovieObjectWmv : public SoX::RefCountObject { public: bool m_UseCustomDimensions; be m_Left; be m_Top; be m_Right; be m_Bottom; MARATHON_INSERT_PADDING(4); stdx::string m_FilePath; MARATHON_INSERT_PADDING(8); bool m_RenderMovie; MARATHON_INSERT_PADDING(0x38); xpointer m_pMyGraphicsDevice; be m_Field84; MARATHON_INSERT_PADDING(0x0C); xpointer m_apTexturesYUV[4 * 3]; MARATHON_INSERT_PADDING(8); be m_Width; be m_Height; MARATHON_INSERT_PADDING(0x0C); xpointer m_pTechnique; stdx::string m_Language; }; MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_UseCustomDimensions, 0x08); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Left, 0x0C); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Top, 0x10); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Right, 0x14); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Bottom, 0x18); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_FilePath, 0x20); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_RenderMovie, 0x44); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_pMyGraphicsDevice, 0x80); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Field84, 0x84); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_apTexturesYUV, 0x94); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Width, 0xCC); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Height, 0xD0); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_pTechnique, 0xE0); MARATHON_ASSERT_OFFSETOF(MovieObjectWmv, m_Language, 0xE4); MARATHON_ASSERT_SIZEOF(MovieObjectWmv, 0x100); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MovieTask.h ================================================ #pragma once #include namespace Sonicteam { class MovieTask : public SoX::Engine::Task, public CsdLink { public: MARATHON_INSERT_PADDING(0x40); xpointer m_pMovieObject; MARATHON_INSERT_PADDING(0x18); }; MARATHON_ASSERT_OFFSETOF(MovieTask, m_pMovieObject, 0xA4); MARATHON_ASSERT_SIZEOF(MovieTask, 0xC0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MyCue.h ================================================ #pragma once #include class MyCue : public Sonicteam::SoX::Audio::Cue {}; MARATHON_ASSERT_SIZEOF(MyCue, 0x2C); ================================================ FILE: MarathonRecomp/api/Sonicteam/MyCueAdx.h ================================================ #pragma once #include #include #include #include class MyCueAdx : public MyCue, public MyCueAttenuate { public: MARATHON_INSERT_PADDING(0x0C); be m_Field50; be m_Field54; xpointer m_pXmaPlayer; bool m_IsPaused; MARATHON_INSERT_PADDING(3); }; MARATHON_ASSERT_OFFSETOF(MyCueAdx, m_Field50, 0x50); MARATHON_ASSERT_OFFSETOF(MyCueAdx, m_Field54, 0x54); MARATHON_ASSERT_OFFSETOF(MyCueAdx, m_pXmaPlayer, 0x58); MARATHON_ASSERT_OFFSETOF(MyCueAdx, m_IsPaused, 0x5C); MARATHON_ASSERT_SIZEOF(MyCueAdx, 0x60); ================================================ FILE: MarathonRecomp/api/Sonicteam/MyCueAttenuate.h ================================================ #pragma once class MyCueAttenuate { public: be m_Time; be m_FadeOutRate; be m_FadeInRate; be m_MinVolume; be m_MaxVolume; bool m_IsFadeOut; MARATHON_INSERT_PADDING(3); }; MARATHON_ASSERT_OFFSETOF(MyCueAttenuate, m_Time, 0x00); MARATHON_ASSERT_OFFSETOF(MyCueAttenuate, m_FadeOutRate, 0x04); MARATHON_ASSERT_OFFSETOF(MyCueAttenuate, m_FadeInRate, 0x08); MARATHON_ASSERT_OFFSETOF(MyCueAttenuate, m_MinVolume, 0x0C); MARATHON_ASSERT_OFFSETOF(MyCueAttenuate, m_MaxVolume, 0x10); MARATHON_ASSERT_OFFSETOF(MyCueAttenuate, m_IsFadeOut, 0x14); MARATHON_ASSERT_SIZEOF(MyCueAttenuate, 0x18); ================================================ FILE: MarathonRecomp/api/Sonicteam/MyGraphicsDevice.h ================================================ #pragma once #include #include namespace Sonicteam { class MyGraphicsDevice : public SoX::Graphics::Xenon::DeviceXenon { public: MARATHON_INSERT_PADDING(0x1C0); }; MARATHON_ASSERT_SIZEOF(MyGraphicsDevice, 0x350); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MyPhantom.h ================================================ #pragma once #include #include namespace Sonicteam { class MyPhantom : public SoX::Physics::Havok::PhantomHavok {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/MyRenderProcess.h ================================================ #pragma once #include #include #include namespace Sonicteam { class MyRenderProcess : public SoX::Engine::RenderProcess { public: xpointer m_pDocMarathonState; xpointer m_pMyGraphicsDevice; }; MARATHON_ASSERT_SIZEOF(MyRenderProcess, 0x38); MARATHON_ASSERT_OFFSETOF(MyRenderProcess, m_pDocMarathonState, 0x30); MARATHON_ASSERT_OFFSETOF(MyRenderProcess, m_pMyGraphicsDevice, 0x34); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MyTexture.h ================================================ #pragma once #include #include namespace Sonicteam { class MyTexture : public SoX::Graphics::Xenon::TextureXenon { public: MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_SIZEOF(MyTexture, 0x90); } ================================================ FILE: MarathonRecomp/api/Sonicteam/MyTransforms.h ================================================ #pragma once #include #include namespace Sonicteam { class MyTransforms : public SoX::Graphics::Transforms { public: MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_SIZEOF(MyTransforms, 0xF8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/NamedActor.h ================================================ #pragma once #include #include #include namespace Sonicteam { class NamedActor : public Actor { public: stdx::string m_Name; }; MARATHON_ASSERT_OFFSETOF(NamedActor, m_Name, 0x58); MARATHON_ASSERT_SIZEOF(NamedActor, 0x74); } ================================================ FILE: MarathonRecomp/api/Sonicteam/NamedTask.h ================================================ #pragma once #include #include #include namespace Sonicteam { class NamedTask : public SoX::Engine::Task { public: stdx::string m_Name; }; MARATHON_ASSERT_OFFSETOF(NamedTask, m_Name, 0x4C); MARATHON_ASSERT_SIZEOF(NamedTask, 0x68); } ================================================ FILE: MarathonRecomp/api/Sonicteam/NoSyncThread.h ================================================ #pragma once #include #include namespace Sonicteam { class NoSyncThread : SoX::Thread { public: MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_SIZEOF(NoSyncThread, 0x4C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/ObjectVehicle.h ================================================ #pragma once #include namespace Sonicteam { class ObjectVehicle : public Actor { public: MARATHON_INSERT_PADDING(0x180); }; MARATHON_ASSERT_SIZEOF(ObjectVehicle, 0x1D8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/ObjectVehicleBike.h ================================================ #pragma once #include #include namespace Sonicteam { class ObjectVehicleBike : public ObjectVehicle { public: MARATHON_INSERT_PADDING(0xD8); xpointer m_pVehicleMissileCtrlAutomatic; MARATHON_INSERT_PADDING(0xAC); }; MARATHON_ASSERT_OFFSETOF(ObjectVehicleBike, m_pVehicleMissileCtrlAutomatic, 0x2B0); MARATHON_ASSERT_SIZEOF(ObjectVehicleBike, 0x360); } ================================================ FILE: MarathonRecomp/api/Sonicteam/PauseAdapter.h ================================================ #pragma once #include namespace Sonicteam { class PauseAdapter : public SoX::Engine::Task { public: xpointer m_pGame; be m_SelectedID; be m_Field54; MARATHON_INSERT_PADDING(0x20); GameImp* GetGame() const { return (GameImp*)m_pGame.get(); } }; MARATHON_ASSERT_OFFSETOF(PauseAdapter, m_pGame, 0x4C); MARATHON_ASSERT_OFFSETOF(PauseAdapter, m_SelectedID, 0x50); MARATHON_ASSERT_OFFSETOF(PauseAdapter, m_Field54, 0x54); MARATHON_ASSERT_SIZEOF(PauseAdapter, 0x78); } ================================================ FILE: MarathonRecomp/api/Sonicteam/PauseTask.h ================================================ #pragma once #include namespace Sonicteam { class PauseTask : public SoX::Engine::Task { public: enum PauseTaskState { PauseTaskState_Opened, PauseTaskState_Opening, PauseTaskState_Idle, PauseTaskState_ClosingToAction, PauseTaskState_Closing, PauseTaskState_Closed }; MARATHON_INSERT_PADDING(4); xpointer m_pHUDPause; MARATHON_INSERT_PADDING(0x18); be m_State; be m_Flags; be m_SelectedIndex; MARATHON_INSERT_PADDING(0x28); be m_Buttons; MARATHON_INSERT_PADDING(0x1E0); be m_ItemCount; MARATHON_INSERT_PADDING(0x12C); }; MARATHON_ASSERT_OFFSETOF(PauseTask, m_pHUDPause, 0x50); MARATHON_ASSERT_OFFSETOF(PauseTask, m_State, 0x6C); MARATHON_ASSERT_OFFSETOF(PauseTask, m_Flags, 0x70); MARATHON_ASSERT_OFFSETOF(PauseTask, m_SelectedIndex, 0x74); MARATHON_ASSERT_OFFSETOF(PauseTask, m_Buttons, 0xA0); MARATHON_ASSERT_OFFSETOF(PauseTask, m_ItemCount, 0x284); MARATHON_ASSERT_SIZEOF(PauseTask, 0x3B4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/GroundRayListener.h ================================================ #pragma once #include #include #include #include #include #include #include namespace Sonicteam::Player { class GroundRayListener : public ICollisionListenerTemplate { public: SoX::Math::Vector m_ContactPosition; SoX::Math::Vector m_ContactNormal; be m_RayDistance; be m_RayFlags; SoX::LinkRef m_ContactEntity; }; MARATHON_ASSERT_OFFSETOF(GroundRayListener, m_ContactPosition, 0x50); MARATHON_ASSERT_OFFSETOF(GroundRayListener, m_ContactNormal, 0x60); MARATHON_ASSERT_OFFSETOF(GroundRayListener, m_RayDistance, 0x70); MARATHON_ASSERT_OFFSETOF(GroundRayListener, m_RayFlags, 0x74); MARATHON_ASSERT_OFFSETOF(GroundRayListener, m_ContactEntity, 0x78); MARATHON_ASSERT_SIZEOF(GroundRayListener, 0x90); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/ICollisionListener.h ================================================ #pragma once #include namespace Sonicteam::Player { class ICollisionListener { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0x0C); be m_FlagsA; be m_FlagsB; be m_FlagsC; MARATHON_INSERT_PADDING(4); SoX::Math::Vector m_SurfaceNormal; }; MARATHON_ASSERT_OFFSETOF(ICollisionListener, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(ICollisionListener, m_FlagsA, 0x10); MARATHON_ASSERT_OFFSETOF(ICollisionListener, m_FlagsB, 0x14); MARATHON_ASSERT_OFFSETOF(ICollisionListener, m_FlagsC, 0x18); MARATHON_ASSERT_OFFSETOF(ICollisionListener, m_SurfaceNormal, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/ICollisionListenerTemplate.h ================================================ #pragma once #include #include namespace Sonicteam::Player { template class ICollisionListenerTemplate : public ICollisionListener, public TListener {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IDynamicLink.h ================================================ #pragma once #include namespace Sonicteam::Player { class IDynamicLink { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IDynamicLink, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IDynamicLink, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IEventerListener.h ================================================ #pragma once namespace Sonicteam::Player { class IEventerListener { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(IEventerListener, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IEventerListener, 0x0C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IExportExternalFlag.h ================================================ #pragma once #include namespace Sonicteam::Player { class IExportExternalFlag { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IExportExternalFlag, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IExportExternalFlag, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IExportPostureRequestFlag.h ================================================ #pragma once #include namespace Sonicteam::Player { class IExportPostureRequestFlag { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IExportPostureRequestFlag, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IExportPostureRequestFlag, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IExportWeaponRequestFlag.h ================================================ #pragma once #include namespace Sonicteam::Player { class IExportWeaponRequestFlag { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IExportWeaponRequestFlag, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IExportWeaponRequestFlag, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IFlagCommunicator.h ================================================ #pragma once #include namespace Sonicteam::Player { class IFlagCommunicator { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IFlagCommunicator, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IFlagCommunicator, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IGauge.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::Player { class IGauge : public IPlugIn, public IVariable, public IStepable {}; MARATHON_ASSERT_SIZEOF(IGauge, 0x28); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/INotification.h ================================================ #pragma once #include namespace Sonicteam::Player { class INotification { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(INotification, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(INotification, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPlugIn.h ================================================ #pragma once #include #include namespace Sonicteam::Player { class IPlugIn { public: xpointer m_pVftable; stdx::string m_Name; }; MARATHON_ASSERT_OFFSETOF(IPlugIn, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(IPlugIn, m_Name, 0x04); MARATHON_ASSERT_SIZEOF(IPlugIn, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPostureControl.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Sonicteam::Player { class IPostureControl : public IVariable, public IDynamicLink, public Unit::ITestCase, public IFlagCommunicator { public: SoX::RefSharedPointer<> m_spRootFrame; SoX::Math::Quaternion m_RotationFixed; SoX::Math::Vector m_PositionFixed; SoX::RefSharedPointer m_spWorld; boost::shared_ptr m_spGravity; boost::shared_ptr m_spInputListener; boost::shared_ptr m_spAmigoListener; boost::shared_ptr m_spCommonContextIF; boost::shared_ptr m_spActorManager; xpointer m_pTask; boost::shared_ptr m_spPosturePlugIn; SoX::Math::Vector m_GravityDirection; be m_GravityForce; SoX::Math::Vector m_SurfaceNormal; SoX::Math::Vector m_Position; SoX::Math::Quaternion m_Rotation; MARATHON_INSERT_PADDING(0x20); be m_PostureFlag; be m_ImpulseForward; be m_ImpulseVertical; SoX::Math::Vector m_ImpulseUp; be m_CommonContextIFFlags; be m_PostureRequestFlags; be m_PostureFlags118; be m_PostureFlags11C; }; MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spRootFrame, 0x10); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_RotationFixed, 0x20); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_PositionFixed, 0x30); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spWorld, 0x40); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spGravity, 0x44); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spInputListener, 0x4C); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spAmigoListener, 0x54); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spCommonContextIF, 0x5C); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spActorManager, 0x64); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_pTask, 0x6C); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_spPosturePlugIn, 0x70); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_GravityDirection, 0x80); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_GravityForce, 0x90); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_SurfaceNormal, 0xA0); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_Position, 0xB0); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_Rotation, 0xC0); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_PostureFlag, 0xF0); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_ImpulseForward, 0xF4); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_ImpulseVertical, 0xF8); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_ImpulseUp, 0x100); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_CommonContextIFFlags, 0x110); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_PostureRequestFlags, 0x114); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_PostureFlags118, 0x118); MARATHON_ASSERT_OFFSETOF(IPostureControl, m_PostureFlags11C, 0x11C); MARATHON_ASSERT_SIZEOF(IPostureControl, 0x120); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPosturePlugIn.h ================================================ #pragma once #include namespace Sonicteam::Player { class IPosturePlugIn { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IPosturePlugIn, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IPosturePlugIn, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPostureSupportEdge.h ================================================ #pragma once #include namespace Sonicteam::Player { class IPostureSupportEdge { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0x6C); }; MARATHON_ASSERT_OFFSETOF(IPostureSupportEdge, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IPostureSupportEdge, 0x70); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPostureSupportInput.h ================================================ #pragma once #include namespace Sonicteam::Player { class IPostureSupportInput { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0x2C); }; MARATHON_ASSERT_OFFSETOF(IPostureSupportInput, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IPostureSupportInput, 0x30); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPostureSupportOttoto.h ================================================ #pragma once #include namespace Sonicteam::Player { class IPostureSupportOttoto { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0x6C); }; MARATHON_ASSERT_OFFSETOF(IPostureSupportOttoto, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IPostureSupportOttoto, 0x70); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPostureSupportRayTemplate.h ================================================ #pragma once #include #include #include #include #include namespace Sonicteam::Player { template class IPostureSupportRayTemplate { public: xpointer m_pVftable; SoX::RefSharedPointer m_spWorld; boost::shared_ptr m_spCollisionListener; xpointer m_pRootFrame; }; MARATHON_ASSERT_OFFSETOF(IPostureSupportRayTemplate, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(IPostureSupportRayTemplate, m_spWorld, 0x04); MARATHON_ASSERT_OFFSETOF(IPostureSupportRayTemplate, m_spCollisionListener, 0x08); MARATHON_ASSERT_OFFSETOF(IPostureSupportRayTemplate, m_pRootFrame, 0x10); MARATHON_ASSERT_SIZEOF(IPostureSupportRayTemplate, 0x14); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IPostureSupportSphere.h ================================================ #pragma once #include namespace Sonicteam::Player { class IPostureSupportSphere { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0xCC); }; MARATHON_ASSERT_OFFSETOF(IPostureSupportSphere, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IPostureSupportSphere, 0xD0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IScore.h ================================================ #pragma once #include namespace Sonicteam::Player { class IScore : public IPlugIn {}; MARATHON_ASSERT_SIZEOF(IScore, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IStepable.h ================================================ #pragma once #include namespace Sonicteam::Player { class IStepable { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IStepable, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IStepable, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IVariable.h ================================================ #pragma once #include namespace Sonicteam::Player { class IVariable { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IVariable, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IVariable, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/IZock.h ================================================ #pragma once #include #include namespace Sonicteam::Player { class IZock : public IPlugIn {}; MARATHON_ASSERT_SIZEOF(IZock, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Input/IListener.h ================================================ #pragma once #include namespace Sonicteam::Player::Input { class IListener { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0x14); }; MARATHON_ASSERT_OFFSETOF(IListener, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IListener, 0x18); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Input/ListenerNormal.h ================================================ #pragma once #include #include namespace Sonicteam::Player::Input { class ListenerNormal : public IListener, public IPlugIn, public IVariable, public IStepable { public: MARATHON_INSERT_PADDING(4); xpointer m_pIsListening; be m_State; MARATHON_INSERT_PADDING(0x50); be m_ActionA; be m_ActionB; be m_ActionC; be m_ActionD; TimedAction m_TimedAction; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(ListenerNormal, m_pIsListening, 0x44); MARATHON_ASSERT_OFFSETOF(ListenerNormal, m_State, 0x48); MARATHON_ASSERT_OFFSETOF(ListenerNormal, m_ActionA, 0x9C); MARATHON_ASSERT_OFFSETOF(ListenerNormal, m_ActionB, 0xA0); MARATHON_ASSERT_OFFSETOF(ListenerNormal, m_ActionC, 0xA4); MARATHON_ASSERT_OFFSETOF(ListenerNormal, m_ActionD, 0xA8); MARATHON_ASSERT_OFFSETOF(ListenerNormal, m_TimedAction, 0xAC); MARATHON_ASSERT_SIZEOF(ListenerNormal, 0xC0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Input/TimedAction.h ================================================ #pragma once #include namespace Sonicteam::Player::Input { struct TimedAction { be m_Time; be m_Action; bool m_Field08; bool m_IsDoubleTapped; bool m_Field0A; bool m_Field0B; be m_Field0C; }; MARATHON_ASSERT_OFFSETOF(TimedAction, m_Time, 0x00); MARATHON_ASSERT_OFFSETOF(TimedAction, m_Action, 0x04); MARATHON_ASSERT_OFFSETOF(TimedAction, m_Field08, 0x08); MARATHON_ASSERT_OFFSETOF(TimedAction, m_IsDoubleTapped, 0x09); MARATHON_ASSERT_OFFSETOF(TimedAction, m_Field0A, 0x0A); MARATHON_ASSERT_OFFSETOF(TimedAction, m_Field0B, 0x0B); MARATHON_ASSERT_OFFSETOF(TimedAction, m_Field0C, 0x0C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Object.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Sonicteam::Player { class Object : public Actor { public: struct CreationParams { xpointer pPlayerLua; MARATHON_INSERT_PADDING(0x2C); SoX::Math::Vector Position; }; struct EquipFlags { be GlobalFlag; be EquipFlag; }; stdx::string m_LuaFile; stdx::string m_PackageFile; be m_TargetCameraActorID; xpointer m_pCameraman; be m_Index; be m_PadID; SoX::Math::Quaternion m_SpawnRotation; SoX::Math::Vector m_SpawnPosition; be m_SpawnRingCount; SoX::RefSharedPointer<> m_spSpawnSource; bool m_IsPlayer; bool m_IsPosture; bool m_IsAmigo; MARATHON_INSERT_PADDING(1); SoX::RefSharedPointer m_spRootFrame; SoX::RefSharedPointer<> m_spPackageBinary; boost::shared_ptr m_spModel; boost::shared_ptr m_spPostureControl; boost::shared_ptr m_spStateMachine; boost::shared_ptr m_spGravity; boost::shared_ptr m_spImpulse; be m_SetupModuleIndexPrefix; be m_SetupModuleIndexPostfix; boost::shared_ptr m_spGauge; MARATHON_INSERT_PADDING(8); stdx::vector> m_vspPlugins; MARATHON_INSERT_PADDING(0x58); be m_DeltaTime; MARATHON_INSERT_PADDING(0x48); stdx::vector m_vEquipFlags; stdx::string m_Name; MARATHON_INSERT_PADDING(0x110); template T* GetGauge() { return (T*)m_spGauge.get(); } template inline T* GetPlugin(const char* pluginName) { for (auto& spPlugin : m_vspPlugins) { if (spPlugin->m_Name == pluginName) return static_cast(spPlugin.get()); } return nullptr; } SoX::Input::Manager* GetInputManager() { if (!m_IsPlayer) return nullptr; auto pDoc = GetDoc(); auto pGame = pDoc->GetDocMode()->GetGame(); auto playerIndex = pGame->PlayerActorIDToIndex(m_ActorID); auto padID = pDoc->m_aPadIDs[playerIndex]; return pDoc->m_vspInputManager[padID].get(); } }; MARATHON_ASSERT_OFFSETOF(Object::CreationParams, pPlayerLua, 0x00); MARATHON_ASSERT_OFFSETOF(Object::CreationParams, Position, 0x30); MARATHON_ASSERT_OFFSETOF(Object::EquipFlags, GlobalFlag, 0x00); MARATHON_ASSERT_OFFSETOF(Object::EquipFlags, EquipFlag, 0x04); MARATHON_ASSERT_OFFSETOF(Object, m_LuaFile, 0x58); MARATHON_ASSERT_OFFSETOF(Object, m_PackageFile, 0x74); MARATHON_ASSERT_OFFSETOF(Object, m_TargetCameraActorID, 0x90); MARATHON_ASSERT_OFFSETOF(Object, m_pCameraman, 0x94); MARATHON_ASSERT_OFFSETOF(Object, m_Index, 0x98); MARATHON_ASSERT_OFFSETOF(Object, m_PadID, 0x9C); MARATHON_ASSERT_OFFSETOF(Object, m_SpawnRotation, 0xA0); MARATHON_ASSERT_OFFSETOF(Object, m_SpawnPosition, 0xB0); MARATHON_ASSERT_OFFSETOF(Object, m_SpawnRingCount, 0xC0); MARATHON_ASSERT_OFFSETOF(Object, m_spSpawnSource, 0xC4); MARATHON_ASSERT_OFFSETOF(Object, m_IsPlayer, 0xC8); MARATHON_ASSERT_OFFSETOF(Object, m_IsPosture, 0xC9); MARATHON_ASSERT_OFFSETOF(Object, m_IsAmigo, 0xCA); MARATHON_ASSERT_OFFSETOF(Object, m_spRootFrame, 0xCC); MARATHON_ASSERT_OFFSETOF(Object, m_spPackageBinary, 0xD0); MARATHON_ASSERT_OFFSETOF(Object, m_spModel, 0xD4); MARATHON_ASSERT_OFFSETOF(Object, m_spPostureControl, 0xDC); MARATHON_ASSERT_OFFSETOF(Object, m_spStateMachine, 0xE4); MARATHON_ASSERT_OFFSETOF(Object, m_spGravity, 0xEC); MARATHON_ASSERT_OFFSETOF(Object, m_spImpulse, 0xF4); MARATHON_ASSERT_OFFSETOF(Object, m_SetupModuleIndexPrefix, 0xFC); MARATHON_ASSERT_OFFSETOF(Object, m_SetupModuleIndexPostfix, 0x100); MARATHON_ASSERT_OFFSETOF(Object, m_spGauge, 0x104); MARATHON_ASSERT_OFFSETOF(Object, m_vspPlugins, 0x114); MARATHON_ASSERT_OFFSETOF(Object, m_DeltaTime, 0x17C); MARATHON_ASSERT_OFFSETOF(Object, m_vEquipFlags, 0x1C8); MARATHON_ASSERT_OFFSETOF(Object, m_Name, 0x1D8); MARATHON_ASSERT_SIZEOF(Object, 0x310); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/PostureControl.h ================================================ #pragma once #include #include #include #include #include #include #include #include namespace Sonicteam::Player { class PostureControl : public IPostureControl, public IPostureSupportSphere, public IPostureSupportOttoto, public IPostureSupportEdge, public IPostureSupportInput, public IPostureSupportRayTemplate { public: enum PostureFlag { // The player is grounded. PostureFlag_Grounded = 0x01, // The player is brushing against a wall. PostureFlag_WallSide = 0x08, // The player is head on against a wall. PostureFlag_WallFront = 0x10, // The player is grinding on a rail. PostureFlag_RailGrind = 0x40, // The player is in the intermediate state between jumping and falling. PostureFlag_FallIntermediate = 0x100, // The player is falling. PostureFlag_Fall = 0x200, // The player is on water collision. PostureFlag_Water = 0x800, // The player is light dashing. PostureFlag_LightDash = 0x4000, // The player is rotating in a non-forward direction. PostureFlag_QuickRotate = 0x8000, // The player is on tentative collision. PostureFlag_Tentative = 0x10000, // The player is water sliding. PostureFlag_WaterSlide = 0x20000, // The player is on grass collision. PostureFlag_Grass = 0x100000, // The player is on dirt collision. PostureFlag_Dirt = 0x200000, // The player is on stone collision. PostureFlag_Stone = 0x400000, // The player is on sand collision. PostureFlag_Sand = 0x1000000 }; MARATHON_INSERT_PADDING(0xE0); }; MARATHON_ASSERT_SIZEOF(PostureControl, 0x400); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/RootFrame.h ================================================ #pragma once #include #include #include #include #include #include namespace Sonicteam::Player { class RootFrame : public SoX::Graphics::Frame, public IPlugIn, public IExportExternalFlag { public: SoX::Math::Matrix4x4 m_Field70; SoX::Math::Matrix4x4 m_FieldB0; SoX::Math::Vector m_PositionF0; SoX::Math::Vector m_Field100; SoX::Math::Vector m_Impulse; MARATHON_INSERT_PADDING(0x30); be m_ExternalFlag; }; MARATHON_ASSERT_OFFSETOF(RootFrame, m_Field70, 0x70); MARATHON_ASSERT_OFFSETOF(RootFrame, m_FieldB0, 0xB0); MARATHON_ASSERT_OFFSETOF(RootFrame, m_PositionF0, 0xF0); MARATHON_ASSERT_OFFSETOF(RootFrame, m_Field100, 0x100); MARATHON_ASSERT_OFFSETOF(RootFrame, m_Impulse, 0x110); MARATHON_ASSERT_OFFSETOF(RootFrame, m_ExternalFlag, 0x150); MARATHON_ASSERT_SIZEOF(RootFrame, 0x160); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Score.h ================================================ #pragma once #include namespace Sonicteam::Player { class Score : public IScore, public IVariable, public IStepable { public: MARATHON_INSERT_PADDING(0x0C); xpointer m_pPlayer; MARATHON_INSERT_PADDING(0x38); }; MARATHON_ASSERT_OFFSETOF(Score, m_pPlayer, 0x34); MARATHON_ASSERT_SIZEOF(Score, 0x70); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/SonicGauge.h ================================================ #pragma once #include namespace Sonicteam::Player { class SonicGauge : public IGauge { public: be m_Value; be m_GroundedTime; be m_Flags; be m_GroundedFlags; be c_gauge_max; be c_gauge_green; be c_gauge_red; be c_gauge_blue; be c_gauge_white; be c_gauge_sky; be c_gauge_yellow; be c_gauge_purple; be c_gauge_super; be c_gauge_heal; be c_gauge_heal_delay; }; MARATHON_ASSERT_OFFSETOF(SonicGauge, m_Value, 0x28); MARATHON_ASSERT_OFFSETOF(SonicGauge, m_GroundedTime, 0x2C); MARATHON_ASSERT_OFFSETOF(SonicGauge, m_Flags, 0x30); MARATHON_ASSERT_OFFSETOF(SonicGauge, m_GroundedFlags, 0x34); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_max, 0x38); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_green, 0x3C); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_red, 0x40); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_blue, 0x44); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_white, 0x48); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_sky, 0x4C); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_yellow, 0x50); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_purple, 0x54); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_super, 0x58); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_heal, 0x5C); MARATHON_ASSERT_OFFSETOF(SonicGauge, c_gauge_heal_delay, 0x60); MARATHON_ASSERT_SIZEOF(SonicGauge, 0x64); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/CommonContext.h ================================================ #pragma once #include #include #include #include #include #include namespace Sonicteam::Player::State { class CommonContext : public ICommonContext, public IExportPostureRequestFlag, public IExportWeaponRequestFlag { public: xpointer m_spComboAttackManager; be m_CommonContextIFFlags1; be m_CommonContextIFFlags2; be m_CommonContextIFFlags3; be m_ExportPostureRequestFlags; be m_ExportWeaponRequestFlags; MARATHON_INSERT_PADDING(0x14); be m_PostureFlags; be m_ExternalFlags; be m_VehicleFlags; be m_AmigoFlags; MARATHON_INSERT_PADDING(0x50); boost::shared_ptr m_spScore; MARATHON_INSERT_PADDING(8); boost::shared_ptr m_spInputListenerNormal; boost::anonymous_shared_ptr m_spInputListenerAmigo; MARATHON_INSERT_PADDING(0xE8); SoX::Input::Manager* GetInputManager() const { if (!m_spScore.get()) return nullptr; return m_spScore->m_pPlayer->GetInputManager(); } }; MARATHON_ASSERT_OFFSETOF(CommonContext, m_spComboAttackManager, 0x98); MARATHON_ASSERT_OFFSETOF(CommonContext, m_CommonContextIFFlags1, 0x9C); MARATHON_ASSERT_OFFSETOF(CommonContext, m_CommonContextIFFlags2, 0xA0); MARATHON_ASSERT_OFFSETOF(CommonContext, m_CommonContextIFFlags3, 0xA4); MARATHON_ASSERT_OFFSETOF(CommonContext, m_ExportPostureRequestFlags, 0xA8); MARATHON_ASSERT_OFFSETOF(CommonContext, m_ExportWeaponRequestFlags, 0xAC); MARATHON_ASSERT_OFFSETOF(CommonContext, m_PostureFlags, 0xC4); MARATHON_ASSERT_OFFSETOF(CommonContext, m_ExternalFlags, 0xC8); MARATHON_ASSERT_OFFSETOF(CommonContext, m_VehicleFlags, 0xD0); MARATHON_ASSERT_OFFSETOF(CommonContext, m_AmigoFlags, 0xD4); MARATHON_ASSERT_OFFSETOF(CommonContext, m_spScore, 0x128); MARATHON_ASSERT_OFFSETOF(CommonContext, m_spInputListenerNormal, 0x138); MARATHON_ASSERT_OFFSETOF(CommonContext, m_spInputListenerAmigo, 0x140); MARATHON_ASSERT_SIZEOF(CommonContext, 0x230); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/CommonFall.h ================================================ #pragma once #include #include namespace Sonicteam::Player::State { class CommonFall : public CommonObject { public: MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_SIZEOF(CommonFall, 0x14); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/CommonObject.h ================================================ #pragma once #include #include namespace Sonicteam::Player::State { class CommonObject : public Object2 {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/ContextSpeedAndJump.h ================================================ #pragma once #include namespace Sonicteam::Player::State { class ContextSpeedAndJump { public: be m_BaseSpeedForward; be m_GimmickSpeedForward; be m_BaseSpeedVertical; be m_GimmickSpeedVertical; }; MARATHON_ASSERT_OFFSETOF(ContextSpeedAndJump, m_BaseSpeedForward, 0x00); MARATHON_ASSERT_OFFSETOF(ContextSpeedAndJump, m_GimmickSpeedForward, 0x04); MARATHON_ASSERT_OFFSETOF(ContextSpeedAndJump, m_BaseSpeedVertical, 0x08); MARATHON_ASSERT_OFFSETOF(ContextSpeedAndJump, m_GimmickSpeedVertical, 0x0C); MARATHON_ASSERT_SIZEOF(ContextSpeedAndJump, 0x10); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/ICommonContext.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::Player::State { class ICommonContext : public IContext, public ICommonContextIF, public ContextSpeedAndJump { public: be m_AnimationID; be m_LockButtons; be m_LastVelocityForward; be m_LastVelocityVertical; be m_LastLockButtons; be m_Buttons; be m_CurrentStickBorder; MARATHON_INSERT_PADDING(4); be m_AnimationState; MARATHON_INSERT_PADDING(0x2C); }; MARATHON_ASSERT_OFFSETOF(ICommonContext, m_AnimationID, 0x40); MARATHON_ASSERT_OFFSETOF(ICommonContext, m_LockButtons, 0x44); MARATHON_ASSERT_OFFSETOF(ICommonContext, m_LastVelocityForward, 0x48); MARATHON_ASSERT_OFFSETOF(ICommonContext, m_LastVelocityVertical, 0x4C); MARATHON_ASSERT_OFFSETOF(ICommonContext, m_LastLockButtons, 0x50); MARATHON_ASSERT_OFFSETOF(ICommonContext, m_Buttons, 0x54); MARATHON_ASSERT_OFFSETOF(ICommonContext, m_CurrentStickBorder, 0x58); MARATHON_ASSERT_OFFSETOF(ICommonContext, m_AnimationState, 0x60); MARATHON_ASSERT_SIZEOF(ICommonContext, 0x90); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/ICommonContextIF.h ================================================ #pragma once #include namespace Sonicteam::Player::State { class ICommonContextIF { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(ICommonContextIF, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(ICommonContextIF, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/IContext.h ================================================ #pragma once #include #include #include #include #include namespace Sonicteam::Player::State { class IContext : public IPlugIn, public IVariable, public IDynamicLink, public IFlagCommunicator {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/IMachine.h ================================================ #pragma once #include namespace Sonicteam::Player::State { class IMachine : public IPlugIn {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/Machine2.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::Player::State { class Machine2 : public SoX::AI::StateMachine, public IMachine { public: MARATHON_INSERT_PADDING(0x4C); inline SoX::AI::StateMachine* GetBase() { return (SoX::AI::StateMachine*)((uint8_t*)this - 0x20); } }; MARATHON_ASSERT_SIZEOF(Machine2, 0x78); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/Object2.h ================================================ #pragma once #include namespace Sonicteam::Player::State { class Object2 : public SoX::AI::StateMachine {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/SonicContext.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::Player::State { class SonicContext : public CommonContext { public: enum GemSprite : uint32_t { GemSprite_Green = 1, GemSprite_Red, GemSprite_Blue, GemSprite_White, GemSprite_Sky, GemSprite_Yellow, GemSprite_Purple, GemSprite_Super }; enum Gem : uint32_t { Gem_Blue = 1, Gem_Red, Gem_Green, Gem_Purple, Gem_Sky, Gem_White, Gem_Yellow, Gem_Super }; static constexpr GemSprite ms_GemSpriteConversionTable[8] = { GemSprite_Blue, GemSprite_Red, GemSprite_Green, GemSprite_Purple, GemSprite_Sky, GemSprite_White, GemSprite_Yellow, GemSprite_Super }; be m_CurrentGemSprite; boost::shared_ptr m_Gauge; uint8_t m_HomingLockOn; uint8_t m_DisablePlayerMovement; uint8_t m_AntigravityHitBox; uint8_t m_Field23F; uint8_t m_BoundAttackHitBox; uint8_t m_Field241; uint8_t m_Shrink; uint8_t m_ThunderGuard; uint8_t m_Tornado; uint8_t m_FPS; uint8_t m_ThrowGem; uint8_t m_SlowTime; uint8_t m_MachAura; uint8_t m_GemsEnabled; uint8_t m_Field24A; uint8_t m_Field24B; be m_HomingFlip; be m_CurrentGem; MARATHON_INSERT_PADDING(0x58); }; MARATHON_ASSERT_OFFSETOF(SonicContext, m_CurrentGemSprite, 0x230); MARATHON_ASSERT_OFFSETOF(SonicContext, m_Gauge, 0x234); MARATHON_ASSERT_OFFSETOF(SonicContext, m_HomingLockOn, 0x23C); MARATHON_ASSERT_OFFSETOF(SonicContext, m_DisablePlayerMovement, 0x23D); MARATHON_ASSERT_OFFSETOF(SonicContext, m_AntigravityHitBox, 0x23E); MARATHON_ASSERT_OFFSETOF(SonicContext, m_Field23F, 0x23F); MARATHON_ASSERT_OFFSETOF(SonicContext, m_BoundAttackHitBox, 0x240); MARATHON_ASSERT_OFFSETOF(SonicContext, m_Field241, 0x241); MARATHON_ASSERT_OFFSETOF(SonicContext, m_Shrink, 0x242); MARATHON_ASSERT_OFFSETOF(SonicContext, m_ThunderGuard, 0x243); MARATHON_ASSERT_OFFSETOF(SonicContext, m_Tornado, 0x244); MARATHON_ASSERT_OFFSETOF(SonicContext, m_FPS, 0x245); MARATHON_ASSERT_OFFSETOF(SonicContext, m_ThrowGem, 0x246); MARATHON_ASSERT_OFFSETOF(SonicContext, m_SlowTime, 0x247); MARATHON_ASSERT_OFFSETOF(SonicContext, m_MachAura, 0x248); MARATHON_ASSERT_OFFSETOF(SonicContext, m_GemsEnabled, 0x249); MARATHON_ASSERT_OFFSETOF(SonicContext, m_Field24A, 0x24A); MARATHON_ASSERT_OFFSETOF(SonicContext, m_Field24B, 0x24B); MARATHON_ASSERT_OFFSETOF(SonicContext, m_HomingFlip, 0x24C); MARATHON_ASSERT_OFFSETOF(SonicContext, m_CurrentGem, 0x250); MARATHON_ASSERT_SIZEOF(SonicContext, 0x2B0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/SonicObject.h ================================================ #pragma once #include namespace Sonicteam::Player::State { class SonicObject : public CommonObject {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/TailsContext.h ================================================ #pragma once #include namespace Sonicteam::Player::State { class TailsContext : public CommonContext { public: be m_FlightTime; MARATHON_INSERT_PADDING(8); be m_FlightDuration; be m_FlightLimit; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(TailsContext, m_FlightTime, 0x230); MARATHON_ASSERT_OFFSETOF(TailsContext, m_FlightDuration, 0x23C); MARATHON_ASSERT_OFFSETOF(TailsContext, m_FlightLimit, 0x240); MARATHON_ASSERT_SIZEOF(TailsContext, 0x250); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/State/TailsFlight.h ================================================ #pragma once #include namespace Sonicteam::Player::State { class TailsFlight : public CommonFall { public: MARATHON_INSERT_PADDING(4); be m_FlightTime; }; MARATHON_ASSERT_OFFSETOF(TailsFlight, m_FlightTime, 0x18); MARATHON_ASSERT_SIZEOF(TailsFlight, 0x1C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Unit/ITestCase.h ================================================ #pragma once #include namespace Sonicteam::Player::Unit { class ITestCase { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(ITestCase, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(ITestCase, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Weapon/SonicWeapons.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include namespace Sonicteam::Player::Weapon { class SonicWeapons : public IPlugIn, public IFlagCommunicator, public IStepable, public IDynamicLink, public IVariable, public INotification { public: MARATHON_INSERT_PADDING(0x4C); SoX::LinkRef m_GunDrive; MARATHON_INSERT_PADDING(0x28); }; MARATHON_ASSERT_OFFSETOF(SonicWeapons, m_GunDrive, 0x80); MARATHON_ASSERT_SIZEOF(SonicWeapons, 0xB8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/Player/Zock.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace Sonicteam::Player { class Zock : public IZock, public IFlagCommunicator, public IStepable, public IDynamicLink, public IVariable { public: SoX::RefSharedPointer m_spWorld; SoX::RefSharedPointer m_spRootFrame; SoX::RefSharedPointer m_spPhantomA; MARATHON_INSERT_PADDING(0x20); SoX::RefSharedPointer m_spPhantomB; MARATHON_INSERT_PADDING(0x40); SoX::RefSharedPointer m_spPhantomListener; MARATHON_INSERT_PADDING(0x3C); }; MARATHON_ASSERT_OFFSETOF(Zock, m_spWorld, 0x30); MARATHON_ASSERT_OFFSETOF(Zock, m_spRootFrame, 0x34); MARATHON_ASSERT_OFFSETOF(Zock, m_spPhantomA, 0x38); MARATHON_ASSERT_OFFSETOF(Zock, m_spPhantomB, 0x5C); MARATHON_ASSERT_OFFSETOF(Zock, m_spPhantomListener, 0xA0); MARATHON_ASSERT_SIZEOF(Zock, 0xE0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/PropFixture.h ================================================ #pragma once #include #include namespace Sonicteam { class PropFixture : public Fixture { public: MARATHON_INSERT_PADDING(0x10); }; MARATHON_ASSERT_SIZEOF(PropFixture, 0x180); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RaderMapManager.h ================================================ #pragma once #include namespace Sonicteam { class RaderMapManager : public Actor { public: xpointer m_pClump; xpointer m_pFrameGP1; xpointer m_pFrameGP2; MARATHON_INSERT_PADDING(0x0C); SoX::Math::Vector m_Transform; be m_Zoom; MARATHON_INSERT_PADDING(0x0C); SoX::Math::Vector m_Field90; MARATHON_INSERT_PADDING(0x10); }; MARATHON_ASSERT_OFFSETOF(RaderMapManager, m_pClump, 0x58); MARATHON_ASSERT_OFFSETOF(RaderMapManager, m_pFrameGP1, 0x5C); MARATHON_ASSERT_OFFSETOF(RaderMapManager, m_pFrameGP2, 0x60); MARATHON_ASSERT_OFFSETOF(RaderMapManager, m_Transform, 0x70); MARATHON_ASSERT_OFFSETOF(RaderMapManager, m_Zoom, 0x80); MARATHON_ASSERT_OFFSETOF(RaderMapManager, m_Field90, 0x90); MARATHON_ASSERT_SIZEOF(RaderMapManager, 0xB0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ApplyBloom.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ApplyBloom : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x4); }; MARATHON_ASSERT_SIZEOF(ApplyBloom, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ApplyDevice.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ApplyDevice : public MyRenderProcess {}; MARATHON_ASSERT_SIZEOF(ApplyDevice, 0x38); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ApplySceneParams.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ApplySceneParams : public MyRenderProcess {}; MARATHON_ASSERT_SIZEOF(ApplySceneParams, 0x38); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/AutoSetAspect.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ApplyAutoAspect : public MyRenderProcess { public: be m_CameraIndex; }; MARATHON_ASSERT_OFFSETOF(ApplyAutoAspect, m_CameraIndex, 0x38); MARATHON_ASSERT_SIZEOF(ApplyAutoAspect, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/Capture.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class Capture : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x4); xpointer m_pFramebufferHDR; MARATHON_INSERT_PADDING(0x14); xpointer m_pFBO1; MARATHON_INSERT_PADDING(0x10); xpointer m_pFBO2; }; MARATHON_ASSERT_OFFSETOF(Capture, m_pFramebufferHDR, 0x3C); MARATHON_ASSERT_OFFSETOF(Capture, m_pFBO1, 0x54); MARATHON_ASSERT_OFFSETOF(Capture, m_pFBO2, 0x68); MARATHON_ASSERT_SIZEOF(Capture, 0x6C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ClearRenderTarget.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ClearRenderTarget : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x8); }; MARATHON_ASSERT_SIZEOF(ClearRenderTarget, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ColorFill.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ColorFill : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x10); }; MARATHON_ASSERT_SIZEOF(ColorFill, 0x48); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/CopyTexture.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class CopyTexture : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x4); xpointer m_pTexture; MARATHON_INSERT_PADDING(0x4); }; MARATHON_ASSERT_OFFSETOF(CopyTexture, m_pTexture, 0x3C); MARATHON_ASSERT_SIZEOF(CopyTexture, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/LockBlendMode.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class LockBlendMode : public MyRenderProcess { public: bool m_Lock; }; MARATHON_ASSERT_OFFSETOF(LockBlendMode, m_Lock, 0x38); MARATHON_ASSERT_SIZEOF(LockBlendMode, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/LockCullMode.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class LockCullMode : public MyRenderProcess { public: bool m_Lock; }; MARATHON_ASSERT_OFFSETOF(LockCullMode, m_Lock, 0x38); MARATHON_ASSERT_SIZEOF(LockCullMode, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/LockZMode.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class LockZMode : public MyRenderProcess { public: bool m_Lock; }; MARATHON_ASSERT_OFFSETOF(LockZMode, m_Lock, 0x38); MARATHON_ASSERT_SIZEOF(LockZMode, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/MakeBloom.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class MakeBloom : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x34); }; MARATHON_ASSERT_SIZEOF(MakeBloom, 0x6C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/Movie.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class Movie : public MyRenderProcess { public: xpointer m_pMovieObject; }; MARATHON_ASSERT_OFFSETOF(Movie, m_pMovieObject, 0x38); MARATHON_ASSERT_SIZEOF(Movie, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/PrepareCalculateCSM.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class PrepareCalculateCSM : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x18); }; MARATHON_ASSERT_SIZEOF(PrepareCalculateCSM, 0x50); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/Rasterize.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class Rasterize : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x4); xpointer m_pTexture; bool m_Flag; }; MARATHON_ASSERT_OFFSETOF(Rasterize, m_pTexture, 0x3C); MARATHON_ASSERT_OFFSETOF(Rasterize, m_Flag, 0x40); MARATHON_ASSERT_SIZEOF(Rasterize, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/RasterizeBurnoutBlur.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class RasterizeBurnoutBlur : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x10); }; MARATHON_ASSERT_SIZEOF(RasterizeBurnoutBlur, 0x48); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ResetRenderStates.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ResetRenderStates : public MyRenderProcess {}; MARATHON_ASSERT_SIZEOF(ResetRenderStates, 0x38); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ResetScissorRect.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ResetScissorRect : public MyRenderProcess {}; MARATHON_ASSERT_SIZEOF(ResetScissorRect, 0x38); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/ResetViewport.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class ResetViewport : public MyRenderProcess {}; MARATHON_ASSERT_SIZEOF(ResetViewport, 0x38); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/Resolve.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class Resolve : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x8); xpointer m_pFBO; }; MARATHON_ASSERT_OFFSETOF(Resolve, m_pFBO, 0x40); MARATHON_ASSERT_SIZEOF(Resolve, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/RunCommandBuffer.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class RunCommandBuffer : public MyRenderProcess { public: be m_CameraIndex; MARATHON_INSERT_PADDING(0x4); }; MARATHON_ASSERT_OFFSETOF(RunCommandBuffer, m_CameraIndex, 0x38); MARATHON_ASSERT_SIZEOF(RunCommandBuffer, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetAutoZPass.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetAutoZPass : public MyRenderProcess { public: bool m_Enabled; }; MARATHON_ASSERT_OFFSETOF(SetAutoZPass, m_Enabled, 0x38); MARATHON_ASSERT_SIZEOF(SetAutoZPass, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetBackStencilOp.h ================================================ #pragma once #include #include #include namespace Sonicteam::RenderAction { class SetBackStencilOp : public MyRenderProcess { public: be m_Field38; be m_Field3C; be m_Field40; }; MARATHON_ASSERT_OFFSETOF(SetBackStencilOp, m_Field38, 0x38); MARATHON_ASSERT_OFFSETOF(SetBackStencilOp, m_Field3C, 0x3C); MARATHON_ASSERT_OFFSETOF(SetBackStencilOp, m_Field40, 0x40); MARATHON_ASSERT_SIZEOF(SetBackStencilOp, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetBlendMode.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetBlendMode : public MyRenderProcess { public: enum BlendMode { BlendMode_Opaque = 0, BlendMode_Blend = 1, BlendMode_Add = 2 }; be m_BlendMode; }; MARATHON_ASSERT_OFFSETOF(SetBlendMode, m_BlendMode, 0x38); MARATHON_ASSERT_SIZEOF(SetBlendMode, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetCSMTextures.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetCSMTextures : public MyRenderProcess { public: bool m_Flag; xpointer m_pCSMTexture; MARATHON_INSERT_PADDING(0x4); }; MARATHON_ASSERT_OFFSETOF(SetCSMTextures, m_Flag, 0x38); MARATHON_ASSERT_OFFSETOF(SetCSMTextures, m_pCSMTexture, 0x3C); MARATHON_ASSERT_SIZEOF(SetCSMTextures, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetClip.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetClip : public MyRenderProcess { public: be m_CameraIndex; be m_Clip1; be m_Clip2; }; MARATHON_ASSERT_OFFSETOF(SetClip, m_CameraIndex, 0x38); MARATHON_ASSERT_OFFSETOF(SetClip, m_Clip1, 0x3C); MARATHON_ASSERT_OFFSETOF(SetClip, m_Clip2, 0x40); MARATHON_ASSERT_SIZEOF(SetClip, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetColorWriteEnable.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetColorWriteEnable : public MyRenderProcess { public: enum ColorWrite : uint32_t { ColorWrite_None = 0, ColorWrite_Red = 1 << 0, ColorWrite_Green = 1 << 1, ColorWrite_Blue = 1 << 2, ColorWrite_Alpha = 1 << 3 }; be m_ColorWriteMask; }; MARATHON_ASSERT_OFFSETOF(SetColorWriteEnable, m_ColorWriteMask, 0x38); MARATHON_ASSERT_SIZEOF(SetColorWriteEnable, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetConstantShader.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetConstantShader : public MyRenderProcess { public: enum PassIndex { PassIndex_Main, PassIndex_Transparent, PassIndex_Sky, PassIndex_Shadowmap, PassIndex_Psi, PassIndex_Oc, PassIndex_Glare, PassIndex_AfterPp, PassIndex_Radermap, PassIndex_User0, PassIndex_User1 }; be m_PassIndex; xpointer m_pShader; }; MARATHON_ASSERT_OFFSETOF(SetConstantShader, m_PassIndex, 0x38); MARATHON_ASSERT_OFFSETOF(SetConstantShader, m_pShader, 0x3C); MARATHON_ASSERT_SIZEOF(SetConstantShader, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetCullMode.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetCullMode : public MyRenderProcess { public: enum CullMode { CullMode_None, CullMode_CW, CullMode_CCW }; be m_CullMode; }; MARATHON_ASSERT_OFFSETOF(SetCullMode, m_CullMode, 0x38); MARATHON_ASSERT_SIZEOF(SetCullMode, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetCurrentScreen.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetCurrentScreen : public MyRenderProcess { public: be m_ScreenIndex; }; MARATHON_ASSERT_OFFSETOF(SetCurrentScreen, m_ScreenIndex, 0x38); MARATHON_ASSERT_SIZEOF(SetCurrentScreen, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetDepthTextures.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetDepthTextures : public MyRenderProcess { public: bool m_Flag; xpointer m_pDepthTexture; }; MARATHON_ASSERT_OFFSETOF(SetDepthTextures, m_Flag, 0x38); MARATHON_ASSERT_OFFSETOF(SetDepthTextures, m_pDepthTexture, 0x3C); MARATHON_ASSERT_SIZEOF(SetDepthTextures, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetFovY.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetFovY : public MyRenderProcess { public: be m_CameraIndex; be m_FovY; }; MARATHON_ASSERT_OFFSETOF(SetFovY, m_CameraIndex, 0x38); MARATHON_ASSERT_OFFSETOF(SetFovY, m_FovY, 0x3C); MARATHON_ASSERT_SIZEOF(SetFovY, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetFrameBufferObject.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetFrameBufferObject : public MyRenderProcess { public: xpointer m_pFrameBufferObject; MARATHON_INSERT_PADDING(0x4); bool m_Once; bool m_PostProcess; }; MARATHON_ASSERT_OFFSETOF(SetFrameBufferObject, m_pFrameBufferObject, 0x38); MARATHON_ASSERT_OFFSETOF(SetFrameBufferObject, m_Once, 0x40); MARATHON_ASSERT_OFFSETOF(SetFrameBufferObject, m_PostProcess, 0x41); MARATHON_ASSERT_SIZEOF(SetFrameBufferObject, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetReflectionTextures.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetReflectionTextures : public MyRenderProcess { public: bool m_Flag; xpointer m_pReflectionTexture; }; MARATHON_ASSERT_OFFSETOF(SetReflectionTextures, m_Flag, 0x38); MARATHON_ASSERT_OFFSETOF(SetReflectionTextures, m_pReflectionTexture, 0x3C); MARATHON_ASSERT_SIZEOF(SetReflectionTextures, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetScissorRect.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetScissorRect : public MyRenderProcess { public: be m_X; be m_Y; be m_Width; be m_Height; }; MARATHON_ASSERT_OFFSETOF(SetScissorRect, m_X, 0x38); MARATHON_ASSERT_OFFSETOF(SetScissorRect, m_Y, 0x3C); MARATHON_ASSERT_OFFSETOF(SetScissorRect, m_Width, 0x40); MARATHON_ASSERT_OFFSETOF(SetScissorRect, m_Height, 0x44); MARATHON_ASSERT_SIZEOF(SetScissorRect, 0x48); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetScissorTest.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetScissorTest : public MyRenderProcess { public: bool m_Enabled; }; MARATHON_ASSERT_OFFSETOF(SetScissorTest, m_Enabled, 0x38); MARATHON_ASSERT_SIZEOF(SetScissorTest, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetScreen.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetScreen : public MyRenderProcess { public: be m_CameraIndex; be m_Width; be m_Height; }; MARATHON_ASSERT_OFFSETOF(SetScreen, m_CameraIndex, 0x38); MARATHON_ASSERT_OFFSETOF(SetScreen, m_Width, 0x3C); MARATHON_ASSERT_OFFSETOF(SetScreen, m_Height, 0x40); MARATHON_ASSERT_SIZEOF(SetScreen, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetShaderGPRAllocation.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetShaderGPRAllocation : public MyRenderProcess { public: be m_VertexShaderCount; be m_PixelShaderCount; }; MARATHON_ASSERT_OFFSETOF(SetShaderGPRAllocation, m_VertexShaderCount, 0x38); MARATHON_ASSERT_OFFSETOF(SetShaderGPRAllocation, m_PixelShaderCount, 0x3C); MARATHON_ASSERT_SIZEOF(SetShaderGPRAllocation, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetStencilEnable.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetStencilEnable : public MyRenderProcess { public: bool m_FrontEnable; bool m_BackEnable; }; MARATHON_ASSERT_OFFSETOF(SetStencilEnable, m_FrontEnable, 0x38); MARATHON_ASSERT_OFFSETOF(SetStencilEnable, m_BackEnable, 0x39); MARATHON_ASSERT_SIZEOF(SetStencilEnable, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetStencilFunc.h ================================================ #pragma once #include #include #include namespace Sonicteam::RenderAction { class SetStencilFunc : public MyRenderProcess { public: be m_StencilMode; be m_FrontReference; be m_BackReference; }; MARATHON_ASSERT_OFFSETOF(SetStencilFunc, m_StencilMode, 0x38); MARATHON_ASSERT_OFFSETOF(SetStencilFunc, m_FrontReference, 0x3C); MARATHON_ASSERT_OFFSETOF(SetStencilFunc, m_BackReference, 0x40); MARATHON_ASSERT_SIZEOF(SetStencilFunc, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetStencilOp.h ================================================ #pragma once #include #include #include namespace Sonicteam::RenderAction { class SetStencilOp : public MyRenderProcess { public: be m_Field38; be m_Field3C; be m_Field40; }; MARATHON_ASSERT_OFFSETOF(SetStencilOp, m_Field38, 0x38); MARATHON_ASSERT_OFFSETOF(SetStencilOp, m_Field3C, 0x3C); MARATHON_ASSERT_OFFSETOF(SetStencilOp, m_Field40, 0x40); MARATHON_ASSERT_SIZEOF(SetStencilOp, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetStencilWriteMask.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetStencilWriteMask : public MyRenderProcess { public: be m_WriteMask; }; MARATHON_ASSERT_OFFSETOF(SetStencilWriteMask, m_WriteMask, 0x38); MARATHON_ASSERT_SIZEOF(SetStencilWriteMask, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetTexture.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetTexture : public MyRenderProcess { public: enum AddressMode : uint32_t { AddressMode_Wrap = 1, AddressMode_Clamp = 2, AddressMode_Mirror = 3, AddressMode_Border = 4, }; enum FilterMode : uint32_t { FilterMode_Point = 1, FilterMode_Linear = 2, FilterMode_LinearMipmap = 3, FilterMode_Trilinear = 4, FilterMode_Anisotropic = 5, FilterMode_Gaussian = 5, }; be m_Unknown; xpointer m_pTexture; be m_XAddressMode; be m_YAddressMode; be m_FilterMode; }; MARATHON_ASSERT_OFFSETOF(SetTexture, m_Unknown, 0x38); MARATHON_ASSERT_OFFSETOF(SetTexture, m_pTexture, 0x3C); MARATHON_ASSERT_OFFSETOF(SetTexture, m_XAddressMode, 0x40); MARATHON_ASSERT_OFFSETOF(SetTexture, m_YAddressMode, 0x44); MARATHON_ASSERT_OFFSETOF(SetTexture, m_FilterMode, 0x48); MARATHON_ASSERT_SIZEOF(SetTexture, 0x4C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetUserClipPlaneEnable.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetUserClipPlaneEnable : public MyRenderProcess { public: be m_CameraIndex; MARATHON_INSERT_PADDING(0x4); }; MARATHON_ASSERT_OFFSETOF(SetUserClipPlaneEnable, m_CameraIndex, 0x38); MARATHON_ASSERT_SIZEOF(SetUserClipPlaneEnable, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetViewport.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetViewport : public MyRenderProcess { public: be m_X; be m_Y; be m_Width; be m_Height; }; MARATHON_ASSERT_OFFSETOF(SetViewport, m_X, 0x38); MARATHON_ASSERT_OFFSETOF(SetViewport, m_Y, 0x3C); MARATHON_ASSERT_OFFSETOF(SetViewport, m_Width, 0x40); MARATHON_ASSERT_OFFSETOF(SetViewport, m_Height, 0x44); MARATHON_ASSERT_SIZEOF(SetViewport, 0x48); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderAction/SetZMode.h ================================================ #pragma once #include #include namespace Sonicteam::RenderAction { class SetZMode : public MyRenderProcess { public: enum ZMode : uint32_t { ZMode_LessEqualOn = 1, ZMode_LessEqualOff = 2, ZMode_LessOn = 3, ZMode_LessOff = 4, ZMode_AlwaysOn = 5, ZMode_AlwaysOff = 6, }; be m_ZMode; }; MARATHON_ASSERT_OFFSETOF(SetZMode, m_ZMode, 0x38); MARATHON_ASSERT_SIZEOF(SetZMode, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/RenderPostprocess.h ================================================ #pragma once #include #include namespace Sonicteam { class RenderPostprocess : public MyRenderProcess { public: MARATHON_INSERT_PADDING(0x4); }; MARATHON_ASSERT_SIZEOF(RenderPostprocess, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SaveDataTask.h ================================================ #pragma once #include namespace Sonicteam { class SaveDataTask : public SoX::Engine::Task { public: xpointer m_pFileName; MARATHON_INSERT_PADDING(0x24); be m_Flags; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(SaveDataTask, m_pFileName, 0x4C); MARATHON_ASSERT_OFFSETOF(SaveDataTask, m_Flags, 0x74); MARATHON_ASSERT_SIZEOF(SaveDataTask, 0x7C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SaveDataTaskXENON.h ================================================ #pragma once #include namespace Sonicteam { class SaveDataTaskXENON : public SaveDataTask { public: enum SaveDataOperation : uint32_t { SaveDataOperation_AlertOverwrite, SaveDataOperation_AlertNoSaveData, SaveDataOperation_AlertSelectDevice, SaveDataOperation_SelectStorageDevice, SaveDataOperation_WriteSaveData, SaveDataOperation_ReadSaveData, SaveDataOperation_AlertSaveFailed, SaveDataOperation_AlertLoadFailed, SaveDataOperation_8, SaveDataOperation_9 }; xpointer m_pAlertWindowTask; MARATHON_INSERT_PADDING(8); bool m_IsAccessSuccess; xpointer m_pSystemTextBook; be m_Operation; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(SaveDataTaskXENON, m_pAlertWindowTask, 0x7C); MARATHON_ASSERT_OFFSETOF(SaveDataTaskXENON, m_IsAccessSuccess, 0x88); MARATHON_ASSERT_OFFSETOF(SaveDataTaskXENON, m_pSystemTextBook, 0x8C); MARATHON_ASSERT_OFFSETOF(SaveDataTaskXENON, m_Operation, 0x90); MARATHON_ASSERT_SIZEOF(SaveDataTaskXENON, 0x98); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SelectWindowTask.h ================================================ #pragma once #include #include namespace Sonicteam { class SelectWindowTask : public SoX::Engine::Task, public HUDCALLBACK { public: MARATHON_INSERT_PADDING(0x40); be m_ChosenIndex; MARATHON_INSERT_PADDING(0x28); be m_SelectedIndex; MARATHON_INSERT_PADDING(0x14); be m_BreatheFrame; MARATHON_INSERT_PADDING(0x28); }; MARATHON_ASSERT_OFFSETOF(SelectWindowTask, m_ChosenIndex, 0x90); MARATHON_ASSERT_OFFSETOF(SelectWindowTask, m_SelectedIndex, 0xBC); MARATHON_ASSERT_OFFSETOF(SelectWindowTask, m_BreatheFrame, 0xD4); MARATHON_ASSERT_SIZEOF(SelectWindowTask, 0x100); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/AI/StateMachine.h ================================================ #pragma once #include namespace Sonicteam::SoX::AI { template class StateMachine { public: xpointer m_pVftable; xpointer> m_pState; xpointer m_pContext; template TState* GetState() { return (TState*)m_pState.get(); } TStateContext* GetContext() { return (TStateContext*)m_pContext.get(); } template TContext* GetContext() { return (TContext*)m_pContext.get(); } }; MARATHON_ASSERT_OFFSETOF(StateMachine, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(StateMachine, m_pState, 0x04); MARATHON_ASSERT_OFFSETOF(StateMachine, m_pContext, 0x08); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.h ================================================ #pragma once #include #include namespace Sonicteam::SoX { class ApplicationXenon : public Engine::Application { public: MARATHON_INSERT_PADDING(0x174); static ApplicationXenon* GetInstance(); }; MARATHON_ASSERT_SIZEOF(ApplicationXenon, 0x180); } #include ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/ApplicationXenon.inl ================================================ namespace Sonicteam::SoX { inline ApplicationXenon* ApplicationXenon::GetInstance() { return *(xpointer*)MmGetHostAddress(0x82D3B348); } } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Audio/Cue.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX::Audio { class Cue : public RefCountObject { public: struct Vftable : RefCountObject::Vftable { MARATHON_INSERT_PADDING(0x18); be fpSetPause; }; MARATHON_INSERT_PADDING(0x0C); LinkNode m_Field14; MARATHON_INSERT_PADDING(8); xpointer m_pPlayer; template T* GetPlayer() { return (T*)m_pPlayer.get(); } void SetPause(bool isPaused) { GuestToHostFunction(((Vftable*)m_pVftable.get())->fpSetPause, this, isPaused); } }; MARATHON_ASSERT_OFFSETOF(Cue, m_Field14, 0x14); MARATHON_ASSERT_OFFSETOF(Cue, m_pPlayer, 0x28); MARATHON_ASSERT_SIZEOF(Cue, 0x2C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Audio/IAudioEngine.h ================================================ #pragma once #include namespace Sonicteam::SoX::Audio { class IAudioEngine { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(IAudioEngine, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IAudioEngine, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Audio/Player.h ================================================ #pragma once #include namespace Sonicteam::SoX::Audio { class Player { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(Player, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(Player, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Audio/PlayerImpl.h ================================================ #pragma once #include namespace Sonicteam::SoX::Audio { class PlayerImpl : public Player {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Component.h ================================================ #pragma once #include #include namespace Sonicteam::SoX { namespace Engine { class DocMode; } class Component : public Object { public: xpointer m_pDocMode; MARATHON_INSERT_PADDING(0x18); template T* GetDocMode() { return (T*)m_pDocMode.get(); } }; MARATHON_ASSERT_OFFSETOF(Component, m_pDocMode, 0x04); MARATHON_ASSERT_SIZEOF(Component, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Engine/Application.h ================================================ #pragma once #include namespace Sonicteam::SoX::Engine { class Application { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(Application, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(Application, 0x0C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Engine/Doc.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX::Engine { class Doc { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(4); xpointer m_pDocMode; xpointer m_pRootTask; xpointer m_pRootGTask; MARATHON_INSERT_PADDING(8); xpointer m_pDocModeExecutor; MARATHON_INSERT_PADDING(0x3C); template inline T* GetDocMode() { return (T*)m_pDocMode.get(); } }; MARATHON_ASSERT_OFFSETOF(Doc, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(Doc, m_pDocMode, 0x08); MARATHON_ASSERT_OFFSETOF(Doc, m_pRootTask, 0x0C); MARATHON_ASSERT_OFFSETOF(Doc, m_pRootGTask, 0x10); MARATHON_ASSERT_OFFSETOF(Doc, m_pDocModeExecutor, 0x1C); MARATHON_ASSERT_SIZEOF(Doc, 0x5C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Engine/DocMode.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Engine { class DocMode : public Task { public: MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_SIZEOF(DocMode, 0x50); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Engine/RenderProcess.h ================================================ #pragma once #include namespace Sonicteam::SoX::Engine { class RenderScheduler; class RenderProcess { public: struct Vftable { be fpDestroy; be fpFunc04; be fpFunc08; be fpPerformProcess; }; xpointer m_pVftable; xpointer m_Flag1; // Or Type? xpointer m_pGTask; be m_Flag2; MARATHON_INSERT_PADDING(0x8); be m_Field18; MARATHON_INSERT_PADDING(0x4); xpointer m_pRenderScheduler; MARATHON_INSERT_PADDING(0xC); }; MARATHON_ASSERT_SIZEOF(RenderProcess, 0x30); MARATHON_ASSERT_OFFSETOF(RenderProcess, m_Flag1, 4); MARATHON_ASSERT_OFFSETOF(RenderProcess, m_pGTask, 8); MARATHON_ASSERT_OFFSETOF(RenderProcess, m_Flag2, 0xC); MARATHON_ASSERT_OFFSETOF(RenderProcess, m_Field18, 0x18); MARATHON_ASSERT_OFFSETOF(RenderProcess, m_pRenderScheduler, 0x20); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Engine/Task.h ================================================ #pragma once #include #include #include #include namespace Sonicteam::SoX::Engine { class Doc; class Task : public Component, public MessageReceiver { public: be m_Flags; be m_Timestamp; xpointer m_pPrevSibling; xpointer m_pNextSibling; xpointer m_pDependency; xpointer m_pDependencies; xpointer m_pDoc; LinkNode m_lnTask; Task* GetFirstDependency() const { if (!m_pDependencies) return nullptr; auto pCurrent = m_pDependencies.get(); while (pCurrent->m_pPrevSibling && pCurrent->m_pPrevSibling != m_pDependencies.get()) pCurrent = pCurrent->m_pPrevSibling.get(); return pCurrent; } Task* GetLastDependency() const { return m_pDependencies.get(); } template T* GetDoc() { return (T*)m_pDoc.get(); } }; MARATHON_ASSERT_OFFSETOF(Task, m_Flags, 0x24); MARATHON_ASSERT_OFFSETOF(Task, m_Timestamp, 0x28); MARATHON_ASSERT_OFFSETOF(Task, m_pPrevSibling, 0x2C); MARATHON_ASSERT_OFFSETOF(Task, m_pNextSibling, 0x30); MARATHON_ASSERT_OFFSETOF(Task, m_pDependency, 0x34); MARATHON_ASSERT_OFFSETOF(Task, m_pDependencies, 0x38); MARATHON_ASSERT_OFFSETOF(Task, m_pDoc, 0x3C); MARATHON_ASSERT_OFFSETOF(Task, m_lnTask, 0x40); MARATHON_ASSERT_SIZEOF(Task, 0x4C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Device.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Graphics { class Device { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0x3C); Math::Matrix4x4 m_Field40; be m_Field80; MARATHON_INSERT_PADDING(0x4C); }; MARATHON_ASSERT_OFFSETOF(Device, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(Device, m_Field40, 0x40); MARATHON_ASSERT_OFFSETOF(Device, m_Field80, 0x80); MARATHON_ASSERT_SIZEOF(Device, 0xD0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Frame.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Graphics { class Frame : public RefCountObject { public: MARATHON_INSERT_PADDING(0x18); // SimpleNode MARATHON_INSERT_PADDING(0x2C); }; MARATHON_ASSERT_SIZEOF(Frame, 0x4C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameGP.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX::Graphics { class FrameGP : public Frame { public: MARATHON_INSERT_PADDING(0x84); stdx::string m_Name; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(FrameGP, m_Name, 0xD0); MARATHON_ASSERT_SIZEOF(FrameGP, 0xF0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/FrameObserver.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Graphics { class FrameObserver { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(8); xpointer m_pParent; xpointer m_pFrameGP; }; MARATHON_ASSERT_OFFSETOF(FrameObserver, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(FrameObserver, m_pParent, 0x0C); MARATHON_ASSERT_OFFSETOF(FrameObserver, m_pFrameGP, 0x10); MARATHON_ASSERT_SIZEOF(FrameObserver, 0x14); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Technique.h ================================================ #pragma once #include namespace Sonicteam::SoX::Graphics { class Technique : public RefCountObject { public: xpointer m_pShader; }; MARATHON_ASSERT_OFFSETOF(Technique, m_pShader, 0x08); MARATHON_ASSERT_SIZEOF(Technique, 0x0C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/TechniqueFXL.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Graphics { class TechniqueFXL : public Technique { public: xpointer m_pShader; MARATHON_INSERT_PADDING(0x10); xpointer m_pParent; MARATHON_INSERT_PADDING(4); stdx::string m_TechniqueName; }; MARATHON_ASSERT_OFFSETOF(TechniqueFXL, m_pShader, 0x0C); MARATHON_ASSERT_OFFSETOF(TechniqueFXL, m_pParent, 0x20); MARATHON_ASSERT_OFFSETOF(TechniqueFXL, m_TechniqueName, 0x28); MARATHON_ASSERT_SIZEOF(TechniqueFXL, 0x44); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Texture.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Graphics { class Texture : public IResource {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Transforms.h ================================================ #pragma once #include namespace Sonicteam::SoX::Graphics { class Transforms { public: struct Vftable { MARATHON_INSERT_PADDING(4); be fpComputeProjectionMatrix; }; xpointer m_pVftable; MARATHON_INSERT_PADDING(0xEC); }; MARATHON_ASSERT_OFFSETOF(Transforms, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(Transforms, 0xF0); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Vertex.h ================================================ #pragma once #include namespace Sonicteam::SoX::Graphics { struct Vertex { be X; be Y; MARATHON_INSERT_PADDING(0x10); be Colour; // ARGB8888 be U; be V; MARATHON_INSERT_PADDING(0x18); }; MARATHON_ASSERT_OFFSETOF(Vertex, X, 0x00); MARATHON_ASSERT_OFFSETOF(Vertex, Y, 0x04); MARATHON_ASSERT_OFFSETOF(Vertex, Colour, 0x18); MARATHON_ASSERT_OFFSETOF(Vertex, U, 0x1C); MARATHON_ASSERT_OFFSETOF(Vertex, V, 0x20); MARATHON_ASSERT_SIZEOF(Vertex, 0x3C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Xenon/DeviceXenon.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Graphics::Xenon { class DeviceXenon : public Device { public: MARATHON_INSERT_PADDING(0xC0); }; MARATHON_ASSERT_SIZEOF(DeviceXenon, 0x190); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Graphics/Xenon/TextureXenon.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Graphics::Xenon { class TextureXenon : public Texture { public: MARATHON_INSERT_PADDING(0x1C); be m_Width; be m_Height; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(TextureXenon, m_Width, 0x80); MARATHON_ASSERT_OFFSETOF(TextureXenon, m_Height, 0x84); MARATHON_ASSERT_SIZEOF(TextureXenon, 0x8C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/IResource.h ================================================ #pragma once #include #include namespace Sonicteam::SoX { class IResource : public RefCountObject { public: MARATHON_INSERT_PADDING(4); stdx::string m_FilePath; MARATHON_INSERT_PADDING(0x3C); }; MARATHON_ASSERT_OFFSETOF(IResource, m_FilePath, 0x0C); MARATHON_ASSERT_SIZEOF(IResource, 0x64); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/IResource2.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX { template class IResource2 : public IResource {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/IResourceMgr.h ================================================ #pragma once #include namespace Sonicteam::SoX { class IResourceMgr { public: struct Vftable { be fpDestroy; be fpMakeResource; MARATHON_INSERT_PADDING(4); }; xpointer m_pVftable; MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_OFFSETOF(IResourceMgr, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(IResourceMgr, 0x10); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Input/Manager.h ================================================ #pragma once #include namespace Sonicteam::SoX::Input { enum KeyState { KeyState_DPadUp = 0x40, KeyState_DPadDown = 0x80, KeyState_DPadLeft = 0x100, KeyState_DPadRight = 0x200, KeyState_Start = 0x400, KeyState_Select = 0x800, KeyState_LeftStick = 0x10000, KeyState_RightStick = 0x20000, KeyState_LeftBumper = 0x1000, KeyState_RightBumper = 0x2000, KeyState_A = 0x01, KeyState_B = 0x02, KeyState_X = 0x08, KeyState_Y = 0x10, KeyState_LeftTrigger = 0x4000, KeyState_RightTrigger = 0x8000, }; struct PadState { be LastButtons; be InvertedLastButtons; be PressedButtons; be ReleasedButtons; be LeftStickHorizontal; be LeftStickVertical; be LeftStickHorizontalS16; be LeftStickVerticalS16; be RightStickHorizontal; be RightStickVertical; be RightStickHorizontalS16; be RightStickVerticalS16; bool IsDown(const KeyState in_Keys) const; bool IsPressed(const KeyState in_Keys) const; bool IsReleased(const KeyState in_Keys) const; }; class Manager { public: be m_PadID; MARATHON_INSERT_PADDING(0x0C); PadState m_PadState; MARATHON_INSERT_PADDING(0x28); }; MARATHON_ASSERT_OFFSETOF(PadState, LastButtons, 0x00); MARATHON_ASSERT_OFFSETOF(PadState, InvertedLastButtons, 0x04); MARATHON_ASSERT_OFFSETOF(PadState, PressedButtons, 0x08); MARATHON_ASSERT_OFFSETOF(PadState, ReleasedButtons, 0x0C); MARATHON_ASSERT_OFFSETOF(PadState, LeftStickHorizontal, 0x10); MARATHON_ASSERT_OFFSETOF(PadState, LeftStickVertical, 0x14); MARATHON_ASSERT_OFFSETOF(PadState, LeftStickHorizontalS16, 0x18); MARATHON_ASSERT_OFFSETOF(PadState, LeftStickVerticalS16, 0x1A); MARATHON_ASSERT_OFFSETOF(PadState, RightStickHorizontal, 0x1C); MARATHON_ASSERT_OFFSETOF(PadState, RightStickVertical, 0x20); MARATHON_ASSERT_OFFSETOF(PadState, RightStickHorizontalS16, 0x24); MARATHON_ASSERT_OFFSETOF(PadState, RightStickVerticalS16, 0x26); MARATHON_ASSERT_OFFSETOF(Manager, m_PadID, 0x00); MARATHON_ASSERT_OFFSETOF(Manager, m_PadState, 0x10); } #include "Manager.inl" ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Input/Manager.inl ================================================ namespace Sonicteam::SoX::Input { inline bool PadState::IsDown(const KeyState in_Keys) const { return (LastButtons & in_Keys) == in_Keys; } inline bool PadState::IsPressed(const KeyState in_Keys) const { return (PressedButtons & in_Keys) == in_Keys; } inline bool PadState::IsReleased(const KeyState in_Keys) const { return (ReleasedButtons & in_Keys) == in_Keys; } } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/LinkNode.h ================================================ #pragma once namespace Sonicteam::SoX { template class ILinkNode { public: xpointer m_pPrev; xpointer m_pNext; }; template class LinkNode : public ILinkNode> { public: xpointer m_pThis; }; template class LinkRef { public: xpointer m_pElement; LinkNode m_lnElement; }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Math/Matrix.h ================================================ #pragma once #include "Marathon.inl" namespace Sonicteam::SoX::Math { class Matrix4x4 { public: be M[4][4]; Matrix4x4() {} Matrix4x4(Matrix4x4& other) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) M[i][j] = other.M[i][j]; } } Matrix4x4 Multiply(const Matrix4x4& other) const { Matrix4x4 result{}; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { result.M[i][j] = 0.0f; for (int k = 0; k < 4; k++) result.M[i][j] = result.M[i][j] + (M[i][k] * other.M[k][j]); } } return result; } Matrix4x4 operator*(Matrix4x4 other) { return Multiply(other); } }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Math/Quaternion.h ================================================ #pragma once #include namespace Sonicteam::SoX::Math { class alignas(16) Quaternion { public: be X; be Y; be Z; be W; }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Math/Vector.h ================================================ #pragma once #include namespace Sonicteam::SoX::Math { class Vector2 { public: be X; be Y; }; class Vector3 { public: be X; be Y; be Z; }; class alignas(16) Vector { public: be X; be Y; be Z; be W; float DistanceTo(const Vector& other) const { float dx = static_cast(X) - static_cast(other.X); float dy = static_cast(Y) - static_cast(other.Y); float dz = static_cast(Z) - static_cast(other.Z); return std::sqrt(dx * dx + dy * dy + dz * dz); } Vector operator+(float addend) { return { X + addend, Y + addend, Z + addend, W + addend }; } }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Message.h ================================================ #pragma once #include namespace Sonicteam::SoX { struct IMessage { be ID{}; IMessage() {} IMessage(const uint32_t id) : ID(id) {} }; template struct Message : IMessage { Message() { ID = id; } static const uint32_t GetID() { return id; } }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/MessageReceiver.h ================================================ #pragma once #include #include namespace Sonicteam::SoX { class MessageReceiver { public: struct Vftable { be fpDestroy; be fpProcessMessage; }; xpointer m_pVftable; void* Destroy(uint8_t flags = 1) { return GuestToHostFunction(m_pVftable->fpDestroy, this, flags); } bool ProcessMessage(IMessage* pMessage) { return GuestToHostFunction(m_pVftable->fpProcessMessage, this, pMessage); } }; MARATHON_ASSERT_OFFSETOF(MessageReceiver, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(MessageReceiver, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Object.h ================================================ #pragma once #include namespace Sonicteam::SoX { class Object { public: struct Vftable { be fpGetName; }; xpointer m_pVftable; const char* GetName() const { return GuestToHostFunction(m_pVftable->fpGetName.get()); } }; MARATHON_ASSERT_OFFSETOF(Object, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(Object, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/Entity.h ================================================ #pragma once #include #include #include #include #include #include #include #include namespace Sonicteam::SoX::Physics { class Entity : public MessageReceiver, public RefCountObject { public: struct Vftable : public MessageReceiver::Vftable { MARATHON_INSERT_PADDING(0x38); be fpInitializeToWorld; MARATHON_INSERT_PADDING(0x18); be fpSetPhantomListener; }; MARATHON_INSERT_PADDING(0x10); RefSharedPointer m_spPhantomListener; xpointer m_pReceiver; RefSharedPointer m_spShape; void InitializeToWorld(RefSharedPointer& world) { auto vft = static_cast(MessageReceiver::m_pVftable.get()); GuestToHostFunction(vft->fpInitializeToWorld, this, &world); } void SetPhantomListener(RefSharedPointer& phantomListener) { auto vft = static_cast(MessageReceiver::m_pVftable.get()); GuestToHostFunction(vft->fpSetPhantomListener, this, &phantomListener); } }; MARATHON_ASSERT_OFFSETOF(Entity, m_spPhantomListener, 0x1C); MARATHON_ASSERT_OFFSETOF(Entity, m_pReceiver, 0x20); MARATHON_ASSERT_OFFSETOF(Entity, m_spShape, 0x24); MARATHON_ASSERT_SIZEOF(Entity, 0x28); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/Havok/EntityHavok.h ================================================ #pragma once #include #include #include #include #include #include #include namespace Sonicteam::SoX::Physics::Havok { class EntityHavok { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(0x0C); xpointer m_pRigidBody; RefSharedPointer m_spWorldHavok; Math::Quaternion m_Rotation; Math::Vector m_Translation; MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_OFFSETOF(EntityHavok, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(EntityHavok, m_pRigidBody, 0x10); MARATHON_ASSERT_OFFSETOF(EntityHavok, m_spWorldHavok, 0x14); MARATHON_ASSERT_OFFSETOF(EntityHavok, m_Rotation, 0x20); MARATHON_ASSERT_OFFSETOF(EntityHavok, m_Translation, 0x30); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/Havok/EntityHavokImp.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX::Physics::Havok { template class EntityHavokImp : public T, public EntityHavok {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/Havok/PhantomHavok.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX::Physics::Havok { class PhantomHavok : public EntityHavokImp {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/Havok/WorldHavok.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX::Physics::Havok { class WorldHavok : public World { public: xpointer m_pWorld; MARATHON_INSERT_PADDING(0x14); bool m_IsDynamicUpdateRate; }; MARATHON_ASSERT_OFFSETOF(WorldHavok, m_pWorld, 0x08); MARATHON_ASSERT_OFFSETOF(WorldHavok, m_IsDynamicUpdateRate, 0x20); MARATHON_ASSERT_SIZEOF(WorldHavok, 0x24); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/IntersectEvent.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class IntersectEvent {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/IntersectListener.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class IntersectListener : public SoX::RefCountObject { public: MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_SIZEOF(IntersectListener, 0x14); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/Phantom.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class Phantom : public Entity {}; MARATHON_ASSERT_SIZEOF(Phantom, 0x28); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/PhantomListener.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class PhantomListener : public RefCountObject {}; MARATHON_ASSERT_SIZEOF(PhantomListener, 8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/Shape.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class Shape : public RefCountObject { public: struct Vftable : public RefCountObject::Vftable { be fpVFunction04; // (__out VECTOR* u1, __out VECTOR* u2) be fpInitializeVolume; }; be m_ShapeType; be m_ShapeVolume; // w * h * ... * FLT_MIN }; MARATHON_ASSERT_OFFSETOF(Shape, m_ShapeType, 0x08); MARATHON_ASSERT_OFFSETOF(Shape, m_ShapeVolume, 0x0C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/ShapeCastEvent.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class ShapeCastEvent {}; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/ShapeCastListener.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class ShapeCastListener : public SoX::RefCountObject {}; MARATHON_ASSERT_SIZEOF(ShapeCastListener, 8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Physics/World.h ================================================ #pragma once #include #include namespace Sonicteam::SoX::Physics { class World : public RefCountObject {}; MARATHON_ASSERT_SIZEOF(World, 8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/RefCountObject.h ================================================ #pragma once #include namespace Sonicteam::SoX { class RefCountObject { public: struct Vftable { be fpDestroy; }; xpointer m_pVftable; be m_ReferenceCount; void* Destroy(uint32_t flag = 1) { return GuestToHostFunction(m_pVftable->fpDestroy, this, flag); } void Release(uint32_t flag = 1) { m_ReferenceCount = m_ReferenceCount - 1; if (!m_ReferenceCount.get()) Destroy(flag); } inline void AddReference() { m_ReferenceCount = m_ReferenceCount + 1; } }; MARATHON_ASSERT_OFFSETOF(RefCountObject, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(RefCountObject, m_ReferenceCount, 0x04); MARATHON_ASSERT_SIZEOF(RefCountObject, 8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/RefSharedPointer.h ================================================ #pragma once #include #include namespace Sonicteam::SoX { template class RefSharedPointer { private: xpointer m_ptr; public: explicit RefSharedPointer(T* value) : m_ptr(value) { if (m_ptr.get()) m_ptr->AddRef(); } explicit RefSharedPointer(xpointer value) : m_ptr(value) { if (m_ptr.get()) m_ptr->AddRef(); } RefSharedPointer(const RefSharedPointer& other) : m_ptr(other.m_ptr) { if (m_ptr.get()) m_ptr->AddRef(); } RefSharedPointer(RefSharedPointer&& other) noexcept : m_ptr(std::move(other.m_ptr)) { other.m_ptr = nullptr; } ~RefSharedPointer() { if (m_ptr.get()) m_ptr->Release(); } RefSharedPointer& operator=(const RefSharedPointer& other) { if (this != &other) { reset(); m_ptr = other.m_ptr; if (m_ptr.get()) m_ptr->AddRef(); } return *this; } RefSharedPointer& operator=(RefSharedPointer&& other) noexcept { if (this != &other) { reset(); m_ptr = std::move(other.m_ptr); other.m_ptr = nullptr; } return *this; } void reset() { if (m_ptr.get()) m_ptr->Release(); m_ptr = 0; } T* get() const noexcept { return m_ptr.get(); } T* operator->() const noexcept { return m_ptr.get(); } T& operator*() const noexcept { return *m_ptr.get(); } explicit operator bool() const noexcept { return m_ptr.get() != nullptr; } bool operator==(const RefSharedPointer& other) const noexcept { return m_ptr.get() == other.m_ptr.get(); } bool operator!=(const RefSharedPointer& other) const noexcept { return !(*this == other); } }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Scenery/Camera.h ================================================ #pragma once #include namespace Sonicteam::SoX::Scenery { class Camera { public: struct Vftable { MARATHON_INSERT_PADDING(0x3C); be fpSetFar; be fpSetFOV; be fpSetViewMatrix; MARATHON_INSERT_PADDING(4); be fpSetAspectRatio; MARATHON_INSERT_PADDING(0x3C); be fpUpdate; }; xpointer m_pVftable; int SetFar(double zFar) { return GuestToHostFunction(m_pVftable->fpSetFar.get(), this, zFar); } int SetFOV(double fov) { return GuestToHostFunction(m_pVftable->fpSetFOV.get(), this, fov); } int SetAspectRatio(float width, float height) { struct AspectRatio { be Width; be Height; }; guest_stack_var aspectRatio(width, height); return GuestToHostFunction(m_pVftable->fpSetAspectRatio.get(), this, aspectRatio.get()); } int Update() { struct Unknown { MARATHON_INSERT_PADDING(0x40); }; guest_stack_var unknown; return GuestToHostFunction(m_pVftable->fpUpdate.get(), unknown.get(), this); } }; MARATHON_ASSERT_OFFSETOF(Camera, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(Camera, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraEventCallback.h ================================================ #pragma once #include namespace Sonicteam::SoX::Scenery { class CameraEventCallback { public: xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(CameraEventCallback, m_pVftable, 0x00); MARATHON_ASSERT_SIZEOF(CameraEventCallback, 4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Scenery/CameraImp.h ================================================ #pragma once #include #include #include #include #include #include #include namespace Sonicteam::SoX::Scenery { class CameraImp : public Camera { public: class frameObserver : public Graphics::FrameObserver { public: xpointer m_pCameraImp; be m_Field18; MARATHON_INSERT_PADDING(0x14); SoX::Math::Matrix4x4 m_Field30; SoX::Math::Matrix4x4 m_Field70; SoX::Math::Quaternion m_FieldB0; SoX::Math::Vector m_FieldC0; MARATHON_INSERT_PADDING(0x20); xpointer m_pWorldIntersectionStandard; MARATHON_INSERT_PADDING(0x10); xpointer m_pMyTransforms; MARATHON_INSERT_PADDING(8); }; stdx::string m_Name; be m_FOV; be m_AspectRatioWidth; be m_AspectRatioHeight; be m_Near; be m_Far; xpointer m_pWorldImp; MARATHON_INSERT_PADDING(0x18); SoX::Math::Matrix4x4 m_ViewMatrix; SoX::Math::Matrix4x4 m_Field90; SoX::Math::Matrix4x4 m_FieldD0; frameObserver m_FrameObserver; }; MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_pCameraImp, 0x14); MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_Field18, 0x18); MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_Field30, 0x30); MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_Field70, 0x70); MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_FieldB0, 0xB0); MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_FieldC0, 0xC0); MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_pWorldIntersectionStandard, 0xF0); MARATHON_ASSERT_OFFSETOF(CameraImp::frameObserver, m_pMyTransforms, 0x104); MARATHON_ASSERT_SIZEOF(CameraImp::frameObserver, 0x110); MARATHON_ASSERT_OFFSETOF(CameraImp, m_Name, 0x04); MARATHON_ASSERT_OFFSETOF(CameraImp, m_FOV, 0x20); MARATHON_ASSERT_OFFSETOF(CameraImp, m_AspectRatioWidth, 0x24); MARATHON_ASSERT_OFFSETOF(CameraImp, m_AspectRatioHeight, 0x28); MARATHON_ASSERT_OFFSETOF(CameraImp, m_Near, 0x2C); MARATHON_ASSERT_OFFSETOF(CameraImp, m_Far, 0x30); MARATHON_ASSERT_OFFSETOF(CameraImp, m_pWorldImp, 0x34); MARATHON_ASSERT_OFFSETOF(CameraImp, m_ViewMatrix, 0x50); MARATHON_ASSERT_OFFSETOF(CameraImp, m_Field90, 0x90); MARATHON_ASSERT_OFFSETOF(CameraImp, m_FieldD0, 0xD0); MARATHON_ASSERT_OFFSETOF(CameraImp, m_FrameObserver, 0x110); MARATHON_ASSERT_SIZEOF(CameraImp, 0x220); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Scenery/Drawable.h ================================================ #pragma once #include #include #include namespace Sonicteam::SoX::Scenery { class Drawable : public RefCountObject, public CameraEventCallback { public: MARATHON_INSERT_PADDING(0x64); }; MARATHON_ASSERT_SIZEOF(Drawable, 0x70); } ================================================ FILE: MarathonRecomp/api/Sonicteam/SoX/Thread.h ================================================ #pragma once #include namespace Sonicteam::SoX { class Thread { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(8); xpointer m_pParent; be m_EventHandleA; be m_EventHandleB; MARATHON_INSERT_PADDING(4); be m_ThreadHandle; bool m_Field20; MARATHON_INSERT_PADDING(3); be m_DeltaTime; MARATHON_INSERT_PADDING(8); xpointer m_pName; MARATHON_INSERT_PADDING(4); bool m_Field38; bool m_Field39; MARATHON_INSERT_PADDING(6); xpointer m_pContext; MARATHON_INSERT_PADDING(4); template T* GetContext() { return (T*)m_pContext.get(); } }; MARATHON_ASSERT_OFFSETOF(Thread, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(Thread, m_pParent, 0x0C); MARATHON_ASSERT_OFFSETOF(Thread, m_EventHandleA, 0x10); MARATHON_ASSERT_OFFSETOF(Thread, m_EventHandleB, 0x14); MARATHON_ASSERT_OFFSETOF(Thread, m_ThreadHandle, 0x1C); MARATHON_ASSERT_OFFSETOF(Thread, m_Field20, 0x20); MARATHON_ASSERT_OFFSETOF(Thread, m_DeltaTime, 0x24); MARATHON_ASSERT_OFFSETOF(Thread, m_pName, 0x30); MARATHON_ASSERT_OFFSETOF(Thread, m_Field38, 0x38); MARATHON_ASSERT_OFFSETOF(Thread, m_Field39, 0x39); MARATHON_ASSERT_OFFSETOF(Thread, m_pContext, 0x40); MARATHON_ASSERT_SIZEOF(Thread, 0x48); } ================================================ FILE: MarathonRecomp/api/Sonicteam/StdImageFilters/BurnoutBlurFilter.h ================================================ #pragma once #include #include namespace Sonicteam::StdImageFilters { class BurnoutBlurFilter : public SingleTechniqueFilter { public: MARATHON_INSERT_PADDING(0x14); be m_Magnitude; xpointer m_p1xBlurTechnique; xpointer m_p4xBlurTechnique; xpointer m_p8xBlurTechnique; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(BurnoutBlurFilter, m_Magnitude, 0x2C); MARATHON_ASSERT_OFFSETOF(BurnoutBlurFilter, m_p1xBlurTechnique, 0x30); MARATHON_ASSERT_OFFSETOF(BurnoutBlurFilter, m_p4xBlurTechnique, 0x34); MARATHON_ASSERT_OFFSETOF(BurnoutBlurFilter, m_p8xBlurTechnique, 0x38); MARATHON_ASSERT_SIZEOF(BurnoutBlurFilter, 0x40); } ================================================ FILE: MarathonRecomp/api/Sonicteam/StdImageFilters/SingleTechniqueFilter.h ================================================ #pragma once #include namespace Sonicteam::StdImageFilters { class SingleTechniqueFilter : public ImageFilter { public: MARATHON_INSERT_PADDING(0x10); }; MARATHON_ASSERT_SIZEOF(SingleTechniqueFilter, 0x18); } ================================================ FILE: MarathonRecomp/api/Sonicteam/System/CreateStatic.h ================================================ #pragma once namespace Sonicteam::System { template class CreateStatic { public: static T* Create() { return GuestToHostFunction(fpCreator); } }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/System/Diagnostics/Performance.h ================================================ #pragma once #include #include #include namespace Sonicteam::System::Diagnostics { class Performance : public Singleton> { public: be m_LastFrequency; }; MARATHON_ASSERT_OFFSETOF(Performance, m_LastFrequency, 0x00); MARATHON_ASSERT_SIZEOF(Performance, 8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/System/Singleton.h ================================================ #pragma once namespace Sonicteam::System { template class Singleton { inline static TCreator ms_Creator{}; public: static T* GetInstance() { auto pInstance = (xpointer*)g_memory.Translate(pSingleton); if (!pInstance->ptr.get()) *pInstance = ms_Creator.Create(); return *pInstance; } }; } ================================================ FILE: MarathonRecomp/api/Sonicteam/TextBook.h ================================================ #pragma once #include #include #include #include #include namespace Sonicteam { class TextBook : public SoX::IResource2 { public: struct Entry { xpointer Field00; xpointer Previous; xpointer Next; stdx::string Name; boost::shared_ptr spTextCard; MARATHON_INSERT_PADDING(1); bool Field31; MARATHON_INSERT_PADDING(2); }; MARATHON_INSERT_PADDING(4); boost::shared_ptr m_spResource; stdx::string m_Name; MARATHON_INSERT_PADDING(4); xpointer m_pRootEntry; be m_EntryCount; const char* FindName(const uint16_t* pText) const { auto pCardTable = m_spResource.get() + 0x2C; for (size_t i = 0; i < m_EntryCount; i++) { auto pCard = pCardTable + (i * 0x0C); auto pCardName = *(xpointer*)pCard; auto pCardText = *(xpointer*)(pCard + 4); if (!strcmpU16(pCardText, pText)) continue; return pCardName; } return nullptr; } }; MARATHON_ASSERT_OFFSETOF(TextBook::Entry, Field00, 0x00); MARATHON_ASSERT_OFFSETOF(TextBook::Entry, Previous, 0x04); MARATHON_ASSERT_OFFSETOF(TextBook::Entry, Next, 0x08); MARATHON_ASSERT_OFFSETOF(TextBook::Entry, Name, 0x0C); MARATHON_ASSERT_OFFSETOF(TextBook::Entry, spTextCard, 0x28); MARATHON_ASSERT_OFFSETOF(TextBook::Entry, Field31, 0x31); MARATHON_ASSERT_SIZEOF(TextBook::Entry, 0x34); MARATHON_ASSERT_OFFSETOF(TextBook, m_spResource, 0x68); MARATHON_ASSERT_OFFSETOF(TextBook, m_Name, 0x70); MARATHON_ASSERT_OFFSETOF(TextBook, m_pRootEntry, 0x90); MARATHON_ASSERT_OFFSETOF(TextBook, m_EntryCount, 0x94); MARATHON_ASSERT_SIZEOF(TextBook, 0x98); } ================================================ FILE: MarathonRecomp/api/Sonicteam/TextBookMgr.h ================================================ #pragma once #include #include #include namespace Sonicteam { class TextBookMgr : public SoX::IResourceMgr, public System::Singleton> {}; MARATHON_ASSERT_SIZEOF(TextBookMgr, 0x10); } ================================================ FILE: MarathonRecomp/api/Sonicteam/TextCard.h ================================================ #pragma once #include namespace Sonicteam { class TextCard { public: boost::shared_ptr m_spResource; xpointer m_pText; xpointer m_pVariables; }; MARATHON_ASSERT_OFFSETOF(TextCard, m_spResource, 0x00); MARATHON_ASSERT_OFFSETOF(TextCard, m_pText, 0x08); MARATHON_ASSERT_OFFSETOF(TextCard, m_pVariables, 0x0C); MARATHON_ASSERT_SIZEOF(TextCard, 0x10); } ================================================ FILE: MarathonRecomp/api/Sonicteam/TextEntity.h ================================================ #pragma once #include #include #include #include namespace Sonicteam { class TextFont; class TextBoard; class TextEntity : public CsdLink { public: MARATHON_INSERT_PADDING(4); boost::shared_ptr m_spTextFont; boost::shared_ptr m_spTextBoard; MARATHON_INSERT_PADDING(4); be m_X; be m_Y; MARATHON_INSERT_PADDING(8); stdx::wstring m_Text; stdx::wstring m_Field5C; MARATHON_INSERT_PADDING(4); xpointer m_pVariables; stdx::wstring m_Field80; MARATHON_INSERT_PADDING(0x14); be m_Width; be m_FieldB4; be m_FieldB8; be m_FieldBC; MARATHON_INSERT_PADDING(0x0C); be m_ScaleX; be m_ScaleY; MARATHON_INSERT_PADDING(8); bool m_FieldDC; bool m_FieldDD; MARATHON_INSERT_PADDING(2); xpointer m_FieldE0; // Only present when there's character vertices. xpointer m_FieldE4; // Only present when there's image vertices. xpointer m_pCharacterVertices; // BL/TL/TR BL/TR/BR (two triangles per character) xpointer m_pImageVertices; // BL/TL/TR BL/TR/BR (two triangles per image) be m_CharacterVertexCount; be m_ImageVertexCount; be m_TextLength; MARATHON_INSERT_PADDING(4); be m_Field100; be m_Field104; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(TextEntity, m_spTextFont, 0x1C); MARATHON_ASSERT_OFFSETOF(TextEntity, m_spTextBoard, 0x24); MARATHON_ASSERT_OFFSETOF(TextEntity, m_X, 0x30); MARATHON_ASSERT_OFFSETOF(TextEntity, m_Y, 0x34); MARATHON_ASSERT_OFFSETOF(TextEntity, m_Text, 0x40); MARATHON_ASSERT_OFFSETOF(TextEntity, m_Field5C, 0x5C); MARATHON_ASSERT_OFFSETOF(TextEntity, m_pVariables, 0x7C); MARATHON_ASSERT_OFFSETOF(TextEntity, m_Field80, 0x80); MARATHON_ASSERT_OFFSETOF(TextEntity, m_Width, 0xB0); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldB4, 0xB4); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldB8, 0xB8); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldBC, 0xBC); MARATHON_ASSERT_OFFSETOF(TextEntity, m_ScaleX, 0xCC); MARATHON_ASSERT_OFFSETOF(TextEntity, m_ScaleY, 0xD0); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldDC, 0xDC); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldDD, 0xDD); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldE0, 0xE0); MARATHON_ASSERT_OFFSETOF(TextEntity, m_FieldE4, 0xE4); MARATHON_ASSERT_OFFSETOF(TextEntity, m_pCharacterVertices, 0xE8); MARATHON_ASSERT_OFFSETOF(TextEntity, m_pImageVertices, 0xEC); MARATHON_ASSERT_OFFSETOF(TextEntity, m_CharacterVertexCount, 0xF0); MARATHON_ASSERT_OFFSETOF(TextEntity, m_ImageVertexCount, 0xF4); MARATHON_ASSERT_OFFSETOF(TextEntity, m_TextLength, 0xF8); MARATHON_ASSERT_OFFSETOF(TextEntity, m_Field100, 0x100); MARATHON_ASSERT_OFFSETOF(TextEntity, m_Field104, 0x104); MARATHON_ASSERT_SIZEOF(TextEntity, 0x110); } ================================================ FILE: MarathonRecomp/api/Sonicteam/TextFontPicture.h ================================================ #pragma once #include #include namespace Sonicteam { class TextFontPicture : public SoX::IResource2 { public: xpointer m_pResource; xpointer m_pTexture; be m_TextureWidth; be m_TextureHeight; be m_Field74; xpointer m_Field78; be m_CropCount; }; MARATHON_ASSERT_OFFSETOF(TextFontPicture, m_pResource, 0x64); MARATHON_ASSERT_OFFSETOF(TextFontPicture, m_pTexture, 0x68); MARATHON_ASSERT_OFFSETOF(TextFontPicture, m_TextureWidth, 0x6C); MARATHON_ASSERT_OFFSETOF(TextFontPicture, m_TextureHeight, 0x70); MARATHON_ASSERT_OFFSETOF(TextFontPicture, m_Field74, 0x74); MARATHON_ASSERT_OFFSETOF(TextFontPicture, m_Field78, 0x78); MARATHON_ASSERT_OFFSETOF(TextFontPicture, m_CropCount, 0x7C); } ================================================ FILE: MarathonRecomp/api/Sonicteam/TextFontPictureMgr.h ================================================ #pragma once #include namespace Sonicteam { class TextFontPictureMgr : public SoX::IResourceMgr, public System::Singleton> {}; MARATHON_ASSERT_SIZEOF(TextFontPictureMgr, 0x10); } ================================================ FILE: MarathonRecomp/api/Sonicteam/TitleTask.h ================================================ #pragma once #include #include namespace Sonicteam { class TitleTask : public SoX::Engine::Task { public: static constexpr float ms_DefaultMovieWaitTime = 30.0f; enum TitleState : uint32_t { TitleState_Open, TitleState_Wait, TitleState_PressStart, TitleState_OptionsOpen = 5, TitleState_OptionsWait, TitleState_OptionsProceed = 7, TitleState_Proceed, TitleState_Outro = 13 }; be m_State; MARATHON_INSERT_PADDING(0x0C); be m_MovieWaitTime; be m_Field60; MARATHON_INSERT_PADDING(0x18); be m_SelectedIndex; MARATHON_INSERT_PADDING(0x20); be m_CastIndex; MARATHON_INSERT_PADDING(4); }; MARATHON_ASSERT_OFFSETOF(TitleTask, m_State, 0x4C); MARATHON_ASSERT_OFFSETOF(TitleTask, m_MovieWaitTime, 0x5C); MARATHON_ASSERT_OFFSETOF(TitleTask, m_Field60, 0x60); MARATHON_ASSERT_OFFSETOF(TitleTask, m_SelectedIndex, 0x7C); MARATHON_ASSERT_OFFSETOF(TitleTask, m_CastIndex, 0xA0); MARATHON_ASSERT_SIZEOF(TitleTask, 0xA8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/VehicleMissileCtrl.h ================================================ #pragma once #include #include #include namespace Sonicteam { class MyAnimation; class MyModel; class VehicleLockOnListener; namespace Enemy { class ShotInfo; class ShotParameter; } namespace SoX::Graphics { class AnimationHierarchyCommon; class FrameGP; class InstanceModelCommon; } namespace SoX::Scenery { class Clump; } class VehicleMissileCtrl { public: xpointer m_pVftable; MARATHON_INSERT_PADDING(4); be m_MissileType; MARATHON_INSERT_PADDING(4); stdx::string m_ObjectName; stdx::string m_ShotName; be m_MissileBoxX; be m_MissileBoxY; be m_MissileBoxZ; xpointer m_pShotInfo; xpointer m_pShotParameter; MARATHON_INSERT_PADDING(8); be m_Field64; MARATHON_INSERT_PADDING(4); xpointer m_pMyPhantom; xpointer m_pVehicleLockOnListener; xpointer m_apRightGunFrameGPs[2]; xpointer m_pRightGunModel; xpointer m_pRightGunInstanceModel; xpointer m_pRightGunClump; xpointer m_pRightGunMotion; xpointer m_pRightGunMotionHierarchy; xpointer m_pLeftGunFrameGP; xpointer m_pLeftGunModel; xpointer m_pLeftGunInstanceModel; xpointer m_pLeftGunClump; xpointer m_pLeftGunMotion; xpointer m_pLeftGunMotionHierarchy; }; MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_MissileType, 0x08); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_ObjectName, 0x10); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_ShotName, 0x2C); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_MissileBoxX, 0x48); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_MissileBoxY, 0x4C); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_MissileBoxZ, 0x50); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pShotInfo, 0x54); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pShotParameter, 0x58); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_Field64, 0x64); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pMyPhantom, 0x6C); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pVehicleLockOnListener, 0x70); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_apRightGunFrameGPs, 0x74); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pRightGunModel, 0x7C); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pRightGunInstanceModel, 0x80); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pRightGunClump, 0x84); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pRightGunMotion, 0x88); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pRightGunMotionHierarchy, 0x8C); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pLeftGunFrameGP, 0x90); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pLeftGunModel, 0x94); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pLeftGunInstanceModel, 0x98); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pLeftGunClump, 0x9C); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pLeftGunMotion, 0xA0); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrl, m_pLeftGunMotionHierarchy, 0xA4); MARATHON_ASSERT_SIZEOF(VehicleMissileCtrl, 0xA8); } ================================================ FILE: MarathonRecomp/api/Sonicteam/VehicleMissileCtrlAutomatic.h ================================================ #pragma once #include #include namespace Sonicteam { class VehicleMissileCtrlAutomatic : public VehicleMissileCtrl { public: be m_MissileInterval; be m_MissileBullet; be m_MissileRecoveryTime; be m_MissileFrame; MARATHON_INSERT_PADDING(0x14); stdx::string m_VehicleName; MARATHON_INSERT_PADDING(8); bool m_IsShot; be m_FieldF4; MARATHON_INSERT_PADDING(8); }; MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlAutomatic, m_MissileInterval, 0xA8); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlAutomatic, m_MissileBullet, 0xAC); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlAutomatic, m_MissileRecoveryTime, 0xB0); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlAutomatic, m_MissileFrame, 0xB4); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlAutomatic, m_VehicleName, 0xCC); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlAutomatic, m_IsShot, 0xF0); MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlAutomatic, m_FieldF4, 0xF4); MARATHON_ASSERT_SIZEOF(VehicleMissileCtrlAutomatic, 0x100); } ================================================ FILE: MarathonRecomp/api/Sonicteam/VehicleMissileCtrlSingle.h ================================================ #pragma once #include namespace Sonicteam { class VehicleMissileCtrlSingle : public VehicleMissileCtrl { public: be m_MissileRechargeTime; MARATHON_INSERT_PADDING(0x18); }; MARATHON_ASSERT_OFFSETOF(VehicleMissileCtrlSingle, m_MissileRechargeTime, 0xA8); MARATHON_ASSERT_SIZEOF(VehicleMissileCtrlSingle, 0xC4); } ================================================ FILE: MarathonRecomp/api/Sonicteam/WorldRenderProcess.h ================================================ #pragma once #include #include namespace Sonicteam { class WorldRenderProcess : public MyRenderProcess { public: be m_PassIndex; MARATHON_INSERT_PADDING(0x4); }; MARATHON_ASSERT_SIZEOF(WorldRenderProcess, 0x40); MARATHON_ASSERT_OFFSETOF(WorldRenderProcess, m_PassIndex, 0x38); } ================================================ FILE: MarathonRecomp/api/Sonicteam/sonicXmaPlayer.h ================================================ #pragma once #include namespace Sonicteam { class sonicXmaPlayer : public SoX::RefCountObject { public: MARATHON_INSERT_PADDING(0xD8); bool m_IsPaused; MARATHON_INSERT_PADDING(0x108); }; MARATHON_ASSERT_OFFSETOF(sonicXmaPlayer, m_IsPaused, 0xE0); MARATHON_ASSERT_SIZEOF(sonicXmaPlayer, 0x1EC); } ================================================ FILE: MarathonRecomp/api/boost/smart_ptr/make_shared_object.h ================================================ #pragma once #include "boost/smart_ptr/shared_ptr.h" namespace boost { template shared_ptr make_shared(T* p, uint32_t vftable) { shared_ptr sp; auto* counted = (detail::sp_counted_impl_p*)g_userHeap.Alloc(sizeof(detail::sp_counted_impl_p)); new (counted) detail::sp_counted_impl_p(p); sp.px = p; sp.pn = (detail::sp_counted_base*)counted; sp.pn->vftable_.ptr = vftable; return sp; } } ================================================ FILE: MarathonRecomp/api/boost/smart_ptr/shared_ptr.h ================================================ #pragma once #include #include #include namespace boost { namespace detail { class sp_counted_base { protected: struct vftable_t { be destructor; be dispose; be destroy; be get_deleter; }; public: xpointer vftable_; protected: be use_count_; be weak_count_; public: // TODO sp_counted_base() {} void add_ref() { std::atomic_ref useCount(use_count_.value); be original, incremented; do { original = use_count_; incremented = original + 1; } while (!useCount.compare_exchange_weak(original.value, incremented.value)); } void release() { std::atomic_ref useCount(use_count_.value); be original, decremented; do { original = use_count_; decremented = original - 1; } while (!useCount.compare_exchange_weak(original.value, decremented.value)); if (decremented == 0) { GuestToHostFunction(vftable_->dispose, this); weak_release(); } } void weak_release() { std::atomic_ref weakCount(weak_count_.value); be original, decremented; do { original = weak_count_; decremented = original - 1; } while (!weakCount.compare_exchange_weak(original.value, decremented.value)); if (decremented == 0) { GuestToHostFunction(vftable_->destroy, this); } } uint32_t use_count() const { return ByteSwap(static_cast(use_count_.value)); } bool unique() const { return use_count() == 1; } }; template class sp_counted_impl_p : public sp_counted_base { public: sp_counted_impl_p(T* p) : ptr_(p) { use_count_ = 1; weak_count_ = 1; } ~sp_counted_impl_p() {} private: xpointer ptr_; }; template struct sp_dereference { typedef T& type; }; template<> struct sp_dereference { typedef void type; }; } template class shared_ptr { public: xpointer px; xpointer pn; void add_ref() { if (pn) pn->add_ref(); } void release() { if (pn) pn->release(); } shared_ptr() : px(), pn() {} // TODO explicit shared_ptr(T* p) = delete; shared_ptr(const shared_ptr& other) : px(other.px), pn(other.pn) { add_ref(); } shared_ptr(shared_ptr&& other) noexcept : px(std::exchange(other.px, nullptr)), pn(std::exchange(other.pn, nullptr)) {} ~shared_ptr() { release(); } shared_ptr& operator=(const shared_ptr& other) { if (this != &other) { release(); px = other.px; pn = other.pn; add_ref(); } return *this; } shared_ptr& operator=(shared_ptr&& other) noexcept { if (this != &other) { release(); px = std::exchange(other.px, nullptr); pn = std::exchange(other.pn, nullptr); } return *this; } shared_ptr& operator=(std::nullptr_t) { release(); px = NULL; pn = NULL; return *this; } T* get() const { return px; } detail::sp_dereference operator*() const { assert(px); return *px; } T* operator->() const { assert(px); return px; } explicit operator bool() const { return px != nullptr; } size_t use_count() const { return pn ? pn->use_count() : 0; } bool unique() const { return !pn || pn->unique(); } }; using anonymous_shared_ptr = shared_ptr; } ================================================ FILE: MarathonRecomp/api/d3dxb.h ================================================ #pragma once #include enum D3DXBSTENCILOP { D3DXBSTENCILOP_KEEP = 0, D3DXBSTENCILOP_ZERO = 1, D3DXBSTENCILOP_REPLACE = 2, D3DXBSTENCILOP_INCRSAT = 3, D3DXBSTENCILOP_DECRSAT = 4, D3DXBSTENCILOP_INVERT = 5, D3DXBSTENCILOP_INCR = 6, D3DXBSTENCILOP_DECR = 7 }; ================================================ FILE: MarathonRecomp/api/hk330/hkArray.h ================================================ #pragma once #include namespace hk330 { template class hkArray { public: xpointer m_data; be m_size; be m_capacityAndFlags; template T* GetIndex(E i) { return (T*)(m_data.get() + ((int)i * sizeof(T))); } }; MARATHON_ASSERT_OFFSETOF(hkArray, m_data, 0x00); MARATHON_ASSERT_OFFSETOF(hkArray, m_size, 0x04); MARATHON_ASSERT_OFFSETOF(hkArray, m_capacityAndFlags, 0x08); } ================================================ FILE: MarathonRecomp/api/hk330/hkReferencedObject.h ================================================ #pragma once #include namespace hk330 { class hkReferencedObject { public: xpointer m_pVftable; be m_memSizeAndFlags; be m_referenceCount; }; MARATHON_ASSERT_OFFSETOF(hkReferencedObject, m_pVftable, 0x00); MARATHON_ASSERT_OFFSETOF(hkReferencedObject, m_memSizeAndFlags, 0x04); MARATHON_ASSERT_OFFSETOF(hkReferencedObject, m_referenceCount, 0x06); } ================================================ FILE: MarathonRecomp/api/hk330/hkpBroadPhaseHandle.h ================================================ #pragma once #include #include namespace hk330 { class hkpBroadPhaseHandle { public: be m_id; }; MARATHON_ASSERT_OFFSETOF(hkpBroadPhaseHandle, m_id, 0x00); } ================================================ FILE: MarathonRecomp/api/hk330/hkpCdBody.h ================================================ #pragma once #include #include namespace hk330 { class hkpCdBody { public: xpointer m_shape; be m_shapeKey; xpointer m_motion; xpointer m_parent; }; MARATHON_ASSERT_OFFSETOF(hkpCdBody, m_shape, 0x00); MARATHON_ASSERT_OFFSETOF(hkpCdBody, m_shapeKey, 0x04); MARATHON_ASSERT_OFFSETOF(hkpCdBody, m_motion, 0x08); MARATHON_ASSERT_OFFSETOF(hkpCdBody, m_parent, 0x0C); } ================================================ FILE: MarathonRecomp/api/hk330/hkpCollidable.h ================================================ #pragma once #include #include #include namespace hk330 { class hkpCollidable : public hkpCdBody { public: MARATHON_INSERT_PADDING(4); hkpTypedBroadPhaseHandle m_broadPhaseHandle; MARATHON_INSERT_PADDING(0x28); be m_allowedPenetrationDepth; }; MARATHON_ASSERT_OFFSETOF(hkpCollidable, m_broadPhaseHandle, 0x14); MARATHON_ASSERT_OFFSETOF(hkpCollidable, m_allowedPenetrationDepth, 0x48); } ================================================ FILE: MarathonRecomp/api/hk330/hkpCollidableCollidableFilter.h ================================================ #pragma once #include namespace hk330 { class hkpCollidableCollidableFilter { public: struct Vftable { be fpCtor; be fpIsCollisionEnabled; }; xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(hkpCollidableCollidableFilter, m_pVftable, 0x00); } ================================================ FILE: MarathonRecomp/api/hk330/hkpCollisionFilter.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include namespace hk330 { class hkpCollisionFilter : public hkReferencedObject, public hkpCollidableCollidableFilter, public hkpShapeCollectionFilter, public hkpRayShapeCollectionFilter, public hkpRayCollidableFilter { public: be m_FilterType; MARATHON_INSERT_PADDING(0x0C); }; MARATHON_ASSERT_OFFSETOF(hkpCollisionFilter, m_FilterType, 0x18); } ================================================ FILE: MarathonRecomp/api/hk330/hkpEntity.h ================================================ #pragma once #include namespace hk330 { class hkpEntity : public hkpWorldObject { public: MARATHON_INSERT_PADDING(0x1E0); }; } ================================================ FILE: MarathonRecomp/api/hk330/hkpLinkedCollidable.h ================================================ #pragma once #include #include #include namespace hk330 { class hkpLinkedCollidable : public hkpCollidable { public: struct CollisionEntry { MARATHON_INSERT_PADDING(4); xpointer m_partner; }; hkArray m_collisionEntries; }; MARATHON_ASSERT_OFFSETOF(hkpLinkedCollidable::CollisionEntry, m_partner, 0x04); MARATHON_ASSERT_OFFSETOF(hkpLinkedCollidable, m_collisionEntries, 0x4C); } ================================================ FILE: MarathonRecomp/api/hk330/hkpPhantom.h ================================================ #pragma once #include #include namespace hk330 { class hkpPhantom : public hkpWorldObject { public: hkArray> m_overlapListeners; hkArray> m_phantomListeners; }; MARATHON_ASSERT_OFFSETOF(hkpPhantom, m_overlapListeners, 0x8C); MARATHON_ASSERT_OFFSETOF(hkpPhantom, m_phantomListeners, 0x98); } ================================================ FILE: MarathonRecomp/api/hk330/hkpProperty.h ================================================ #pragma once #include #include namespace hk330 { class hkpProperty { public: MARATHON_INSERT_PADDING(8); }; } ================================================ FILE: MarathonRecomp/api/hk330/hkpRayCollidableFilter.h ================================================ #pragma once #include namespace hk330 { class hkpRayCollidableFilter { public: struct Vftable { be fpCtor; be fpIsCollisionEnabled; }; xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(hkpRayCollidableFilter, m_pVftable, 0x00); } ================================================ FILE: MarathonRecomp/api/hk330/hkpRayShapeCollectionFilter.h ================================================ #pragma once #include namespace hk330 { class hkpRayShapeCollectionFilter { public: struct Vftable { be fpCtor; be fpIsCollisionEnabled; }; xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(hkpRayShapeCollectionFilter, m_pVftable, 0x00); } ================================================ FILE: MarathonRecomp/api/hk330/hkpRigidBody.h ================================================ #pragma once #include #include namespace hk330 { class hkpRigidBody : public hkpEntity {}; } ================================================ FILE: MarathonRecomp/api/hk330/hkpShape.h ================================================ #pragma once #include #include namespace hk330 { class hkpShape : public hkReferencedObject { public: be m_userData; be m_type; }; MARATHON_ASSERT_OFFSETOF(hkpShape, m_userData, 0x08); MARATHON_ASSERT_OFFSETOF(hkpShape, m_type, 0x0C); } ================================================ FILE: MarathonRecomp/api/hk330/hkpShapeCollectionFilter.h ================================================ #pragma once #include namespace hk330 { class hkpShapeCollectionFilter { public: struct Vftable { be fpCtor; be fpIsCollisionEnabled; }; xpointer m_pVftable; }; MARATHON_ASSERT_OFFSETOF(hkpShapeCollectionFilter, m_pVftable, 0x00); } ================================================ FILE: MarathonRecomp/api/hk330/hkpTypedBroadPhaseHandle.h ================================================ #pragma once #include #include namespace hk330 { class hkpTypedBroadPhaseHandle : public hkpBroadPhaseHandle { public: int8_t m_type; int8_t m_ownerOffset; be m_objectQualityType; be m_collisionFilterInfo; }; MARATHON_ASSERT_OFFSETOF(hkpTypedBroadPhaseHandle, m_type, 0x04); MARATHON_ASSERT_OFFSETOF(hkpTypedBroadPhaseHandle, m_ownerOffset, 0x05); MARATHON_ASSERT_OFFSETOF(hkpTypedBroadPhaseHandle, m_objectQualityType, 0x06); MARATHON_ASSERT_OFFSETOF(hkpTypedBroadPhaseHandle, m_collisionFilterInfo, 0x08); } ================================================ FILE: MarathonRecomp/api/hk330/hkpWorld.h ================================================ #pragma once #include #include #include #include namespace hk330 { class hkpBroadPhase; class hkpBroadPhaseBorderListener; class hkpCollisionDispatcher; class hkpCollisionFilter; class hkpEntityEntityBroadPhaseListener; class hkpPhantom; class hkpPhantomBroadPhaseListener; class hkpProcessCollisionInput; class hkpRigidBody; class hkpSimulation; class hkpSimulationIsland; class hkpTypedBroadPhaseDispatcher; class hkpWorldMaintenanceMgr; class hkpWorldOperationQueue; class hkWorldMemoryAvailableWatchDog; class hkpWorld : public hkReferencedObject { public: xpointer m_simulation; MARATHON_INSERT_PADDING(0x14); Sonicteam::SoX::Math::Vector m_gravity; xpointer m_fixedIsland; xpointer m_fixedRigidBody; hkArray> m_activeSimulationIslands; hkArray> m_inactiveSimulationIslands; hkArray> m_dirtySimulationIslands; xpointer m_maintenanceMgr; xpointer m_memoryWatchDog; xpointer m_broadPhase; xpointer m_broadPhaseDispatcher; xpointer m_phantomBroadPhaseListener; xpointer m_entityEntityBroadPhaseListener; xpointer m_broadPhaseBorderListener; xpointer m_collisionInput; xpointer m_collisionFilter; xpointer m_collisionDispatcher; xpointer m_pendingOperations; be m_pendingOperationsCount; be m_criticalOperationsLockCount; be m_criticalOperationsLockCountForPhantoms; bool m_blockExecutingPendingOperations; bool m_criticalOperationsAllowed; MARATHON_INSERT_PADDING(0x2C); hkArray> m_phantoms; void updateCollisionFilterOnWorld(uint32_t updateMode, uint32_t updateShapeCollectionFilter) { GuestToHostFunction(sub_82832910, this, updateMode, updateShapeCollectionFilter); } }; MARATHON_ASSERT_OFFSETOF(hkpWorld, m_simulation, 0x08); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_gravity, 0x20); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_fixedIsland, 0x30); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_fixedRigidBody, 0x34); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_activeSimulationIslands, 0x38); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_inactiveSimulationIslands, 0x44); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_dirtySimulationIslands, 0x50); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_maintenanceMgr, 0x5C); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_memoryWatchDog, 0x60); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_broadPhase, 0x64); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_broadPhaseDispatcher, 0x68); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_phantomBroadPhaseListener, 0x6C); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_entityEntityBroadPhaseListener, 0x70); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_broadPhaseBorderListener, 0x74); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_collisionInput, 0x78); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_collisionFilter, 0x7C); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_collisionDispatcher, 0x80); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_pendingOperations, 0x84); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_pendingOperationsCount, 0x88); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_criticalOperationsLockCount, 0x8C); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_criticalOperationsLockCountForPhantoms, 0x90); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_blockExecutingPendingOperations, 0x94); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_criticalOperationsAllowed, 0x95); MARATHON_ASSERT_OFFSETOF(hkpWorld, m_phantoms, 0xC4); } ================================================ FILE: MarathonRecomp/api/hk330/hkpWorldObject.h ================================================ #pragma once #include #include #include #include #include #include namespace hk330 { class hkpWorldObject : public hkReferencedObject { public: xpointer m_world; be m_userData; MARATHON_INSERT_PADDING(0x0C); hkpLinkedCollidable m_collidable; MARATHON_INSERT_PADDING(8); xpointer m_name; hkArray m_properties; }; MARATHON_ASSERT_OFFSETOF(hkpWorldObject, m_world, 0x08); MARATHON_ASSERT_OFFSETOF(hkpWorldObject, m_userData, 0x0C); MARATHON_ASSERT_OFFSETOF(hkpWorldObject, m_collidable, 0x1C); MARATHON_ASSERT_OFFSETOF(hkpWorldObject, m_name, 0x7C); MARATHON_ASSERT_OFFSETOF(hkpWorldObject, m_properties, 0x80); } ================================================ FILE: MarathonRecomp/api/stdx/string.h ================================================ #pragma once #include #include namespace stdx { class string { private: union _Bxty { _Bxty() {} char _buffer[0x10]; xpointer _str; }; be _Myproxy; _Bxty _bx; be _Mysize; be _Myres; bool is_short() const { return _Mysize <= 0xF; } public: const char* c_str() const { return is_short() ? (const char*)&_bx._buffer : (const char*)_bx._str.get(); } size_t size() const { return _Mysize.get(); } size_t capacity() const { return _Myres.get(); } string() { _Myres = 0xF; _Mysize = 0; _bx._buffer[0] = '\0'; } string(xpointer str) : string(str.get()) {} string(const char* str) { _Myres = 0xF; _Mysize = 0; _bx._buffer[0] = '\0'; from_cstr(str); } ~string() { cleanup(); _Myres = 0xF; _Mysize = 0; _bx._buffer[0] = '\0'; } void cleanup() { if (!is_short()) g_userHeap.Free((void*)_bx._str.get()); } void from_cstr(const char* str) { auto len = strlen(str); if (len <= 0xF) { memcpy((void*)&_bx._buffer, str, len + 1); _Mysize = (uint32_t)(len); } else { if (is_short() || capacity() < len + 1) { cleanup(); char* new_buf = g_userHeap.Alloc(len + 1); memset((void*)(new_buf), 0, len + 1); memcpy((void*)(new_buf), (const void*)(str), len + 1); _bx._str = new_buf; _Myres = len + 1; } else { memcpy((void*)_bx._str.get(), (void*)str, len + 1); } _Mysize = len; } } string& operator=(const char* str) { from_cstr(str); return *this; } bool operator==(const char* str) const { return strcmp(c_str(), str) == 0; } }; }; ================================================ FILE: MarathonRecomp/api/stdx/vector.h ================================================ #pragma once #include #include #include #include namespace stdx { template class vector { private: be _Myproxy; xpointer _MyFirst; xpointer _MyLast; xpointer _MyEnd; static xpointer xpointer_add(xpointer ptr, size_t offset) { return xpointer(reinterpret_cast( reinterpret_cast(ptr.get()) + offset * sizeof(T))); } void _ConstructRange(T* first, T* last, const T& value) { for (; first != last; ++first) new (first) T(value); } void _DestroyRange(T* first, T* last) { while (first != last) { first->~T(); ++first; } } void _Reallocate(size_t new_capacity) { T* new_block = static_cast(g_userHeap.Alloc(sizeof(T) * new_capacity)); const size_t old_size = size(); for (size_t i = 0; i < old_size; ++i) { T* target = reinterpret_cast( reinterpret_cast(new_block) + i * sizeof(T)); new (target) T(std::move(_MyFirst[i])); _MyFirst[i].~T(); } if (_MyFirst) { _DestroyRange(_MyFirst.get(), _MyLast.get()); g_userHeap.Free(_MyFirst.get()); } _MyFirst = xpointer(new_block); _MyLast = xpointer_add(_MyFirst, old_size); _MyEnd = xpointer_add(_MyFirst, new_capacity); } void _GrowIfNeeded() { if (_MyLast.get() == _MyEnd.get()) _Reallocate(size() ? size() * 2 : 1); } class iterator_wrapper { private: xpointer _ptr; public: explicit iterator_wrapper(xpointer ptr) : _ptr(ptr) {} T& operator*() const { return *_ptr; } T* operator->() const { return _ptr.get(); } iterator_wrapper& operator++() { _ptr = xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) + sizeof(T))); return *this; } iterator_wrapper operator++(int) { iterator_wrapper tmp = *this; ++(*this); return tmp; } iterator_wrapper& operator--() { _ptr = xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) - sizeof(T))); return *this; } iterator_wrapper operator--(int) { iterator_wrapper tmp = *this; --(*this); return tmp; } iterator_wrapper operator+(size_t n) const { return iterator_wrapper(xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) + n * sizeof(T)))); } iterator_wrapper operator-(size_t n) const { return iterator_wrapper(xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) - n * sizeof(T)))); } bool operator==(const iterator_wrapper& other) const { return _ptr.get() == other._ptr.get(); } bool operator!=(const iterator_wrapper& other) const { return _ptr.get() != other._ptr.get(); } }; class const_iterator_wrapper { private: xpointer _ptr; public: explicit const_iterator_wrapper(xpointer ptr) : _ptr(ptr) {} const T& operator*() const { return *_ptr; } const T* operator->() const { return _ptr.get(); } const_iterator_wrapper& operator++() { _ptr = xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) + sizeof(T))); return *this; } const_iterator_wrapper operator++(int) { const_iterator_wrapper tmp = *this; ++(*this); return tmp; } const_iterator_wrapper& operator--() { _ptr = xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) - sizeof(T))); return *this; } const_iterator_wrapper operator--(int) { const_iterator_wrapper tmp = *this; --(*this); return tmp; } const_iterator_wrapper operator+(size_t n) const { return const_iterator_wrapper(xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) + n * sizeof(T)))); } const_iterator_wrapper operator-(size_t n) const { return const_iterator_wrapper(xpointer(reinterpret_cast( reinterpret_cast(_ptr.get()) - n * sizeof(T)))); } bool operator==(const const_iterator_wrapper& other) const { return _ptr.get() == other._ptr.get(); } bool operator!=(const const_iterator_wrapper& other) const { return _ptr.get() != other._ptr.get(); } }; public: using iterator = iterator_wrapper; using const_iterator = const_iterator_wrapper; vector() noexcept : _MyFirst(nullptr), _MyLast(nullptr), _MyEnd(nullptr) {} explicit vector(size_t count) { _MyFirst = xpointer(static_cast(g_userHeap.Alloc(sizeof(T) * count))); _MyLast = xpointer_add(_MyFirst, count); _MyEnd = _MyLast; _ConstructRange(_MyFirst.get(), _MyLast.get(), T()); } vector(size_t count, const T& value) { _MyFirst = xpointer(static_cast(g_userHeap.Alloc(sizeof(T) * count))); _MyLast = xpointer_add(_MyFirst, count); _MyEnd = _MyLast; _ConstructRange(_MyFirst.get(), _MyLast.get(), value); } vector(const vector& other) { _MyFirst = xpointer(static_cast(g_userHeap.Alloc(sizeof(T) * other.size()))); _MyLast = xpointer_add(_MyFirst, other.size()); _MyEnd = _MyLast; for (size_t i = 0; i < other.size(); ++i) new (xpointer_add(_MyFirst, i).get()) T(other._MyFirst[i]); } vector(vector&& other) noexcept : _MyFirst(other._MyFirst), _MyLast(other._MyLast), _MyEnd(other._MyEnd) { other._MyFirst = other._MyLast = other._MyEnd = nullptr; } ~vector() { clear(); if (_MyFirst) g_userHeap.Free(_MyFirst.get()); } vector& operator=(const vector& other) { if (this != &other) { vector tmp(other); swap(tmp); } return *this; } vector& operator=(vector&& other) noexcept { if (this != &other) { clear(); if (_MyFirst) g_userHeap.Free(_MyFirst.get()); _MyFirst = other._MyFirst; _MyLast = other._MyLast; _MyEnd = other._MyEnd; other._MyFirst = other._MyLast = other._MyEnd = nullptr; } return *this; } T& operator[](size_t pos) { return _MyFirst.get()[pos]; } const T& operator[](size_t pos) const { return _MyFirst.get()[pos]; } T& at(size_t pos) { return _MyFirst.get()[pos]; } const T& at(size_t pos) const { return _MyFirst.get()[pos]; } T& front() { return *_MyFirst; } const T& front() const { return *_MyFirst; } T& back() { return *(xpointer_add(_MyLast, -1).get()); } const T& back() const { return *(xpointer_add(_MyLast, -1).get()); } T* data() { return _MyFirst.get(); } const T* data() const { return _MyFirst.get(); } iterator begin() { return iterator(_MyFirst); } const_iterator begin() const { return const_iterator(_MyFirst); } const_iterator cbegin() const { return const_iterator(_MyFirst); } iterator end() { return iterator(_MyLast); } const_iterator end() const { return const_iterator(_MyLast); } const_iterator cend() const { return const_iterator(_MyLast); } bool empty() const { return _MyFirst.get() == _MyLast.get(); } size_t size() const { return (_MyLast.get() - _MyFirst.get()); } size_t capacity() const { return (_MyEnd.get() - _MyFirst.get()); } void reserve(size_t new_capacity) { if (new_capacity > capacity()) _Reallocate(new_capacity); } void shrink_to_fit() { if (size() < capacity()) _Reallocate(size()); } void clear() { _DestroyRange(_MyFirst.get(), _MyLast.get()); _MyLast = _MyFirst; } void push_back(const T& value) { _GrowIfNeeded(); new (_MyLast.get()) T(value); _MyLast = xpointer_add(_MyLast, 1); } void push_back(T&& value) { _GrowIfNeeded(); new (_MyLast.get()) T(std::move(value)); _MyLast = xpointer_add(_MyLast, 1); } template void emplace_back(Args&&... args) { _GrowIfNeeded(); new (_MyLast.get()) T(std::forward(args)...); _MyLast = xpointer_add(_MyLast, 1); } void pop_back() { _MyLast = xpointer_add(_MyLast, -1); _MyLast.get()->~T(); } void resize(size_t count) { if (count < size()) { _DestroyRange(xpointer_add(_MyFirst, count).get(), _MyLast.get()); _MyLast = xpointer_add(_MyFirst, count); } else if (count > size()) { reserve(count); while (_MyLast.get() != xpointer_add(_MyFirst, count).get()) { new (_MyLast.get()) T(); _MyLast = xpointer_add(_MyLast, 1); } } } void resize(size_t count, const T& value) { if (count < size()) { _DestroyRange(xpointer_add(_MyFirst, count).get(), _MyLast.get()); _MyLast = xpointer_add(_MyFirst, count); } else if (count > size()) { reserve(count); while (_MyLast.get() != xpointer_add(_MyFirst, count).get()) { new (_MyLast.get()) T(value); _MyLast = xpointer_add(_MyLast, 1); } } } void swap(vector& other) noexcept { std::swap(_MyFirst, other._MyFirst); std::swap(_MyLast, other._MyLast); std::swap(_MyEnd, other._MyEnd); } }; template void swap(vector& lhs, vector& rhs) noexcept { lhs.swap(rhs); } template bool operator==(const vector& lhs, const vector& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template bool operator!=(const vector& lhs, const vector& rhs) { return !(lhs == rhs); } template bool operator<(const vector& lhs, const vector& rhs) { return std::lexicographical_compare( lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template bool operator<=(const vector& lhs, const vector& rhs) { return !(rhs < lhs); } template bool operator>(const vector& lhs, const vector& rhs) { return rhs < lhs; } template bool operator>=(const vector& lhs, const vector& rhs) { return !(lhs < rhs); } } ================================================ FILE: MarathonRecomp/api/stdx/wstring.h ================================================ #pragma once #include #include namespace stdx { class wstring { private: union _Bxty { _Bxty() {} uint16_t _buffer[8]; xpointer _str; }; be _Myproxy; _Bxty _bx; be _Mysize; be _Myres; bool is_short() const { return _Mysize < 8; } public: const uint16_t* c_str() const { return is_short() ? (const uint16_t*)&_bx._buffer : (const uint16_t*)_bx._str.get(); } size_t size() const { return _Mysize.get(); } size_t capacity() const { return _Myres.get(); } wstring() { _Myres = 0xF; _Mysize = 0; _bx._buffer[0] = '\0'; _bx._buffer[1] = '\0'; } wstring(xpointer str) : wstring(str.get()) {} wstring(const uint16_t* str) { _Myres = 0xF; _Mysize = 0; _bx._buffer[0] = '\0'; _bx._buffer[1] = '\0'; auto len = strlenU16(str); if (len <= 0xF) { memcpy((void*)&_bx._buffer, str, len + 2); _Mysize = (uint32_t)(len); } else { if (is_short() || capacity() < len + 2) { auto new_buf = g_userHeap.Alloc(len + 2); memset((void*)(new_buf), 0, len + 2); memcpy((void*)(new_buf), (const void*)(str), len + 2); _bx._str = new_buf; _Myres = len + 2; } else { memcpy((void*)_bx._str.get(), (void*)str, len + 2); } _Mysize = len; } } ~wstring() { if (!is_short()) g_userHeap.Free((void*)_bx._str.get()); _Myres = 0xF; _Mysize = 0; _bx._buffer[0] = '\0'; _bx._buffer[1] = '\0'; } bool operator==(const uint16_t* str) const { return strcmpU16(c_str(), str, false, true); } bool operator==(xpointer str) const { return strcmpU16(c_str(), str); } }; }; ================================================ FILE: MarathonRecomp/app.cpp ================================================ #include "app.h" #include #include #include #include #include #include #include #include #include #include #include static std::thread::id g_mainThreadId = std::this_thread::get_id(); void App::Restart(std::vector restartArgs) { os::process::StartProcess(os::process::GetExecutablePath(), restartArgs, os::process::GetWorkingDirectory()); Exit(); } void App::Exit() { Config::Save(); #ifdef _WIN32 timeEndPeriod(1); #endif std::_Exit(0); } // Sonicteam::AppMarathon::AppMarathon PPC_FUNC_IMPL(__imp__sub_8262A568); PPC_FUNC(sub_8262A568) { App::s_isInit = true; App::s_isMissingDLC = true; App::s_language = Config::Language; Sonicteam::Globals::Init(); Registry::Save(); struct RenderConfig { be Width; be Height; }; auto pRenderConfig = reinterpret_cast(g_memory.Translate(ctx.r4.u32)); pRenderConfig->Width = Video::s_viewportWidth; pRenderConfig->Height = Video::s_viewportHeight; auto pAudioEngine = Sonicteam::AudioEngineXenon::GetInstance(); pAudioEngine->m_MusicVolume = Config::MusicVolume * Config::MasterVolume; pAudioEngine->m_EffectsVolume = Config::EffectsVolume * Config::MasterVolume; LOGFN_UTILITY("Changed resolution: {}x{}", pRenderConfig->Width.get(), pRenderConfig->Height.get()); __imp__sub_8262A568(ctx, base); App::s_pApp = (Sonicteam::AppMarathon*)g_memory.Translate(ctx.r3.u32); InitPatches(); } // Sonicteam::DocMarathonState::Update PPC_FUNC_IMPL(__imp__sub_825EA610); PPC_FUNC(sub_825EA610) { Video::WaitOnSwapChain(); // Correct small delta time errors. if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX) { double targetDeltaTime = 1.0 / Config::FPS; if (abs(ctx.f1.f64 - targetDeltaTime) < 0.00001) ctx.f1.f64 = targetDeltaTime; } App::s_deltaTime = ctx.f1.f64; App::s_time += App::s_deltaTime; // This function can also be called by the loading thread, // which SDL does not like. To prevent the OS from thinking // the process is unresponsive, we will flush while waiting // for the pipelines to finish compiling in video.cpp. if (std::this_thread::get_id() == g_mainThreadId) { SDL_PumpEvents(); SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); GameWindow::Update(); } // Allow variable FPS when config is not 60 FPS. App::s_pApp->m_pDoc->m_VFrame = Config::FPS != 60; AudioPatches::Update(App::s_deltaTime); __imp__sub_825EA610(ctx, base); } PPC_FUNC_IMPL(__imp__sub_82582648); PPC_FUNC(sub_82582648) { struct File { public: MARATHON_INSERT_PADDING(4); xpointer pFilePath; MARATHON_INSERT_PADDING(0x0C); be Length; be Capacity; }; auto pFile = reinterpret_cast(base + ctx.r5.u32); if (pFile->pFilePath && pFile->Length > 0) LOGFN_UTILITY("Loading file: {}", pFile->pFilePath.get()); __imp__sub_82582648(ctx, base); } #if _DEBUG // Sonicteam::SoX::Thread::Thread PPC_FUNC_IMPL(__imp__sub_825867A8); PPC_FUNC(sub_825867A8) { auto pThreadName = (const char*)g_memory.Translate(ctx.r4.u32); os::logger::Log(fmt::format("Created thread: {}", pThreadName), os::logger::ELogType::Utility, "Sonicteam::SoX::Thread"); __imp__sub_825867A8(ctx, base); } #endif PPC_FUNC_IMPL(__imp__sub_82744840); PPC_FUNC(sub_82744840) { LOG_UTILITY("RenderFrame"); __imp__sub_82744840(ctx, base); } // Sonicteam::SpanverseHeap::Alloc PPC_FUNC_IMPL(__imp__sub_825E7918); PPC_FUNC(sub_825E7918) { #if _DEBUG os::logger::Log(fmt::format("Allocated {} bytes", ctx.r3.u32), os::logger::ELogType::Utility, "Sonicteam::SpanverseHeap"); #endif // This function checks if R4 is non-zero // to allow an allocation, but it's always // passed in as zero. ctx.r4.u32 = 1; __imp__sub_825E7918(ctx, base); } // Sonicteam::SpanverseHeap::Free PPC_FUNC_IMPL(__imp__sub_825E7958); PPC_FUNC(sub_825E7958) { #if _DEBUG os::logger::Log(fmt::format("Freed {:08X}", ctx.r3.u32), os::logger::ELogType::Utility, "Sonicteam::SpanverseHeap"); #endif // This function checks if R4 is non-zero // to allow a free, but it's always // passed in as zero. ctx.r4.u32 = 1; __imp__sub_825E7958(ctx, base); } #if _DEBUG PPC_FUNC_IMPL(__imp__sub_825822D0); PPC_FUNC(sub_825822D0) { LOG_UTILITY("!!!"); __imp__sub_825822D0(ctx, base); } #endif ================================================ FILE: MarathonRecomp/app.h ================================================ #pragma once #include #include class App { public: static inline bool s_isInit; static inline bool s_isSkipLogos; static inline bool s_isMissingDLC; static inline bool s_isLoading; static inline bool s_isSaving; static inline bool s_isSaveDataCorrupt; static inline Sonicteam::AppMarathon* s_pApp; static inline EPlayerCharacter s_playerCharacter; static inline ELanguage s_language; static inline double s_deltaTime; static inline double s_time = 0.0; // How much time elapsed since the game started. static void Restart(std::vector restartArgs = {}); static void Exit(); }; ================================================ FILE: MarathonRecomp/apu/audio.cpp ================================================ #include #include #include "audio.h" #include #define AUDIO_DRIVER_KEY (uint32_t)('DAUD') // Use to dump raw audio captures to the game folder. //#define AUDIO_DUMP_SAMPLES_PATH "audio.pcm" #ifdef AUDIO_DUMP_SAMPLES_PATH std::ofstream g_audioDumpStream; #endif uint32_t XAudioRegisterRenderDriverClient(be* callback, be* driver) { #ifdef AUDIO_DUMP_SAMPLES_PATH g_audioDumpStream.open(AUDIO_DUMP_SAMPLES_PATH, std::ios::binary); #endif *driver = AUDIO_DRIVER_KEY; XAudioRegisterClient(g_memory.FindFunction(*callback), callback[1]); return 0; } uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver) { return 0; } uint32_t XAudioSubmitRenderDriverFrame(uint32_t driver, void* samples) { #ifdef AUDIO_DUMP_SAMPLES_PATH static uint32_t xaudioSamplesBuffer[XAUDIO_NUM_SAMPLES * XAUDIO_NUM_CHANNELS]; for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) { for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++) { xaudioSamplesBuffer[i * XAUDIO_NUM_CHANNELS + j] = ByteSwap(((uint32_t *)samples)[j * XAUDIO_NUM_SAMPLES + i]); } } g_audioDumpStream.write((const char *)(xaudioSamplesBuffer), sizeof(xaudioSamplesBuffer)); #endif XAudioSubmitFrame(samples); return 0; } ================================================ FILE: MarathonRecomp/apu/audio.h ================================================ #pragma once #define XAUDIO_SAMPLES_HZ 48000 #define XAUDIO_NUM_CHANNELS 6 #define XAUDIO_SAMPLE_BITS 32 // Number of samples in a frame #define XAUDIO_NUM_SAMPLES 256 void XAudioInitializeSystem(); void XAudioRegisterClient(PPCFunc* callback, uint32_t param); void XAudioSubmitFrame(void* samples); void XAudioConfigValueChangedCallback(class IConfigDef* configDef); uint32_t XAudioRegisterRenderDriverClient(be* callback, be* driver); uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver); uint32_t XAudioSubmitRenderDriverFrame(uint32_t driver, void* samples); ================================================ FILE: MarathonRecomp/apu/driver/sdl2_driver.cpp ================================================ #include #include #include #include #include #include static PPCFunc* g_clientCallback{}; static uint32_t g_clientCallbackParam{}; // pointer in guest memory static SDL_AudioDeviceID g_audioDevice{}; static bool g_downMixToStereo; static void CreateAudioDevice() { if (g_audioDevice != NULL) SDL_CloseAudioDevice(g_audioDevice); bool surround = Config::ChannelConfiguration == EChannelConfiguration::Surround; int allowedChanges = surround ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0; SDL_AudioSpec desired{}, obtained{}; desired.freq = XAUDIO_SAMPLES_HZ; desired.format = AUDIO_F32SYS; desired.channels = surround ? XAUDIO_NUM_CHANNELS : 2; desired.samples = XAUDIO_NUM_SAMPLES; g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, allowedChanges); if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS) // This check may fail only when surround sound is enabled. { SDL_CloseAudioDevice(g_audioDevice); g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); } if (!g_audioDevice) LOGFN_ERROR("Failed to open audio device: {}", SDL_GetError()); g_downMixToStereo = (obtained.channels == 2); } void XAudioInitializeSystem() { #ifdef _WIN32 // Force wasapi on Windows. SDL_setenv("SDL_AUDIODRIVER", "wasapi", true); #endif SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback"); SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Marathon Recompiled"); if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { LOGFN_ERROR("Failed to init audio subsystem: {}", SDL_GetError()); return; } CreateAudioDevice(); } static std::unique_ptr g_audioThread; static volatile bool g_audioThreadShouldExit; static void AudioThread() { using namespace std::chrono_literals; GuestThreadContext ctx(0); size_t channels = g_downMixToStereo ? 2 : XAUDIO_NUM_CHANNELS; while (!g_audioThreadShouldExit) { uint32_t queuedAudioSize = SDL_GetQueuedAudioSize(g_audioDevice); constexpr size_t MAX_LATENCY = 10; const size_t callbackAudioSize = channels * XAUDIO_NUM_SAMPLES * sizeof(float); if ((queuedAudioSize / callbackAudioSize) <= MAX_LATENCY) { ctx.ppcContext.r3.u32 = g_clientCallbackParam; g_clientCallback(ctx.ppcContext, g_memory.base); } auto now = std::chrono::steady_clock::now(); constexpr auto INTERVAL = 1000000000ns * XAUDIO_NUM_SAMPLES / XAUDIO_SAMPLES_HZ; auto next = now + (INTERVAL - now.time_since_epoch() % INTERVAL); std::this_thread::sleep_for(std::chrono::floor(next - now)); while (std::chrono::steady_clock::now() < next) std::this_thread::yield(); } } static void CreateAudioThread() { SDL_PauseAudioDevice(g_audioDevice, 0); g_audioThreadShouldExit = false; g_audioThread = std::make_unique(AudioThread); } void XAudioRegisterClient(PPCFunc* callback, uint32_t param) { auto* pClientParam = static_cast(g_userHeap.Alloc(sizeof(param))); ByteSwapInplace(param); *pClientParam = param; g_clientCallbackParam = g_memory.MapVirtual(pClientParam); g_clientCallback = callback; CreateAudioThread(); } void XAudioSubmitFrame(void* samples) { auto floatSamples = reinterpret_cast*>(samples); auto volume = Config::MasterVolume.Value; if (Config::MuteOnFocusLost && !GameWindow::s_isFocused) volume = 0.0f; if (g_downMixToStereo) { // 0: left 1.0f, right 0.0f // 1: left 0.0f, right 1.0f // 2: left 0.75f, right 0.75f // 3: left 0.0f, right 0.0f // 4: left 1.0f, right 0.0f // 5: left 0.0f, right 1.0f std::array audioFrames; for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) { float ch0 = floatSamples[0 * XAUDIO_NUM_SAMPLES + i]; float ch1 = floatSamples[1 * XAUDIO_NUM_SAMPLES + i]; float ch2 = floatSamples[2 * XAUDIO_NUM_SAMPLES + i]; float ch3 = floatSamples[3 * XAUDIO_NUM_SAMPLES + i]; float ch4 = floatSamples[4 * XAUDIO_NUM_SAMPLES + i]; float ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i]; float samp0 = (ch0 + ch2 * 0.75f + ch4) * volume; float samp1 = (ch1 + ch2 * 0.75f + ch5) * volume; audioFrames[i * 2 + 0] = isnan(samp0) ? 0.0f : samp0; audioFrames[i * 2 + 1] = isnan(samp1) ? 0.0f : samp1; } SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames)); } else { std::array audioFrames; for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) { for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++) { float samp = floatSamples[j * XAUDIO_NUM_SAMPLES + i] * volume; audioFrames[i * 2 + j] = isnan(samp) ? 0.0f : samp; } } SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames)); } } void XAudioConfigValueChangedCallback(IConfigDef* configDef) { if (configDef == &Config::ChannelConfiguration) { if (g_audioThread->joinable()) { g_audioThreadShouldExit = true; g_audioThread->join(); } CreateAudioDevice(); CreateAudioThread(); } } ================================================ FILE: MarathonRecomp/apu/embedded_player.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include enum class EmbeddedSound { WindowOpen, WindowClose, Cursor, Deside, Move, MainDeside, CannotDeside, Count }; struct EmbeddedSoundData { Mix_Chunk* chunk{}; }; static std::array g_embeddedSoundData = {}; static const std::unordered_map g_embeddedSoundMap = { { "window_open", EmbeddedSound::WindowOpen }, { "window_close", EmbeddedSound::WindowClose }, { "cursor", EmbeddedSound::Cursor }, { "deside", EmbeddedSound::Deside }, { "move", EmbeddedSound::Move }, { "main_deside", EmbeddedSound::MainDeside }, { "cannot_deside", EmbeddedSound::CannotDeside }, }; static size_t g_channelIndex; static void PlayEmbeddedSound(EmbeddedSound s) { EmbeddedSoundData &data = g_embeddedSoundData[size_t(s)]; if (data.chunk == nullptr) { // The sound hasn't been created yet, create it and pick it. const void *soundData = nullptr; size_t soundDataSize = 0; switch (s) { case EmbeddedSound::WindowOpen: soundData = g_window_open; soundDataSize = sizeof(g_window_open); break; case EmbeddedSound::WindowClose: soundData = g_window_close; soundDataSize = sizeof(g_window_close); break; case EmbeddedSound::Cursor: soundData = g_cursor; soundDataSize = sizeof(g_cursor); break; case EmbeddedSound::Deside: soundData = g_deside; soundDataSize = sizeof(g_deside); break; case EmbeddedSound::Move: soundData = g_move; soundDataSize = sizeof(g_move); break; case EmbeddedSound::MainDeside: soundData = g_main_deside; soundDataSize = sizeof(g_main_deside); break; case EmbeddedSound::CannotDeside: soundData = g_cannot_deside; soundDataSize = sizeof(g_cannot_deside); break; default: assert(false && "Unknown embedded sound."); return; } data.chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(soundData, soundDataSize), 1); } Mix_VolumeChunk(data.chunk, (Config::MasterVolume * Config::EffectsVolume * EmbeddedPlayer::EFFECTS_VOLUME) * MIX_MAX_VOLUME); Mix_PlayChannel(g_channelIndex % MIX_CHANNELS, data.chunk, 0); ++g_channelIndex; } static Mix_Music* g_installerMusic; void EmbeddedPlayer::Init() { Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, 2, 4096); g_installerMusic = Mix_LoadMUS_RW(SDL_RWFromConstMem(g_installer_music, sizeof(g_installer_music)), 1); s_isActive = true; } void EmbeddedPlayer::Play(const char *name) { assert(s_isActive && "Playback shouldn't be requested if the Embedded Player isn't active."); auto it = g_embeddedSoundMap.find(name); if (it == g_embeddedSoundMap.end()) { return; } PlayEmbeddedSound(it->second); } void EmbeddedPlayer::PlayMusic() { if (!Mix_PlayingMusic()) { Mix_PlayMusic(g_installerMusic, INT_MAX); Mix_VolumeMusic(Config::MasterVolume * Config::MusicVolume * MUSIC_VOLUME * MIX_MAX_VOLUME); } } void EmbeddedPlayer::FadeOutMusic() { if (Mix_PlayingMusic()) Mix_FadeOutMusic(1000); } void EmbeddedPlayer::Shutdown() { for (EmbeddedSoundData &data : g_embeddedSoundData) { if (data.chunk != nullptr) Mix_FreeChunk(data.chunk); } Mix_HaltMusic(); Mix_FreeMusic(g_installerMusic); Mix_CloseAudio(); Mix_Quit(); s_isActive = false; } ================================================ FILE: MarathonRecomp/apu/embedded_player.h ================================================ #pragma once struct EmbeddedPlayer { // Arbitrarily picked volume to match the mixing in the original game. static constexpr float MUSIC_VOLUME = 0.25f; static constexpr float EFFECTS_VOLUME = 0.2f; static inline bool s_isActive = false; static void Init(); static void Play(const char *name); static void PlayMusic(); static void FadeOutMusic(); static void Shutdown(); }; ================================================ FILE: MarathonRecomp/apu/xma_decoder.cpp ================================================ /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2024 Xenia Canary. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ // Almost all decoding code is from Xenia Canary, so leave the copyright here #include "xma_decoder.h" // #define ENABLE_DEBUG_XMA_DECODER #ifdef ENABLE_DEBUG_XMA_DECODER #define debug_printf(...) printf(__VA_ARGS__) #else #define debug_printf(...) #endif constexpr uint32_t kOutputBytesPerBlock = 256; constexpr uint32_t kBitsPerPacketHeader = 32; constexpr uint32_t kBitsPerPacket = kBytesPerPacket * 8; constexpr uint32_t kMaxFrameLength = 0x7FFF; constexpr uint32_t kBitsPerFrameHeader = 15; constexpr uint32_t kMaxFrameSizeinBits = 0x4000 - kBitsPerPacketHeader; uint32_t XMAPlaybackGetFrameOffsetFromPacketHeader(uint32_t header) { uint32_t result = 0; if (header != 0x7FFF) { return ((header >> 11) & 0x7FFF) + 32; } return result; } int16_t GetPacketNumber(size_t size, size_t bitOffset) { if (bitOffset < kBitsPerPacketHeader) { return -1; } if (bitOffset >= (size << 3)) { return -1; } size_t byteOffset = bitOffset >> 3; size_t packetNumber = byteOffset / kBytesPerPacket; return (int16_t)packetNumber; } static uint32_t GetPacketFrameOffset(const uint8_t *packet) { uint32_t val = (uint16_t)(((packet[0] & 0x3) << 13) | (packet[1] << 5) | (packet[2] >> 3)); return val + 32; } struct kPacketInfo { uint8_t frameCount; uint8_t currentFrame; uint32_t currentFrameSize; const bool IsLastFrameInPacket() const { return currentFrame == frameCount - 1; } }; kPacketInfo GetPacketInfo(uint8_t *packet, uint32_t frameOffset) { kPacketInfo packetInfo = {}; const uint32_t firstFrameOffset = GetPacketFrameOffset(packet); BitStream stream(packet, kBitsPerPacket); stream.SetOffset(firstFrameOffset); // Handling of split frame if (frameOffset < firstFrameOffset) { packetInfo.currentFrame = 0; packetInfo.currentFrameSize = firstFrameOffset - frameOffset; } while (true) { if (stream.BitsRemaining() < kBitsPerFrameHeader) { break; } const uint64_t frameSize = stream.Peek(kBitsPerFrameHeader); if (frameSize == kMaxFrameLength) { break; } if (stream.offset_bits() == frameOffset) { packetInfo.currentFrame = packetInfo.frameCount; packetInfo.currentFrameSize = (uint32_t)frameSize; } packetInfo.frameCount++; if (frameSize > stream.BitsRemaining()) { // Last frame. break; } stream.Advance(frameSize - 1); // Read the trailing bit to see if frames follow if (stream.Read(1) == 0) { break; } } return packetInfo; } uint8_t *GetNextPacket(XmaPlayback *playback, uint32_t nextPacketIndex, uint32_t currentInputPacketCount) { if (nextPacketIndex < currentInputPacketCount) { uint8_t *currentInputBuffer = (uint8_t *)g_memory.Translate(playback->GetCurrentInputBufferAddress()); return currentInputBuffer + nextPacketIndex * kBytesPerPacket; } const uint8_t nextBufferIndex = playback->currentBuffer ^ 1; if (!playback->IsInputBufferValid(nextBufferIndex)) { return nullptr; } const uint32_t nextBufferAddress = playback->GetInputBufferAddress(nextBufferIndex); if (!nextBufferAddress) { // This should never occur, but there is always a chance debug_printf("XmaContext: Buffer is marked as valid, but doesn't have valid pointer!\n"); return nullptr; } return (uint8_t *)g_memory.Translate(nextBufferAddress); } uint8_t GetPacketSkipCount(const uint8_t *packet) { return packet[3]; } uint32_t GetAmountOfBitsToRead(const uint32_t remainingStreamBits, const uint32_t frameSize) { return std::min(remainingStreamBits, frameSize); } template T clamp_float(T value, T minValue, T maxValue) { float clampedToMin = std::isgreater(value, minValue) ? value : minValue; return std::isless(clampedToMin, maxValue) ? clampedToMin : maxValue; } const uint32_t GetNextPacketReadOffset(uint8_t *buffer, uint32_t nextPacketIndex, uint32_t currentInputPacketCount) { if (nextPacketIndex >= currentInputPacketCount) { return kBitsPerPacketHeader; } uint8_t *nextPacket = buffer + (nextPacketIndex * kBytesPerPacket); const uint32_t packetFrameOffset = GetPacketFrameOffset(nextPacket); if (packetFrameOffset > kMaxFrameSizeinBits) { const uint32_t offset = GetNextPacketReadOffset(buffer, nextPacketIndex + 1, currentInputPacketCount); return offset; } const uint32_t newInputBufferOffset = (nextPacketIndex * kBitsPerPacket) + packetFrameOffset; return newInputBufferOffset; } void SwapInputBuffer(XmaPlayback *playback) { // No more frames. if (playback->currentBuffer == 0) { playback->inputBuffer1Valid = 0; } else { playback->inputBuffer2Valid = 0; } playback->currentBuffer ^= 1; playback->inputBufferReadOffset = kBitsPerPacketHeader; } void UpdateLoopStatus(XmaPlayback *playback) { if (playback->numLoops == 0) { return; } const uint32_t loop_start = std::max(kBitsPerPacketHeader, playback->loopStartOffset); const uint32_t loop_end = std::max(kBitsPerPacketHeader, playback->loopEndOffset); if (playback->inputBufferReadOffset != loop_end) { return; } playback->inputBufferReadOffset = loop_start; if (playback->numLoops != 255) { playback->numLoops--; } } void Decode(XmaPlayback *playback) { if (!playback->IsAnyInputBufferValid()) { return; } if (playback->currentFrameRemainingSubframes > 0) { return; } uint8_t *currentInputBuffer = playback->GetCurrentInputBuffer(); playback->inputBuffer.fill(0); UpdateLoopStatus(playback); // TODO const uint32_t currentInputSize = playback->GetCurrentInputBufferPacketCount() * kBytesPerPacket; uint32_t currentInputPacketCount = currentInputSize / kBytesPerPacket; int16_t packetIndex = GetPacketNumber(currentInputSize, playback->inputBufferReadOffset); if (packetIndex == -1) { return; } uint8_t *packet = currentInputBuffer + (packetIndex * kBytesPerPacket); const uint32_t frameOffset = GetPacketFrameOffset(packet); if (playback->inputBufferReadOffset < frameOffset) { playback->inputBufferReadOffset = frameOffset; } uint32_t relativeOffset = playback->inputBufferReadOffset % kBitsPerPacket; const kPacketInfo packetInfo = GetPacketInfo(packet, relativeOffset); const uint32_t packetToSkip = GetPacketSkipCount(packet) + 1; const uint32_t nextPacketIndex = packetIndex + packetToSkip; BitStream stream = BitStream(currentInputBuffer, (packetIndex + 1) * kBitsPerPacket); stream.SetOffset(playback->inputBufferReadOffset); const uint64_t bitsToCopy = GetAmountOfBitsToRead((uint32_t)stream.BitsRemaining(), packetInfo.currentFrameSize); if (bitsToCopy == 0) { SwapInputBuffer(playback); return; } if (packetInfo.IsLastFrameInPacket()) { // Frame is a split frame if (stream.BitsRemaining() < packetInfo.currentFrameSize) { const uint8_t *nextPacket = GetNextPacket(playback, nextPacketIndex, currentInputPacketCount); if (!nextPacket) { // Error path // Decoder probably should return error here // Not sure what error code should be returned // data->error_status = 4; __builtin_debugtrap(); return; } // Copy next packet to buffer std::memcpy(playback->inputBuffer.data() + kBytesPerPacketData, nextPacket + kBytesPerPacketHeader, kBytesPerPacketData); } } std::memcpy(playback->inputBuffer.data(), packet + kBytesPerPacketHeader, kBytesPerPacketData); stream = BitStream(playback->inputBuffer.data(), (kBitsPerPacket - kBitsPerPacketHeader) * 2); stream.SetOffset(relativeOffset - kBitsPerPacketHeader); playback->xmaFrame.fill(0); const uint32_t paddingStart = static_cast(stream.Copy(playback->xmaFrame.data() + 1, packetInfo.currentFrameSize)); playback->rawFrame.fill(0); playback->av_packet_->data = playback->xmaFrame.data(); playback->av_packet_->size = static_cast(1 + ((paddingStart + packetInfo.currentFrameSize) / 8) + (((paddingStart + packetInfo.currentFrameSize) % 8) ? 1 : 0)); auto paddingEnd = playback->av_packet_->size * 8 - (8 + paddingStart + packetInfo.currentFrameSize); playback->xmaFrame[0] = ((paddingStart & 7) << 5) | ((paddingEnd & 7) << 2); auto ret = avcodec_send_packet(playback->codec_ctx, playback->av_packet_); if (ret < 0) { debug_printf("Error sending packet for decoding: %s\n", av_err2str(ret)); } ret = avcodec_receive_frame(playback->codec_ctx, playback->av_frame_); if (ret < 0) { debug_printf("Error receiving frame from decoder: %s\n", av_err2str(ret)); } constexpr float scale = (1 << 15) - 1; auto out = reinterpret_cast(playback->rawFrame.data()); auto samples = reinterpret_cast(&playback->av_frame_->data); uint32_t o = 0; if (playback->av_frame_->nb_samples != 0) { for (uint32_t i = 0; i < kSamplesPerFrame; i++) { for (uint32_t j = 0; j < playback->av_frame_->ch_layout.nb_channels; j++) { // Select the appropriate array based on the current channel. auto in = reinterpret_cast(samples[j]); // Raw samples sometimes aren't within [-1, 1] float scaledSample = clamp_float(in[i], -1.0f, 1.0f) * scale; // Convert the sample and output it in big endian. auto sample = static_cast(scaledSample); out[o++] = ByteSwap(sample); } } } playback->currentFrameRemainingSubframes = 4 * playback->channelCount; if (!packetInfo.IsLastFrameInPacket()) { const uint32_t nextFrameOffset = (playback->inputBufferReadOffset + bitsToCopy) % kBitsPerPacket; playback->inputBufferReadOffset = (packetIndex * kBitsPerPacket) + nextFrameOffset; return; } uint32_t nextInputOffset = GetNextPacketReadOffset(currentInputBuffer, nextPacketIndex, currentInputPacketCount); if (nextInputOffset == kBitsPerPacketHeader) { SwapInputBuffer(playback); // We're at start of next buffer // Any frames in this packet decoder should go to the first frame in the packet. // If it doesn't have any frames, then it should immediately go to the next packet. if (playback->IsAnyInputBufferValid()) { nextInputOffset = GetPacketFrameOffset((uint8_t *)g_memory.Translate( playback->GetCurrentInputBufferAddress())); if (nextInputOffset > kMaxFrameSizeinBits) { SwapInputBuffer(playback); return; } } else { // HACK SwapInputBuffer(playback); } } playback->inputBufferReadOffset = nextInputOffset; } void Consume(XmaPlayback *playback) { if (!playback->currentFrameRemainingSubframes) { return; } const int8_t subframesToWrite = std::min((int8_t)playback->currentFrameRemainingSubframes, (int8_t)playback->subframes); const int8_t rawFrameReadOffset = ((kBytesPerFrameChannel / kOutputBytesPerBlock) * playback->channelCount) - playback->currentFrameRemainingSubframes; playback->outputRb.Write(playback->rawFrame.data() + (kOutputBytesPerBlock * rawFrameReadOffset), subframesToWrite * kOutputBytesPerBlock); playback->remainingSubframeBlocksInOutputBuffer -= subframesToWrite; playback->currentFrameRemainingSubframes -= subframesToWrite; } void DecoderThreadFunc(XmaPlayback *playback) { while (playback->isRunning) { std::unique_lock lock(playback->mutex); playback->cv.wait(lock, [&] { return (!playback->isRunning || (playback->outputBufferValid == 1 && playback->IsAnyInputBufferValid())) && !playback->isLocked.load(); }); if (!playback->outputBufferValid) continue; if (!playback->isRunning) break; lock.unlock(); const int32_t minimumSubframeDecodeCount = (playback->subframes * playback->channelCount) - 1; size_t outputCapacity = playback->outputBufferBlockCount * kOutputBytesPerBlock; const uint32_t outputReadOffset = playback->outputBufferReadOffset * kOutputBytesPerBlock; const uint32_t outputWriteOffset = playback->outputBufferWriteOffset * kOutputBytesPerBlock; playback->outputRb = RingBuffer((uint8_t *)g_memory.Translate(playback->outputBuffer), outputCapacity); playback->outputRb.set_read_offset(outputReadOffset); playback->outputRb.set_write_offset(outputWriteOffset); playback->remainingSubframeBlocksInOutputBuffer = (int32_t)playback->outputRb.write_count() / kOutputBytesPerBlock; if (minimumSubframeDecodeCount > playback->remainingSubframeBlocksInOutputBuffer) { playback->bAllowedToDecode = false; lock.lock(); continue; } while (playback->remainingSubframeBlocksInOutputBuffer >= minimumSubframeDecodeCount) { Decode(playback); Consume(playback); if (!playback->IsAnyInputBufferValid()) { break; } } playback->outputBufferWriteOffset = playback->outputRb.write_offset() / kOutputBytesPerBlock; playback->bAllowedToDecode = false; if (playback->outputRb.empty()) { playback->outputBufferValid = 0; } lock.lock(); } } uint32_t XMAPlaybackCreate(uint32_t streams, XMAPLAYBACKINIT *init, uint32_t flags, be *outPlayback) { const auto xmaPlayback = g_userHeap.AllocPhysical( init->sampleRate.get(), init->outputBufferSize.get(), init->channelCount, init->subframes); xmaPlayback->decoderThread = std::thread(DecoderThreadFunc, xmaPlayback); *outPlayback = g_memory.MapVirtual(xmaPlayback); return 0; } uint32_t XMAPlaybackRequestModifyLock(XmaPlayback *playback) { std::lock_guard lock(playback->mutex); playback->isLocked = true; return 0; } uint32_t XMAPlaybackWaitUntilModifyLockObtained(XmaPlayback *playback) { std::unique_lock lock(playback->mutex); playback->cv.wait(lock, [&playback] { return playback->isLocked.load(); }); return 0; } uint32_t XMAPlaybackQueryReadyForMoreData(XmaPlayback *playback, uint32_t stream) { return playback->inputBuffer1Valid == 0 || playback->inputBuffer2Valid == 0; } uint32_t XMAPlaybackIsIdle(XmaPlayback *playback, uint32_t stream) { return playback->inputBuffer1Valid == 0 && playback->inputBuffer2Valid == 0; } uint32_t XMAPlaybackQueryContextsAllocated(XmaPlayback *playback) { if (!playback) { return 0; } return 1; } uint32_t XMAPlaybackResumePlayback(XmaPlayback *playback) { std::lock_guard lock(playback->mutex); playback->isLocked = false; playback->cv.notify_one(); return 0; } uint32_t XMAPlaybackQueryInputDataPending(XmaPlayback *playback, uint32_t stream, uint32_t data) { if (playback->inputBuffer1Valid && playback->inputBuffer1 == data) { return 1; } if (playback->inputBuffer2Valid && playback->inputBuffer2 == data) { return 1; } return 0; } uint32_t XMAPlaybackGetErrorBits(XmaPlayback *playback, uint32_t stream) { return 0; } uint32_t XMAPlaybackSubmitData(XmaPlayback *playback, uint32_t stream, uint32_t data, uint32_t dataSize) { if (!playback->isLocked) { return 1; } std::lock_guard lock(playback->mutex); uint32_t packetCount = dataSize >> 11; uint32_t validBuffer = playback->inputBuffer1Valid | (playback->inputBuffer2Valid << 1); if (playback->inputBuffer1Valid == 1) { if (playback->inputBuffer2Valid == 1) { return 0x80070005; } playback->inputBuffer2 = data; playback->inputBuffer2Size = packetCount & 0xFFF; playback->inputBuffer2Valid = 1; } else { playback->inputBuffer1 = data; playback->inputBuffer1Size = packetCount & 0xFFF; playback->inputBuffer1Valid = 1; } if (!validBuffer) { uint8_t *currentInputBuffer = (uint8_t *)g_memory.Translate(data); uint32_t frameOffset = XMAPlaybackGetFrameOffsetFromPacketHeader(*currentInputBuffer); if (frameOffset) { playback->inputBufferReadOffset = frameOffset & 0x3FFFFFF; } } playback->bAllowedToDecode = true; playback->cv.notify_one(); return 0; } uint32_t XMAPlaybackQueryAvailableData(XmaPlayback *playback, uint32_t stream) { if (!playback->isLocked || (playback->inputBuffer1Valid == 0 && playback->inputBuffer2Valid == 0)) { return 0; } uint32_t partialBytesRead = playback->partialBytesRead; uint32_t writeBufferOffsetRead = playback->outputBufferReadOffset & 0x1F; uint32_t offsetWrite = playback->outputBufferWriteOffset; uint32_t sizeWrite = playback->outputBufferBlockCount & 0x1F; uint32_t availableBytes = 0; uint32_t isValidWrite = playback->outputBufferValid; if (partialBytesRead) { availableBytes = 256 - partialBytesRead; writeBufferOffsetRead++; isValidWrite = 1; } uint32_t availableBlocks = 0; if (offsetWrite <= writeBufferOffsetRead) { if (offsetWrite < writeBufferOffsetRead || !isValidWrite) { availableBlocks = sizeWrite - writeBufferOffsetRead; } } else { availableBlocks = offsetWrite - writeBufferOffsetRead; } uint32_t totalBytes = (availableBlocks << 8) + availableBytes; uint32_t bytesPerSample = playback->channelCount; return totalBytes >> bytesPerSample; } uint32_t XMAPlaybackAccessDecodedData(XmaPlayback *playback, uint32_t stream, uint32_t **data) { if (!playback->isLocked) return 0; uint32_t partialBytesRead = playback->partialBytesRead; uint32_t addr = reinterpret_cast( playback->outputBuffer + ((playback->outputBufferReadOffset << 8) & 0x1F00) + partialBytesRead); ; *data = (uint32_t *)__builtin_bswap32(addr); uint32_t writeBufferOffsetRead = playback->outputBufferReadOffset & 0x1F; uint32_t offsetWrite = playback->outputBufferWriteOffset; uint32_t sizeWrite = playback->outputBufferBlockCount & 0x1F; uint32_t availableBytes = 0; uint32_t isValidWrite = playback->outputBufferValid; if (partialBytesRead) { availableBytes = 256 - partialBytesRead; writeBufferOffsetRead++; isValidWrite = 1; } uint32_t availableBlocks = 0; if (offsetWrite <= writeBufferOffsetRead) { if (offsetWrite < writeBufferOffsetRead || !isValidWrite) { availableBlocks = sizeWrite - writeBufferOffsetRead; } } else { availableBlocks = offsetWrite - writeBufferOffsetRead; } uint32_t totalBytes = (availableBlocks << 8) + availableBytes; uint32_t bytesPerSample = playback->channelCount; return totalBytes >> bytesPerSample; } uint32_t XMAPlaybackConsumeDecodedData(XmaPlayback *playback, uint32_t stream, uint32_t maxSamples, uint32_t **data) { if (!playback->isLocked) { return 0; } uint32_t totalBytes = 0; uint32_t partialBytesRead = playback->partialBytesRead; uint32_t addr = reinterpret_cast( playback->outputBuffer + ((playback->outputBufferReadOffset << 8) & 0x1F00) + partialBytesRead); *data = (uint32_t *)__builtin_bswap32(addr); uint32_t bytesPerSample = playback->channelCount; uint32_t bytesDesired = maxSamples << bytesPerSample; if (partialBytesRead) { if (bytesDesired < 256 - partialBytesRead) { totalBytes = bytesDesired; playback->partialBytesRead += bytesDesired; bytesDesired = 0; } else { totalBytes = 256 - partialBytesRead; bytesDesired -= 256 - partialBytesRead; playback->partialBytesRead = 0; uint32_t writeIndex = (playback->outputBufferReadOffset + 1) & 0x1F; if (writeIndex >= (playback->outputBufferBlockCount & 0x1F)) { writeIndex = 0; } playback->outputBufferReadOffset = writeIndex; playback->outputBufferValid = 1; } } uint32_t writeIndex = playback->outputBufferReadOffset; uint32_t blocksToProcess = bytesDesired >> 8; uint32_t availableBlocks = 0; uint32_t writeSize = playback->outputBufferBlockCount; if (writeSize <= writeIndex) { if (writeSize < writeIndex || !playback->outputBufferValid) { availableBlocks = (playback->outputBufferBlockCount & 0x1F) - writeIndex; } } else { availableBlocks = writeSize - writeIndex; } if (blocksToProcess) { if (blocksToProcess > availableBlocks) { blocksToProcess = availableBlocks; } totalBytes += blocksToProcess << 8; availableBlocks -= blocksToProcess; writeIndex = (writeIndex + blocksToProcess) & 0x1F; if (writeIndex >= (playback->outputBufferBlockCount & 0x1F)) { writeIndex = 0; } playback->outputBufferReadOffset = writeIndex; playback->outputBufferValid = 1; } uint32_t remainingBytes = bytesDesired & 0xFF; if (remainingBytes && availableBlocks) { totalBytes += remainingBytes; playback->partialBytesRead = remainingBytes; } // playback->bAllowedToDecode = true; // playback->cv.notify_one(); uint32_t samplesConsumed = totalBytes >> bytesPerSample; playback->streamPosition += samplesConsumed; return samplesConsumed; } uint32_t XMAPlaybackQueryModifyLockObtained(XmaPlayback *playback) { debug_printf("XMAPlaybackQueryModifyLockObtained %x\n", playback); return playback->isLocked.load(); } uint32_t XMAPlaybackDestroy(XmaPlayback *playback) { debug_printf("XMAPlaybackDestroy %x\n", playback); return 0; } uint32_t XMAPlaybackFlushData(XmaPlayback *playback) { debug_printf("XMAPlaybackFlushData %x\n", playback); // __builtin_debugtrap(); return 0; } struct XMAPLAYBACKLOOP { be loopStartOffset; be loopEndOffset; uint8_t loopSubframeEnd; uint8_t loopSubframeSkip; uint8_t numLoops; uint8_t reserved; }; uint32_t XmaPlaybackSetLoop(XmaPlayback *playback, uint32_t streamIndex, XMAPLAYBACKLOOP *loop) { playback->numLoops = loop->numLoops; playback->loopSubframeEnd = loop->loopSubframeEnd; playback->loopSubframeSkip = loop->loopSubframeSkip; playback->loopStartOffset = loop->loopStartOffset.get() & 0x3FFFFFF; playback->loopEndOffset = loop->loopEndOffset.get() & 0x3FFFFFF; return 0; } uint32_t XMAPlaybackGetRemainingLoopCount(XmaPlayback *playback) { debug_printf("XMAPlaybackGetRemainingLoopCount %x\n", playback); __builtin_debugtrap(); return 0; } uint32_t XMAPlaybackGetStreamPosition(XmaPlayback *playback) { return playback->streamPosition; } uint32_t XMAPlaybackSetDecodePosition(XmaPlayback *playback, uint32_t streamIndex, uint32_t bitOffset, uint32_t subframe) { playback->inputBufferReadOffset = bitOffset & 0x3FFFFFF; playback->numSubframesToSkip = subframe & 0x7; return 0; } uint32_t XMAPlaybackRewindDecodePosition(XmaPlayback *playback, uint32_t streamIndex, uint32_t numSamples) { uint32_t shift = 7 - (1 != 0); uint32_t adjustedSamples = numSamples >> shift; uint32_t writeSize = playback->outputBufferBlockCount & 0x1F; uint32_t newOffset; if (adjustedSamples >= writeSize) { newOffset = playback->inputBufferReadOffset & 0x3FFFFFF; playback->outputBufferValid = 1; playback->outputBufferWriteOffset = newOffset >> 27; return 0; } newOffset = (playback->outputBufferWriteOffset - adjustedSamples + writeSize) & 0x1F; if (newOffset >= writeSize) { newOffset -= writeSize; } playback->outputBufferValid = 1; playback->outputBufferWriteOffset = newOffset; return 1; } uint32_t XMAPlaybackQueryCurrentPosition(XmaPlayback *playback) { debug_printf("XMAPlaybackQueryCurrentPosition %x\n", playback); __builtin_debugtrap(); return 0; } GUEST_FUNCTION_HOOK(sub_8255C090, XMAPlaybackCreate); GUEST_FUNCTION_HOOK(sub_8255CC48, XMAPlaybackRequestModifyLock); GUEST_FUNCTION_HOOK(sub_8255CCC8, XMAPlaybackWaitUntilModifyLockObtained); GUEST_FUNCTION_HOOK(sub_8255C4D0, XMAPlaybackQueryReadyForMoreData); GUEST_FUNCTION_HOOK(sub_8255C520, XMAPlaybackIsIdle); GUEST_FUNCTION_HOOK(sub_8255C388, XMAPlaybackQueryContextsAllocated); GUEST_FUNCTION_HOOK(sub_8255CF10, XMAPlaybackResumePlayback); GUEST_FUNCTION_HOOK(sub_8255C470, XMAPlaybackQueryInputDataPending); GUEST_FUNCTION_HOOK(sub_8255C9A0, XMAPlaybackGetErrorBits); GUEST_FUNCTION_HOOK(sub_8255C398, XMAPlaybackSubmitData); GUEST_FUNCTION_HOOK(sub_8255C578, XMAPlaybackQueryAvailableData); GUEST_FUNCTION_HOOK(sub_8255C7A8, XMAPlaybackAccessDecodedData); GUEST_FUNCTION_HOOK(sub_8255C5F0, XMAPlaybackConsumeDecodedData); GUEST_FUNCTION_HOOK(sub_8255CD90, XMAPlaybackQueryModifyLockObtained); GUEST_FUNCTION_HOOK(sub_8255C8D8, XMAPlaybackFlushData); GUEST_FUNCTION_HOOK(sub_8255C9D8, XmaPlaybackSetLoop); GUEST_FUNCTION_HOOK(sub_8255CA50, XMAPlaybackGetRemainingLoopCount); GUEST_FUNCTION_HOOK(sub_8255CA90, XMAPlaybackGetStreamPosition); GUEST_FUNCTION_HOOK(sub_8255CB20, XMAPlaybackSetDecodePosition); GUEST_FUNCTION_HOOK(sub_8255C850, XMAPlaybackRewindDecodePosition); GUEST_FUNCTION_HOOK(sub_8255CAB0, XMAPlaybackQueryCurrentPosition); GUEST_FUNCTION_HOOK(sub_8255C2C0, XMAPlaybackDestroy); ================================================ FILE: MarathonRecomp/apu/xma_decoder.h ================================================ #pragma once #include #include #include #include #include extern "C" { #include } struct XMAPLAYBACKINIT { be sampleRate; be outputBufferSize; uint8_t channelCount; uint8_t subframes; }; constexpr uint32_t kBytesPerPacket = 2048; constexpr uint32_t kBytesPerPacketHeader = 4; constexpr uint32_t kBytesPerPacketData = kBytesPerPacket - kBytesPerPacketHeader; constexpr uint32_t kBytesPerSample = 2; constexpr uint32_t kSamplesPerFrame = 512; constexpr uint32_t kBytesPerFrameChannel = kSamplesPerFrame * kBytesPerSample; struct XmaPlayback { uint32_t sampleRate; uint32_t outputBufferSize; uint32_t channelCount; uint32_t subframes; uint32_t outputBuffer; uint32_t currentDataSize; uint32_t partialBytesRead = 0; uint32_t streamPosition = 0; bool bAllowedToDecode = false; // ffmpeg AVCodecContext *codec_ctx = nullptr; const AVCodec *codec = nullptr; AVPacket *av_packet_ = nullptr; AVFrame *av_frame_ = nullptr; // xenia std::array inputBuffer; std::array xmaFrame; std::array rawFrame; uint32_t outputBufferBlockCount = 0; uint32_t outputBufferReadOffset = 0; uint32_t outputBufferWriteOffset = 0; int32_t remainingSubframeBlocksInOutputBuffer = 0; uint8_t currentFrameRemainingSubframes = 0; uint32_t inputBufferReadOffset = 32; uint32_t numSubframesToSkip = 0; RingBuffer outputRb; uint32_t inputBuffer1 = 0; uint32_t inputBuffer2 = 0; size_t inputBuffer1Size = 0; size_t inputBuffer2Size = 0; uint32_t validInputBuffer = 0; uint32_t inputBuffer1Valid = 0; uint32_t inputBuffer2Valid = 0; uint32_t currentBuffer = 0; uint32_t outputBufferValid = 1; uint8_t numLoops = 0; uint8_t loopSubframeEnd = 0; uint8_t loopSubframeSkip = 0; uint32_t loopStartOffset = 0; uint32_t loopEndOffset = 0; std::thread decoderThread; std::mutex mutex; std::condition_variable cv; std::atomic isLocked { false }; std::atomic isRunning { true }; XmaPlayback(uint32_t sampleRate, uint32_t outputBufferSize, uint32_t channelCount, uint32_t subframes) : sampleRate(sampleRate), outputBufferSize(outputBufferSize), channelCount(channelCount), subframes(subframes), outputRb(nullptr, 0) { outputBufferBlockCount = (((channelCount * outputBufferSize) << 15) & 0x7C00000) >> 22; outputBuffer = g_memory.MapVirtual(g_userHeap.AllocPhysical((size_t)0x2000, 0)); codec = avcodec_find_decoder(AV_CODEC_ID_XMAFRAMES); if (!codec) { throw std::runtime_error("Decoder not found"); } codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { throw std::runtime_error("Failed to allocate codec context"); } codec_ctx->sample_rate = sampleRate; codec_ctx->ch_layout.nb_channels = channelCount; av_frame_ = av_frame_alloc(); if (!av_frame_) { throw std::runtime_error("Coulnd't allocate frame"); } if (int err = avcodec_open2(codec_ctx, codec, nullptr); err < 0) { throw std::runtime_error("Failed to open codec"); } av_packet_ = av_packet_alloc(); } const uint32_t GetInputBufferAddress(uint8_t bufferIndex) const { return bufferIndex == 0 ? inputBuffer1 : inputBuffer2; } const uint32_t GetCurrentInputBufferAddress() const { return GetInputBufferAddress(currentBuffer); } const uint32_t GetInputBufferPacketCount(uint8_t bufferIndex) const { return bufferIndex == 0 ? inputBuffer1Size : inputBuffer2Size; } const uint32_t GetCurrentInputBufferPacketCount() const { return GetInputBufferPacketCount(currentBuffer); } uint8_t *GetCurrentInputBuffer() { return (uint8_t *)g_memory.Translate(GetCurrentInputBufferAddress()); } bool IsInputBufferValid(uint8_t bufferIndex) const { return bufferIndex == 0 ? inputBuffer1Valid : inputBuffer2Valid; } bool IsCurrentInputBufferValid() const { return IsInputBufferValid(currentBuffer); } bool IsAnyInputBufferValid() const { return inputBuffer1Valid || inputBuffer2Valid; } ~XmaPlayback() { { std::lock_guard lock(mutex); isRunning = false; cv.notify_one(); } if (decoderThread.joinable()) { decoderThread.join(); } } }; ================================================ FILE: MarathonRecomp/cpu/guest_stack_var.h ================================================ #pragma once #include "ppc_context.h" #include // DO NOT use this type as anything other than a local variable. // This includes returning. It'll cause memory to leak in the guest stack! template class guest_stack_var { private: uint32_t m_ptr = NULL; uint32_t m_oldStackPtr = NULL; void AllocGuestStackMemory() { auto ctx = GetPPCContext(); m_oldStackPtr = ctx->r1.u32; m_ptr = (ctx->r1.u32 - sizeof(T)) & ~(std::max(alignof(T), 8) - 1); ctx->r1.u32 = m_ptr; } public: T* get() { return reinterpret_cast(g_memory.Translate(m_ptr)); } const T* get() const { return reinterpret_cast(g_memory.Translate(m_ptr)); } template guest_stack_var(Args&&... args) { AllocGuestStackMemory(); if (Init) new (get()) T(std::forward(args)...); } guest_stack_var(const guest_stack_var& other) { AllocGuestStackMemory(); if (Init) new (get()) T(*other->get()); } guest_stack_var(guest_stack_var&& other) { AllocGuestStackMemory(); if (Init) new (get()) T(std::move(*other->get())); } ~guest_stack_var() { get()->~T(); auto ctx = GetPPCContext(); // This assert will fail if the type was used as anything other than a local variable. assert(ctx->r1.u32 == m_ptr); ctx->r1.u32 = m_oldStackPtr; } void operator=(const guest_stack_var& other) { if (this != &other) *get() = *other->get(); } void operator=(guest_stack_var&& other) { if (this != &other) *get() = std::move(*other->get()); } void operator=(const T& other) { if (get() != &other) *get() = *other; } void operator=(T&& other) { if (get() != &other) *get() = std::move(*other); } operator const T* () const { return get(); } operator T* () { return get(); } const T* operator->() const { return get(); } T* operator->() { return get(); } const T& operator*() const { return *get(); } T& operator*() { return *get(); } }; ================================================ FILE: MarathonRecomp/cpu/guest_thread.cpp ================================================ #include #include #include "guest_thread.h" #include #include #include #include "ppc_context.h" constexpr size_t PCR_SIZE = 0xAB0; constexpr size_t TLS_SIZE = 0x100; constexpr size_t TEB_SIZE = 0x2E0; constexpr size_t STACK_SIZE = 0x80000; constexpr size_t TOTAL_SIZE = PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE; constexpr size_t TEB_OFFSET = PCR_SIZE + TLS_SIZE; GuestThreadContext::GuestThreadContext(uint32_t cpuNumber) { assert(thread == nullptr); thread = (uint8_t*)g_userHeap.Alloc(TOTAL_SIZE); // printf("TOTAL_SIZE: %x %x %d\n", thread, TOTAL_SIZE, TOTAL_SIZE); memset(thread, 0, TOTAL_SIZE); *(uint32_t*)thread = ByteSwap(g_memory.MapVirtual(thread + PCR_SIZE)); // tls pointer *(uint32_t*)(thread + 0x100) = ByteSwap(g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE)); // teb pointer *(thread + 0x10C) = cpuNumber; *(uint32_t*)(thread + PCR_SIZE + 0x10) = 0xFFFFFFFF; // that one TLS entry that felt quirky *(uint32_t*)(thread + PCR_SIZE + TLS_SIZE + 0x14C) = ByteSwap(GuestThread::GetCurrentThreadId()); // thread id ppcContext.r1.u64 = g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer ppcContext.r13.u64 = g_memory.MapVirtual(thread); ppcContext.fpscr.loadFromHost(); assert(GetPPCContext() == nullptr); SetPPCContext(ppcContext); } GuestThreadContext::~GuestThreadContext() { g_userHeap.Free(thread); } #ifdef USE_PTHREAD static size_t GetStackSize() { // Cache as this should not change. static size_t stackSize = 0; if (stackSize == 0) { // 8 MiB is a typical default. constexpr auto defaultSize = 8 * 1024 * 1024; struct rlimit lim; const auto ret = getrlimit(RLIMIT_STACK, &lim); if (ret == 0 && lim.rlim_cur < defaultSize) { // Use what the system allows. stackSize = lim.rlim_cur; } else { stackSize = defaultSize; } } return stackSize; } static void* GuestThreadFunc(void* arg) { GuestThreadHandle* hThread = (GuestThreadHandle*)arg; #else static void* GuestThreadFunc(GuestThreadHandle* hThread) { #endif hThread->suspended.wait(true); GuestThread::Start(hThread->params); // HACK(1) hThread->isFinished = true; return nullptr; } GuestThreadHandle::GuestThreadHandle(const GuestThreadParams& params) : params(params), suspended((params.flags & 0x1) != 0) #ifdef USE_PTHREAD { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, GetStackSize()); const auto ret = pthread_create(&thread, &attr, GuestThreadFunc, this); if (ret != 0) { fprintf(stderr, "pthread_create failed with error code 0x%X.\n", ret); return; } } #else , thread(GuestThreadFunc, this) { } #endif GuestThreadHandle::~GuestThreadHandle() { #ifdef USE_PTHREAD pthread_join(thread, nullptr); #else if (thread.joinable()) thread.join(); #endif } template static uint32_t CalcThreadId(const ThreadType& id) { if constexpr (sizeof(id) == 4) return *reinterpret_cast(&id); else return XXH32(&id, sizeof(id), 0); } uint32_t GuestThreadHandle::GetThreadId() const { #ifdef USE_PTHREAD return CalcThreadId(thread); #else return CalcThreadId(thread.get_id()); #endif } uint32_t GuestThreadHandle::Wait(uint32_t timeout) { if (timeout == INFINITE || isFinished.load()) // HACK(1): isFinished { #ifdef USE_PTHREAD pthread_join(thread, nullptr); #else if (thread.joinable()) thread.join(); #endif return STATUS_WAIT_0; } else if (timeout == 0) { #ifndef USE_PTHREAD if (thread.joinable()) return STATUS_TIMEOUT; #endif return STATUS_WAIT_0; } else { #ifdef USE_PTHREAD pthread_join(thread, nullptr); #else auto start = std::chrono::steady_clock::now(); while (thread.joinable()) { auto elapsed = std::chrono::steady_clock::now() - start; if (std::chrono::duration_cast(elapsed).count() >= timeout) return STATUS_TIMEOUT; std::this_thread::sleep_for(std::chrono::milliseconds(1)); } #endif return STATUS_WAIT_0; } } uint32_t GuestThread::Start(const GuestThreadParams& params) { const auto procMask = (uint8_t)(params.flags >> 24); const auto cpuNumber = procMask == 0 ? 0 : 7 - std::countl_zero(procMask); GuestThreadContext ctx(cpuNumber); ctx.ppcContext.r3.u64 = params.value; g_memory.FindFunction(params.function)(ctx.ppcContext, g_memory.base); return ctx.ppcContext.r3.u32; } GuestThreadHandle* GuestThread::Start(const GuestThreadParams& params, uint32_t* threadId) { auto hThread = CreateKernelObject(params); if (threadId != nullptr) { *threadId = hThread->GetThreadId(); } return hThread; } uint32_t GuestThread::GetCurrentThreadId() { #ifdef USE_PTHREAD return CalcThreadId(pthread_self()); #else return CalcThreadId(std::this_thread::get_id()); #endif } void GuestThread::SetLastError(uint32_t error) { auto* thread = (char*)g_memory.Translate(GetPPCContext()->r13.u32); if (*(uint32_t*)(thread + 0x150)) { // Program doesn't want errors return; } // TEB + 0x160 : Win32LastError *(uint32_t*)(thread + TEB_OFFSET + 0x160) = ByteSwap(error); } #ifdef _WIN32 void GuestThread::SetThreadName(uint32_t threadId, const char* name) { #pragma pack(push,8) const DWORD MS_VC_EXCEPTION = 0x406D1388; typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #pragma pack(pop) THREADNAME_INFO info; info.dwType = 0x1000; info.szName = name; info.dwThreadID = threadId; info.dwFlags = 0; __try { RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); } __except (EXCEPTION_EXECUTE_HANDLER) { } } #endif void SetThreadNameImpl(uint32_t a1, uint32_t threadId, uint32_t* name) { #ifdef _WIN32 GuestThread::SetThreadName(threadId, (const char*)g_memory.Translate(ByteSwap(*name))); #endif } int GetThreadPriorityImpl(GuestThreadHandle* hThread) { #ifdef _WIN32 return GetThreadPriority(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle()); #else return 0; #endif } uint32_t SetThreadIdealProcessorImpl(GuestThreadHandle* hThread, uint32_t dwIdealProcessor) { return 0; } // GUEST_FUNCTION_HOOK(sub_82DFA2E8, SetThreadNameImpl); // GUEST_FUNCTION_HOOK(sub_82BD57A8, GetThreadPriorityImpl); GUEST_FUNCTION_HOOK(sub_82537F80, SetThreadIdealProcessorImpl); // GUEST_FUNCTION_STUB(sub_82BD58F8); // Some function that updates the TEB, don't really care since the field is not set ================================================ FILE: MarathonRecomp/cpu/guest_thread.h ================================================ #pragma once #include // Use pthreads directly on macOS to be able to increase default stack size. #ifdef __APPLE__ #define USE_PTHREAD 1 #endif #ifdef USE_PTHREAD #include #endif #define CURRENT_THREAD_HANDLE uint32_t(-2) struct GuestThreadContext { PPCContext ppcContext{}; uint8_t* thread = nullptr; GuestThreadContext(uint32_t cpuNumber); ~GuestThreadContext(); }; struct GuestThreadParams { uint32_t function; uint32_t value; uint32_t flags; }; struct GuestThreadHandle : KernelObject { GuestThreadParams params; std::atomic suspended; #ifdef USE_PTHREAD pthread_t thread; #else std::thread thread; #endif // HACK(1) std::atomic isFinished = false; GuestThreadHandle(const GuestThreadParams& params); ~GuestThreadHandle() override; uint32_t GetThreadId() const; uint32_t Wait(uint32_t timeout) override; }; struct GuestThread { static uint32_t Start(const GuestThreadParams& params); static GuestThreadHandle* Start(const GuestThreadParams& params, uint32_t* threadId); static uint32_t GetCurrentThreadId(); static void SetLastError(uint32_t error); #ifdef _WIN32 static void SetThreadName(uint32_t threadId, const char* name); #endif }; ================================================ FILE: MarathonRecomp/cpu/ppc_context.h ================================================ #pragma once inline thread_local PPCContext* g_ppcContext; inline PPCContext* GetPPCContext() { return g_ppcContext; } inline void SetPPCContext(PPCContext& ctx) { g_ppcContext = &ctx; } ================================================ FILE: MarathonRecomp/decompressor.h ================================================ #pragma once template inline std::unique_ptr decompressZstd(const uint8_t(&data)[N], size_t decompressedSize) { auto decompressedData = std::make_unique(decompressedSize); ZSTD_decompress(decompressedData.get(), decompressedSize, data, N); return decompressedData; } ================================================ FILE: MarathonRecomp/exports.cpp ================================================ #include "exports.h" #include #include #include #include void Game_PlaySound(const char* pName) { if (EmbeddedPlayer::s_isActive) { EmbeddedPlayer::Play(pName); } else { Game_PlaySound("system", pName); } } void Game_PlaySound(const char* pBankName, const char* pName) { auto pBankNameGuest = g_userHeap.Alloc(strlen(pBankName) + 1); auto pNameGuest = g_userHeap.Alloc(strlen(pName) + 1); strcpy((char*)pBankNameGuest, pBankName); strcpy((char*)pNameGuest, pName); GuestToHostFunction(sub_824C7868, App::s_pApp->m_pDoc->m_pRootTask.get(), pBankNameGuest, pNameGuest); g_userHeap.Free(pBankNameGuest); g_userHeap.Free(pNameGuest); } ================================================ FILE: MarathonRecomp/exports.h ================================================ #pragma once void Game_PlaySound(const char* pName); void Game_PlaySound(const char* pBankName, const char* pName); ================================================ FILE: MarathonRecomp/framework.h ================================================ #pragma once #include #define PROC_ADDRESS(libraryName, procName) \ GetProcAddress(LoadLibrary(TEXT(libraryName)), procName) #define LIB_FUNCTION(returnType, libraryName, procName, ...) \ typedef returnType _##procName(__VA_ARGS__); \ _##procName* procName = (_##procName*)PROC_ADDRESS(libraryName, #procName); #define STR(x) #x template inline T RoundUp(const T& in_rValue, uint32_t in_round) { return (in_rValue + in_round - 1) & ~(in_round - 1); } template inline T RoundDown(const T& in_rValue, uint32_t in_round) { return in_rValue & ~(in_round - 1); } inline size_t StringHash(const std::string_view& str) { return XXH3_64bits(str.data(), str.size()); } template constexpr size_t FirstBitLow(TValue value) { constexpr size_t nbits = sizeof(TValue) * 8; constexpr auto zero = TValue{}; constexpr auto one = static_cast(1); for (size_t i = 0; i < nbits; i++) { if ((value & (one << i)) != zero) { return i; } } return 0; } inline std::unique_ptr ReadAllBytes(const char* filePath, size_t& fileSize) { FILE* file = fopen(filePath, "rb"); if (!file) return std::make_unique(0); fseek(file, 0, SEEK_END); fileSize = ftell(file); fseek(file, 0, SEEK_SET); auto data = std::make_unique(fileSize); fread(data.get(), 1, fileSize, file); fclose(file); return data; } inline bool strcmpIgnoreCase(const char* a, const char* b) { for (size_t i = 0; i < strlen(a); i++) { if (a[i] != '\0' && b[i] == '\0') return false; if (a[i] == '\0' && b[i] != '\0') return false; auto c1 = std::tolower((uint8_t)a[i]); auto c2 = std::tolower((uint8_t)b[i]); if (c1 != c2) return false; } return true; } inline bool strcmpWildcard(const char* str, const char* pattern) { // Match if both strings passed all // comparisons and reached the end. if (*pattern == '\0' && *str == '\0') return true; // Check if any number of chars matches. if (*pattern == '*') { // Skip duplicates. while (*(pattern + 1) == '*') pattern++; return strcmpWildcard(str, pattern + 1) || (*str && strcmpWildcard(str + 1, pattern)); } // Check if current char matches. if (*pattern == '?' || *pattern == *str) return strcmpWildcard(str + 1, pattern + 1); return false; } inline std::u16string TransformUTF8ToWString(const std::string& str) { std::wstring_convert, char16_t> converter; return converter.from_bytes(str); } ================================================ FILE: MarathonRecomp/gpu/cache/pipeline_state_cache.h ================================================ ================================================ FILE: MarathonRecomp/gpu/cache/vertex_declaration_cache.h ================================================ g_vertexElements_03CB3EF6B1C43B8C, g_vertexElements_0EC0CD05EE1B1636, g_vertexElements_0FB4544424558E4C, g_vertexElements_28FD2057B9BD5D1B, g_vertexElements_2A6D72391BFFFA3C, g_vertexElements_5A22D93C543DF925, g_vertexElements_5A2395E29F93DA3C, g_vertexElements_6196BF64CB935CA5, g_vertexElements_6538EB0019C3A29A, g_vertexElements_6FAE71C7134074A4, g_vertexElements_75A4FC397A05F4CA, g_vertexElements_7F12180DC3A24B53, g_vertexElements_82A1B9D74331DB2A, g_vertexElements_84BACD816D86543C, g_vertexElements_A81F28FA43A9B511, g_vertexElements_B22B7B7B968141C6, g_vertexElements_B7BBCC93738C9DE4, g_vertexElements_C64D046063DE2F63, g_vertexElements_D452411D3FB80A0D, g_vertexElements_DEB308DCDDF979C7, g_vertexElements_E6B3B3D286909AB9, g_vertexElements_EFD61AD3C5332AAE, g_vertexElements_F10787EFFEEC0153, g_vertexElements_FFFDDC62D86892F1, ================================================ FILE: MarathonRecomp/gpu/cache/vertex_element_cache.h ================================================ static uint8_t g_vertexElements_03CB3EF6B1C43B8C[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x23,0xB9,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2A,0x23,0xB9,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x2C,0x23,0xA5,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x40,0x0,0x2C,0x23,0xA5,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x48,0x0,0x2C,0x23,0xA5,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x50,0x0,0x1A,0x23,0xA6,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x1A,0x23,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2C,0x82,0xA1,0x0,0x0,0x1,0x0,0x0,0x2,0x0,0x0,0x0,0x2C,0x83,0xA4,0x0,0x0,0x2,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_0EC0CD05EE1B1636[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x21,0x59,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x2C,0x21,0x59,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x23,0x90,0x0,0x3,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_0FB4544424558E4C[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_28FD2057B9BD5D1B[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2A,0x23,0xB9,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x23,0xB9,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x2C,0x23,0xA5,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x40,0x0,0x2C,0x23,0xA5,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x48,0x0,0x2C,0x23,0xA5,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x50,0x0,0x1A,0x23,0xA6,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x1A,0x23,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_2A6D72391BFFFA3C[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x28,0x0,0x2C,0x23,0x5F,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x2C,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_5A22D93C543DF925[] = {0x0,0x0,0x0,0x0,0x0,0x2C,0x23,0xA5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_5A2395E29F93DA3C[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_6196BF64CB935CA5[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_6538EB0019C3A29A[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x20,0x0,0x2C,0x23,0x5F,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x24,0x0,0x2C,0x23,0x5F,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x28,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x2C,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_6FAE71C7134074A4[] = {0x0,0x0,0x0,0x0,0x0,0x2C,0x23,0xA5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_75A4FC397A05F4CA[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x23,0xB9,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2A,0x23,0xB9,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x2C,0x23,0xA5,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x40,0x0,0x1A,0x23,0xA6,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_7F12180DC3A24B53[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x5,0x1,0x0,0x0,0x1,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x5,0x2,0x0,0x0,0x1,0x0,0x10,0x0,0x2C,0x21,0x59,0x0,0x5,0x3,0x0,0x0,0x1,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x1,0x0,0x0,0x2,0x0,0x0,0x0,0x2C,0x82,0xA1,0x0,0x0,0x1,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_82A1B9D74331DB2A[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x20,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x1A,0x20,0x86,0x0,0xA,0x1,0x0,0x0,0x0,0x0,0x28,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x2C,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_84BACD816D86543C[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x23,0xB9,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2A,0x23,0xB9,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x2C,0x23,0xA5,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x40,0x0,0x2C,0x23,0xA5,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x48,0x0,0x2C,0x23,0xA5,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x50,0x0,0x1A,0x23,0xA6,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x1A,0x23,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_A81F28FA43A9B511[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_B22B7B7B968141C6[] = {0x0,0x0,0x0,0x0,0x0,0x1A,0x23,0xA6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_B7BBCC93738C9DE4[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_C64D046063DE2F63[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x28,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_D452411D3FB80A0D[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_DEB308DCDDF979C7[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x20,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_E6B3B3D286909AB9[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x5,0x1,0x0,0x0,0x1,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x5,0x2,0x0,0x0,0x1,0x0,0x10,0x0,0x2C,0x83,0xA4,0x0,0x5,0x3,0x0,0x0,0x1,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x1,0x0,0x0,0x2,0x0,0x0,0x0,0x2C,0x82,0xA1,0x0,0x0,0x1,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_EFD61AD3C5332AAE[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x23,0xB9,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2A,0x23,0xB9,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x2C,0x23,0xA5,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x40,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x44,0x0,0x1A,0x23,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x48,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_F10787EFFEEC0153[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x20,0x0,0x2C,0x23,0x5F,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x24,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x2C,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; static uint8_t g_vertexElements_FFFDDC62D86892F1[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; ================================================ FILE: MarathonRecomp/gpu/imgui/imgui_common.cpp ================================================ #include "imgui_common.h" static std::vector> g_callbackData; static uint32_t g_callbackDataIndex = 0; ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback) { if (g_callbackDataIndex >= g_callbackData.size()) g_callbackData.emplace_back(std::make_unique()); auto& callbackData = g_callbackData[g_callbackDataIndex]; ++g_callbackDataIndex; ImGui::GetBackgroundDrawList()->AddCallback(reinterpret_cast(callback), callbackData.get()); return callbackData.get(); } void ResetImGuiCallbacks() { g_callbackDataIndex = 0; } ================================================ FILE: MarathonRecomp/gpu/imgui/imgui_common.h ================================================ #pragma once #define IMGUI_SHADER_MODIFIER_NONE 0 #define IMGUI_SHADER_MODIFIER_TEXT_SKEW 1 #define IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE 2 #define IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE 3 #define IMGUI_SHADER_MODIFIER_GRAYSCALE 4 #define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 5 #define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 6 #define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 7 #define IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT 8 #if defined(__cplusplus) && !defined(__air__) enum class ImGuiCallback : int32_t { SetGradient = -1, SetShaderModifier = -2, SetOrigin = -3, SetScale = -4, SetMarqueeFade = -5, SetOutline = -6, SetProceduralOrigin = -7, // -8 is ImDrawCallback_ResetRenderState, don't use! SetAdditive = -9 }; union ImGuiCallbackData { struct { float boundsMin[2]; float boundsMax[2]; uint32_t gradientTopLeft; uint32_t gradientTopRight; uint32_t gradientBottomRight; uint32_t gradientBottomLeft; } setGradient; struct { uint32_t shaderModifier; } setShaderModifier; struct { float origin[2]; } setOrigin; struct { float scale[2]; } setScale; struct { float boundsMin[2]; float boundsMax[2]; } setMarqueeFade; struct { float outline; } setOutline; struct { float proceduralOrigin[2]; } setProceduralOrigin; struct { bool enabled; } setAdditive; }; extern ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback); extern void ResetImGuiCallbacks(); #endif ================================================ FILE: MarathonRecomp/gpu/imgui/imgui_font_builder.cpp ================================================ #include "imgui_font_builder.h" #include // Taken directly from msdf-atlas-gen, modified to support custom rectangles. struct TightAtlasPacker { TightAtlasPacker() : width(-1), height(-1), spacing(0), dimensionsConstraint(msdf_atlas::DimensionsConstraint::POWER_OF_TWO_SQUARE), scale(-1), minScale(1), unitRange(0), pxRange(0), miterLimit(0), pxAlignOriginX(false), pxAlignOriginY(false), scaleMaximizationTolerance(.001) { } int pack(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount) { double initialScale = scale > 0 ? scale : minScale; if (initialScale > 0) { if (int remaining = tryPack(glyphs, count, customRects, customRectCount, dimensionsConstraint, width, height, initialScale)) return remaining; } else if (width < 0 || height < 0) return -1; if (scale <= 0) scale = packAndScale(glyphs, count, customRects, customRectCount); if (scale <= 0) return -1; return 0; } int width, height; int spacing; msdf_atlas::DimensionsConstraint dimensionsConstraint; double scale; double minScale; msdfgen::Range unitRange; msdfgen::Range pxRange; double miterLimit; bool pxAlignOriginX, pxAlignOriginY; msdf_atlas::Padding innerUnitPadding, outerUnitPadding; msdf_atlas::Padding innerPxPadding, outerPxPadding; double scaleMaximizationTolerance; int tryPack(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount, msdf_atlas::DimensionsConstraint dimensionsConstraint, int& width, int& height, double scale) const { // Wrap glyphs into boxes std::vector rectangles; std::vector rectangleGlyphs; rectangles.reserve(count + customRectCount); rectangleGlyphs.reserve(count); msdf_atlas::GlyphGeometry::GlyphAttributes attribs = { }; attribs.scale = scale; attribs.range = { unitRange.lower + pxRange.lower / scale, unitRange.upper + pxRange.upper / scale }; attribs.innerPadding = innerUnitPadding + innerPxPadding / scale; attribs.outerPadding = outerUnitPadding + outerPxPadding / scale; attribs.miterLimit = miterLimit; attribs.pxAlignOriginX = pxAlignOriginX; attribs.pxAlignOriginY = pxAlignOriginY; for (msdf_atlas::GlyphGeometry* glyph = glyphs, *end = glyphs + count; glyph < end; ++glyph) { if (!glyph->isWhitespace()) { msdf_atlas::Rectangle rect = { }; glyph->wrapBox(attribs); glyph->getBoxSize(rect.w, rect.h); if (rect.w > 0 && rect.h > 0) { rectangles.push_back(rect); rectangleGlyphs.push_back(glyph); } } } rectangles.insert(rectangles.end(), customRects, customRects + customRectCount); // No non-zero size boxes? if (rectangles.empty()) { if (width < 0 || height < 0) width = 0, height = 0; return 0; } // Box rectangle packing if (width < 0 || height < 0) { std::pair dimensions = std::make_pair(width, height); switch (dimensionsConstraint) { case msdf_atlas::DimensionsConstraint::POWER_OF_TWO_SQUARE: dimensions = msdf_atlas::packRectangles(rectangles.data(), rectangles.size(), spacing); break; case msdf_atlas::DimensionsConstraint::POWER_OF_TWO_RECTANGLE: dimensions = msdf_atlas::packRectangles(rectangles.data(), rectangles.size(), spacing); break; case msdf_atlas::DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE: dimensions = msdf_atlas::packRectangles >(rectangles.data(), rectangles.size(), spacing); break; case msdf_atlas::DimensionsConstraint::EVEN_SQUARE: dimensions = msdf_atlas::packRectangles >(rectangles.data(), rectangles.size(), spacing); break; case msdf_atlas::DimensionsConstraint::SQUARE: default: dimensions = msdf_atlas::packRectangles >(rectangles.data(), rectangles.size(), spacing); break; } if (!(dimensions.first > 0 && dimensions.second > 0)) return -1; width = dimensions.first, height = dimensions.second; } else { if (int result = packRectangles(rectangles.data(), rectangles.size(), width, height, spacing)) return result; } // Set glyph box placement for (size_t i = 0; i < rectangleGlyphs.size(); ++i) rectangleGlyphs[i]->placeBox(rectangles[i].x, height - (rectangles[i].y + rectangles[i].h)); for (int i = 0; i < customRectCount; ++i) { customRects[i].x = rectangles[rectangleGlyphs.size() + i].x; customRects[i].y = height - (rectangles[rectangleGlyphs.size() + i].y + rectangles[rectangleGlyphs.size() + i].h); } return 0; } double packAndScale(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount) const { bool lastResult = false; int w = width, h = height; #define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, customRects, customRectCount, msdf_atlas::DimensionsConstraint(), w, h, (scale))) double minScale = 1, maxScale = 1; if (TRY_PACK(1)) { while (maxScale < 1e+32 && ((maxScale = 2 * minScale), TRY_PACK(maxScale))) minScale = maxScale; } else { while (minScale > 1e-32 && ((minScale = .5 * maxScale), !TRY_PACK(minScale))) maxScale = minScale; } if (minScale == maxScale) return 0; while (minScale / maxScale < 1 - scaleMaximizationTolerance) { double midScale = .5 * (minScale + maxScale); if (TRY_PACK(midScale)) minScale = midScale; else maxScale = midScale; } if (!lastResult) TRY_PACK(minScale); return minScale; } }; extern void ImFontAtlasBuildInit(ImFontAtlas* atlas); extern void ImFontAtlasBuildFinish(ImFontAtlas* atlas); static bool FontBuilder_Build(ImFontAtlas* atlas) { ImFontAtlasBuildInit(atlas); auto freeType = msdfgen::initializeFreetype(); std::vector glyphs; std::vector> ranges; std::vector customRects; for (auto& config : atlas->ConfigData) { msdf_atlas::Charset charset; const ImWchar* glyphRanges = config.GlyphRanges; while (*glyphRanges != NULL) { for (ImWchar i = glyphRanges[0]; i <= glyphRanges[1]; i++) charset.add(i); glyphRanges += 2; } size_t index = glyphs.size(); auto font = msdfgen::loadFontData(freeType, reinterpret_cast(config.FontData), config.FontDataSize); msdf_atlas::FontGeometry fontGeometry(&glyphs); fontGeometry.loadCharset(font, config.SizePixels, charset); auto& metrics = fontGeometry.getMetrics(); config.DstFont->FontSize = config.SizePixels; config.DstFont->ConfigData = &config; config.DstFont->ConfigDataCount = 1; config.DstFont->ContainerAtlas = atlas; config.DstFont->Ascent = metrics.ascenderY; config.DstFont->Descent = metrics.descenderY; msdfgen::destroyFont(font); ranges.emplace_back(index, glyphs.size() - index); } for (auto& glyph : glyphs) glyph.edgeColoring(&msdfgen::edgeColoringByDistance, 3.0, 0); for (auto& customRect : atlas->CustomRects) customRects.emplace_back(0, 0, int(customRect.Width), int(customRect.Height)); TightAtlasPacker packer; packer.spacing = 1; packer.dimensionsConstraint = msdf_atlas::DimensionsConstraint::POWER_OF_TWO_RECTANGLE; packer.miterLimit = 1.0; packer.pxRange = 8.0; packer.pack(glyphs.data(), glyphs.size(), customRects.data(), customRects.size()); for (size_t i = 0; i < customRects.size(); i++) { auto& srcRect = customRects[i]; auto& dstRect = atlas->CustomRects[i]; dstRect.X = srcRect.x; dstRect.Y = srcRect.y; } msdf_atlas::ImmediateAtlasGenerator> generator(packer.width, packer.height); generator.generate(glyphs.data(), glyphs.size()); for (size_t i = 0; i < atlas->ConfigData.size(); i++) { auto& config = atlas->ConfigData[i]; double spaceAdvance = 0.0; double spaceMultiplier = 1.0; if (strstr(config.Name, "FOT-Rodin") != nullptr) { spaceMultiplier = 1.75; } else if (strstr(config.Name, "FOT-NewRodin") != nullptr) { spaceMultiplier = 2.5; } auto& [index, count] = ranges[i]; for (size_t j = 0; j < count; j++) { auto& glyph = glyphs[index + j]; double x0, y0, x1, y1, u0, v0, u1, v1; glyph.getQuadPlaneBounds(x0, y0, x1, y1); glyph.getQuadAtlasBounds(u0, v0, u1, v1); double advance = glyph.getAdvance(); if (glyph.getCodepoint() == ' ') { advance *= spaceMultiplier; spaceAdvance = advance; } config.DstFont->AddGlyph( &config, glyph.getCodepoint(), x0, -y1 + config.DstFont->Ascent, x1, -y0 + config.DstFont->Ascent, u0 / packer.width, v1 / packer.height, u1 / packer.width, v0 / packer.height, advance); } // Used as a zero-width helper for automatic line breaks. // This is useful for languages like Japanese to separate 'words' // so that they don't get split mid-kana by the automatic splitter. config.DstFont->AddGlyph( &config, 0x200B, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); // A duplicate of the normal width space character. // Overrides the unicode Four-Per-Em Space character. // This can be used to add visual spacers that are ignored // by the automatic line splitting logic. config.DstFont->AddGlyph( &config, 0x2005, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, spaceAdvance); config.DstFont->BuildLookupTable(); } atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(packer.width * packer.height * 4); atlas->TexWidth = packer.width; atlas->TexHeight = packer.height; atlas->TexUvScale = { 1.0f / packer.width, 1.0f / packer.height }; auto bitmapRef = (msdfgen::BitmapConstRef)generator.atlasStorage(); for (int y = 0; y < packer.height; y++) { for (int x = 0; x < packer.width; x++) { auto* srcPixels = bitmapRef(x, y); auto* dstPixels = (uint8_t*)&atlas->TexPixelsRGBA32[y * packer.width + x]; dstPixels[0] = srcPixels[0]; dstPixels[1] = srcPixels[1]; dstPixels[2] = srcPixels[2]; dstPixels[3] = 0xFF; } } msdfgen::deinitializeFreetype(freeType); ImFontAtlasBuildFinish(atlas); return true; } ImFontBuilderIO g_fontBuilderIO = { FontBuilder_Build }; ================================================ FILE: MarathonRecomp/gpu/imgui/imgui_font_builder.h ================================================ #pragma once extern ImFontBuilderIO g_fontBuilderIO; ================================================ FILE: MarathonRecomp/gpu/imgui/imgui_snapshot.cpp ================================================ #include "imgui_snapshot.h" #include #include #include #include #include #include template void ImFontAtlasSnapshot::SnapPointer(size_t offset, const T1& value, const T2& ptr, size_t count) { if (ptr != nullptr && count != 0) { if (!objects.contains(ptr)) { constexpr size_t ALIGN = alignof(std::remove_pointer_t); constexpr size_t SIZE = sizeof(std::remove_pointer_t); size_t ptrOffset = (data.size() + ALIGN - 1) & ~(ALIGN - 1); data.resize(ptrOffset + SIZE * count); memcpy(&data[ptrOffset], ptr, SIZE * count); for (size_t i = 0; i < count; i++) { size_t curPtrOffset = ptrOffset + SIZE * i; objects[&ptr[i]] = curPtrOffset; Traverse(curPtrOffset, ptr[i]); } } size_t fieldOffset = offset + (reinterpret_cast(&ptr) - reinterpret_cast(&value)); *reinterpret_cast(&data[fieldOffset]) = objects[ptr]; offsets.push_back(fieldOffset); } } template void ImFontAtlasSnapshot::Traverse(size_t offset, const T& value) { if constexpr (std::is_pointer_v) { SnapPointer(offset, value, value, 1); } else if constexpr (std::is_same_v) { SnapPointer(offset, value, value.ConfigData.Data, value.ConfigData.Size); SnapPointer(offset, value, value.CustomRects.Data, value.CustomRects.Size); SnapPointer(offset, value, value.Fonts.Data, value.Fonts.Size); } else if constexpr (std::is_same_v) { SnapPointer(offset, value, value.IndexAdvanceX.Data, value.IndexAdvanceX.Size); SnapPointer(offset, value, value.IndexLookup.Data, value.IndexLookup.Size); SnapPointer(offset, value, value.Glyphs.Data, value.Glyphs.Size); SnapPointer(offset, value, value.FallbackGlyph, 1); SnapPointer(offset, value, value.ContainerAtlas, 1); SnapPointer(offset, value, value.ConfigData, value.ConfigDataCount); } else if constexpr (std::is_same_v) { SnapPointer(offset, value, value.Font, 1); } else if constexpr (std::is_same_v) { SnapPointer(offset, value, value.GlyphRanges, value.GlyphRanges != nullptr ? wcslen(reinterpret_cast(value.GlyphRanges)) + 1 : 0); SnapPointer(offset, value, value.DstFont, 1); } } template size_t ImFontAtlasSnapshot::Snap(const T& value) { size_t offset = (data.size() + alignof(T) - 1) & ~(alignof(T) - 1); data.resize(offset + sizeof(T)); memcpy(&data[offset], &value, sizeof(T)); objects[&value] = offset; Traverse(offset, value); return offset; } struct ImFontAtlasSnapshotHeader { uint32_t imguiVersion; uint32_t dataOffset; uint32_t offsetCount; uint32_t offsetsOffset; }; void ImFontAtlasSnapshot::Snap() { data.resize(sizeof(ImFontAtlasSnapshotHeader)); size_t dataOffset = Snap(*ImGui::GetIO().Fonts); size_t offsetsOffset = data.size(); std::sort(offsets.begin(), offsets.end()); data.insert(data.end(), reinterpret_cast(offsets.data()), reinterpret_cast(offsets.data() + offsets.size())); auto header = reinterpret_cast(data.data()); header->imguiVersion = IMGUI_VERSION_NUM; header->dataOffset = dataOffset; header->offsetCount = offsets.size(); header->offsetsOffset = offsetsOffset; } static std::unique_ptr g_imFontAtlas; ImFontAtlas* ImFontAtlasSnapshot::Load() { g_imFontAtlas = decompressZstd(g_im_font_atlas, g_im_font_atlas_uncompressed_size); auto header = reinterpret_cast(g_imFontAtlas.get()); assert(header->imguiVersion == IMGUI_VERSION_NUM && "ImGui version mismatch, the font atlas needs to be regenerated!"); auto offsetTable = reinterpret_cast(g_imFontAtlas.get() + header->offsetsOffset); for (size_t i = 0; i < header->offsetCount; i++) { *reinterpret_cast(g_imFontAtlas.get() + (*offsetTable)) += reinterpret_cast(g_imFontAtlas.get()); ++offsetTable; } return reinterpret_cast(g_imFontAtlas.get() + header->dataOffset); } static void GetGlyphs(std::set& glyphs, const std::string_view& value) { const char* cur = value.data(); while (cur < value.data() + value.size()) { unsigned int c; cur += ImTextCharFromUtf8(&c, cur, value.data() + value.size()); glyphs.emplace(c); } } static std::vector g_glyphRanges; void ImFontAtlasSnapshot::GenerateGlyphRanges() { std::vector localeStrings; for (auto& config : g_configDefinitions) config->GetLocaleStrings(localeStrings); std::set glyphs; for (size_t i = 0x20; i <= 0xFF; i++) glyphs.emplace(i); for (auto& localeString : localeStrings) GetGlyphs(glyphs, localeString); for (auto& [name, locale] : g_locale) { for (auto& [language, value] : locale) GetGlyphs(glyphs, value); } for (size_t i = XDBF_LANGUAGE_ENGLISH; i <= XDBF_LANGUAGE_ITALIAN; i++) { auto achievements = g_xdbfWrapper.GetAchievements(static_cast(i)); for (auto& achievement : achievements) { GetGlyphs(glyphs, achievement.Name); GetGlyphs(glyphs, achievement.UnlockedDesc); GetGlyphs(glyphs, achievement.LockedDesc); } } for (auto glyph : glyphs) { if (g_glyphRanges.empty() || (g_glyphRanges.back() + 1) != glyph) { g_glyphRanges.push_back(glyph); g_glyphRanges.push_back(glyph); } else { g_glyphRanges.back() = glyph; } } g_glyphRanges.push_back(0); } ImFont* ImFontAtlasSnapshot::GetFont(const char* name) { auto fontAtlas = ImGui::GetIO().Fonts; for (auto& configData : fontAtlas->ConfigData) { if (strstr(configData.Name, name) != nullptr) { assert(configData.DstFont != nullptr); return configData.DstFont; } } #ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT assert(false && "Unable to locate equivalent font in the atlas file."); #endif return fontAtlas->AddFontFromFileTTF(name, 24.0f, nullptr, g_glyphRanges.data()); } ================================================ FILE: MarathonRecomp/gpu/imgui/imgui_snapshot.h ================================================ #pragma once // Undefine this to generate a font atlas file in working directory. // You also need to do this if you are testing localization, as only // characters in the locale get added to the atlas. #define ENABLE_IM_FONT_ATLAS_SNAPSHOT struct ImFontAtlasSnapshot { std::vector data; ankerl::unordered_dense::map objects; std::vector offsets; template void SnapPointer(size_t offset, const T1& value, const T2& ptr, size_t count); template void Traverse(size_t offset, const T& value); template size_t Snap(const T& value); void Snap(); static ImFontAtlas* Load(); static void GenerateGlyphRanges(); // When ENABLE_IM_FONT_ATLAS_SNAPSHOT is undefined, this creates the font runtime instead. static ImFont* GetFont(const char* name); }; ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/.gitignore ================================================ *.hlsl.*.h ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/blend_color_alpha_ps.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define g_SrcAlpha_DestAlpha vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 2400, 0x10) #define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) #define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) #else cbuffer PixelShaderConstants : register(b1, space4) { float4 g_SrcAlpha_DestAlpha : packoffset(c150); }; cbuffer SharedConstants : register(b2, space4) { uint s0_Texture2DDescriptorIndex : packoffset(c0.x); uint s0_SamplerDescriptorIndex : packoffset(c12.x); DEFINE_SHARED_CONSTANTS(); }; #endif float4 shaderMain( in float4 iPos : SV_Position, in float4 iTexCoord0 : TEXCOORD0) : SV_Target0 { Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; float4 color = texture.Sample(samplerState, iTexCoord0.xy); if (any(or(iTexCoord0.xy < 0.0, iTexCoord0.xy > 1.0))) color = float4(0.0, 0.0, 0.0, 1.0); color.rgb *= color.a * g_SrcAlpha_DestAlpha.x; color.a = g_SrcAlpha_DestAlpha.y + (1.0 - color.a) * g_SrcAlpha_DestAlpha.x; return color; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/conditional_survey_ps.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" [earlydepthstencil] float4 shaderMain() : SV_Target { atomicFetchAddUint(g_ConditionalSurveyBuffer, g_conditionalSurveyIndex, 1); return float4(0.0, 0.0, 0.0, 0.0); } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/copy_color_ps.hlsl ================================================ #include "copy_common.hlsli" Texture2D g_Texture2DDescriptorHeap[] : register(t0, space0); float4 shaderMain(in float4 position : SV_Position) : SV_Target { return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0)); } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/copy_common.hlsli ================================================ #pragma once struct PushConstants { uint ResourceDescriptorIndex; }; [[vk::push_constant]] ConstantBuffer g_PushConstants : register(b3, space4); ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/copy_depth_ps.hlsl ================================================ #include "copy_common.hlsli" Texture2D g_Texture2DDescriptorHeap[] : register(t0, space0); float shaderMain(in float4 position : SV_Position) : SV_Depth { return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int3(position.xy, 0)); } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/copy_vs.hlsl ================================================ void shaderMain(in uint vertexId : SV_VertexID, out float4 position : SV_Position, out float2 texCoord : TEXCOORD) { texCoord = float2((vertexId << 1) & 2, vertexId & 2); position = float4(texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/csd_filter_ps.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) #define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) #else cbuffer SharedConstants : register(b2, space4) { uint s0_Texture2DDescriptorIndex : packoffset(c0.x); uint s0_SamplerDescriptorIndex : packoffset(c12.x); DEFINE_SHARED_CONSTANTS(); }; #endif float4 shaderMain( in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0, in float4 iTexCoord1 : TEXCOORD1) : SV_Target { Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; uint2 dimensions; texture.GetDimensions(dimensions.x, dimensions.y); // https://www.shadertoy.com/view/csX3RH float2 uvTexspace = iTexCoord1.xy * dimensions; float2 seam = floor(uvTexspace + 0.5); uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam; uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5); float2 texCoord = uvTexspace / dimensions; float4 color = texture.Sample(samplerState, texCoord); color *= iTexCoord0; // The game enables alpha test for CSD, but the alpha threshold doesn't seem to be assigned anywhere? Weird. clip(color.a - g_AlphaThreshold); return color; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/csd_no_tex_vs.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 2880, 0x10) #define g_Z vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 3936, 0x10) #else cbuffer VertexShaderConstants : register(b0, space4) { float4 g_ViewportSize : packoffset(c180); float4 g_Z : packoffset(c246); }; cbuffer SharedConstants : register(b2, space4) { DEFINE_SHARED_CONSTANTS(); }; #endif void shaderMain( [[vk::location(0)]] in float4 iPosition0 : POSITION0, [[vk::location(8)]] in float4 iColor0 : COLOR0, out float4 oPos : SV_Position, out float4 oTexCoord0 : TEXCOORD0, out float4 oTexCoord1 : TEXCOORD1, out float4 oTexCoord2 : TEXCOORD2, out float4 oTexCoord3 : TEXCOORD3, out float4 oTexCoord4 : TEXCOORD4, out float4 oTexCoord5 : TEXCOORD5, out float4 oTexCoord6 : TEXCOORD6, out float4 oTexCoord7 : TEXCOORD7, out float4 oTexCoord8 : TEXCOORD8, out float4 oTexCoord9 : TEXCOORD9, out float4 oTexCoord10 : TEXCOORD10, out float4 oTexCoord11 : TEXCOORD11, out float4 oTexCoord12 : TEXCOORD12, out float4 oTexCoord13 : TEXCOORD13, out float4 oTexCoord14 : TEXCOORD14, out float4 oTexCoord15 : TEXCOORD15, out float4 oColor0 : COLOR0, out float4 oColor1 : COLOR1) { oPos.xy = iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); oPos.z = g_Z.x; oPos.w = 1.0; oTexCoord0 = iColor0.wxyz; oTexCoord1 = 0.0; oTexCoord2 = 0.0; oTexCoord3 = 0.0; oTexCoord4 = 0.0; oTexCoord5 = 0.0; oTexCoord6 = 0.0; oTexCoord7 = 0.0; oTexCoord8 = 0.0; oTexCoord9 = 0.0; oTexCoord10 = 0.0; oTexCoord11 = 0.0; oTexCoord12 = 0.0; oTexCoord13 = 0.0; oTexCoord14 = 0.0; oTexCoord15 = 0.0; oColor0 = 0.0; oColor1 = 0.0; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/csd_vs.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 2880, 0x10) #define g_Z vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 3936, 0x10) #else cbuffer VertexShaderConstants : register(b0, space4) { float4 g_ViewportSize : packoffset(c180); float4 g_Z : packoffset(c246); }; cbuffer SharedConstants : register(b2, space4) { DEFINE_SHARED_CONSTANTS(); }; #endif void shaderMain( [[vk::location(0)]] in float4 iPosition0 : POSITION0, [[vk::location(8)]] in float4 iColor0 : COLOR0, [[vk::location(4)]] in float4 iTexCoord0 : TEXCOORD0, out float4 oPos : SV_Position, out float4 oTexCoord0 : TEXCOORD0, out float4 oTexCoord1 : TEXCOORD1, out float4 oTexCoord2 : TEXCOORD2, out float4 oTexCoord3 : TEXCOORD3, out float4 oTexCoord4 : TEXCOORD4, out float4 oTexCoord5 : TEXCOORD5, out float4 oTexCoord6 : TEXCOORD6, out float4 oTexCoord7 : TEXCOORD7, out float4 oTexCoord8 : TEXCOORD8, out float4 oTexCoord9 : TEXCOORD9, out float4 oTexCoord10 : TEXCOORD10, out float4 oTexCoord11 : TEXCOORD11, out float4 oTexCoord12 : TEXCOORD12, out float4 oTexCoord13 : TEXCOORD13, out float4 oTexCoord14 : TEXCOORD14, out float4 oTexCoord15 : TEXCOORD15, out float4 oColor0 : COLOR0, out float4 oColor1 : COLOR1) { oPos.xy = iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); oPos.z = g_Z.x; oPos.w = 1.0; oTexCoord0 = iColor0.wxyz; oTexCoord1.xy = iTexCoord0.xy; oTexCoord1.zw = 0.0; oTexCoord2 = 0.0; oTexCoord3 = 0.0; oTexCoord4 = 0.0; oTexCoord5 = 0.0; oTexCoord6 = 0.0; oTexCoord7 = 0.0; oTexCoord8 = 0.0; oTexCoord9 = 0.0; oTexCoord10 = 0.0; oTexCoord11 = 0.0; oTexCoord12 = 0.0; oTexCoord13 = 0.0; oTexCoord14 = 0.0; oTexCoord15 = 0.0; oColor0 = 0.0; oColor1 = 0.0; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/enhanced_burnout_blur_ps.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) #define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) #else cbuffer SharedConstants : register(b2, space4) { uint s0_Texture2DDescriptorIndex : packoffset(c0.x); uint s0_SamplerDescriptorIndex : packoffset(c12.x); DEFINE_SHARED_CONSTANTS(); }; #endif float4 shaderMain( in float4 oPos : SV_Position, in float2 oTexCoord0 : TEXCOORD0, in float2 oVelocity : TEXCOORD1, in float2 oVelScale : TEXCOORD2) : SV_Target { Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; float velocityMag = sqrt(dot(oVelocity, oVelocity)); float blurAmount = saturate(velocityMag - 0.25); if (blurAmount == 0.0) return texture.SampleLevel(samplerState, oTexCoord0, 0); blurAmount = min(blurAmount * blurAmount, 0.25); float blurStrength = blurAmount * velocityMag; int g_SampleCount = clamp((int)(blurStrength * 256.0), 4, 64); float2 scaledVelStep = oVelScale / (float)g_SampleCount; float4 result = float4(0.0, 0.0, 0.0, 0.0); for (int i = 0; i < g_SampleCount; i++) { float2 offset = blurAmount * scaledVelStep * (float)i; float2 samplePos = oTexCoord0 + offset; result += texture.SampleLevel(samplerState, samplePos, 0); } return result * (1.0 / (float)g_SampleCount); } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/enhanced_burnout_blur_vs.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define g_Velocity vk::RawBufferLoad(g_PushConstants.VertexShaderConstants + 3360, 0x10) #else cbuffer VertexShaderConstants : register(b1, space4) { float4 g_Velocity : packoffset(c210); }; #endif void shaderMain( [[vk::location(0)]] in float4 iPosition0 : POSITION0, [[vk::location(13)]] in float2 iTexCoord0 : TEXCOORD0, out float4 oPos : SV_Position, out float2 oTexCoord0 : TEXCOORD0, out float2 oVelocity : TEXCOORD1, out float2 oVelScale : TEXCOORD2) { oPos = iPosition0; oTexCoord0 = iTexCoord0; float2 centeredUV; centeredUV.x = iTexCoord0.y * 2.0 - 1.0; centeredUV.y = iTexCoord0.x * 2.0 - 1.0; oVelocity.x = -g_Velocity.x - centeredUV.y; oVelocity.y = centeredUV.x - g_Velocity.y; float2 scaledVec = oVelocity * g_Velocity.w; oVelScale.x = scaledVec.x * 0.00002; oVelScale.y = -scaledVec.y * 0.00001; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/gamma_correction_ps.hlsl ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define g_Gamma vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) #define g_TextureDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 4) #define g_ViewportOffset vk::RawBufferLoad(g_PushConstants.SharedConstants + 8) #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.SharedConstants + 16) #else cbuffer SharedConstants : register(b2, space4) { float g_Gamma; uint g_TextureDescriptorIndex; int2 g_ViewportOffset; int2 g_ViewportSize; }; #endif float4 shaderMain(in float4 position : SV_Position) : SV_Target { Texture2D texture = g_Texture2DDescriptorHeap[g_TextureDescriptorIndex]; int2 movedPosition = int2(position.xy) - g_ViewportOffset; bool boxed = any(movedPosition < 0) || any(movedPosition >= g_ViewportSize); if (boxed) movedPosition = 0; float4 color = boxed ? 0.0 : texture.Load(int3(movedPosition, 0)); color.rgb = pow(color.rgb, g_Gamma); return color; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/gaussian_blur.hlsli ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef __spirv__ #define g_ViewportSize vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 384, 0x10) #define g_offsets(INDEX) select((INDEX) < 74, vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + (150 + min(INDEX, 73)) * 16, 0x10), 0.0) #define g_weights vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 2656, 0x10) #define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) #define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) #else cbuffer PixelShaderConstants : register(b1, space4) { float4 g_ViewportSize : packoffset(c24); float4 g_offsets[2] : packoffset(c150); #define g_offsets(INDEX) select((INDEX) < 74, g_offsets[min(INDEX, 73)], 0.0) float4 g_weights : packoffset(c166); }; cbuffer SharedConstants : register(b2, space4) { uint s0_Texture2DDescriptorIndex : packoffset(c0.x); uint s0_SamplerDescriptorIndex : packoffset(c12.x); DEFINE_SHARED_CONSTANTS(); }; #endif #ifdef __INTELLISENSE__ #define KERNEL_SIZE 5 #endif #define PI 3.14159265358979323846 float ComputeWeight(float x) { float std = 0.952; return exp(-(x * x) / (2.0 * std * std)) / (std * sqrt(2.0 * PI)); } float4 shaderMain(in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0) : SV_Target { Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; float scale; if ((g_ViewportSize.x * g_ViewportSize.w) >= (16.0 / 9.0)) scale = g_ViewportSize.y / 360.0; else scale = g_ViewportSize.x / 640.0; float2 offsets[3]; offsets[0] = g_offsets(0).xy * scale; offsets[1] = g_offsets(0).zw * scale; offsets[2] = g_offsets(1).xy * scale; float4 color = 0.0; float weightSum = 0.0; [unroll] for (int i = 0; i < KERNEL_SIZE; i++) { float step = i / float(KERNEL_SIZE - 1); float scaled = step * 2; float2 offset = lerp(offsets[int(scaled)], offsets[min(int(scaled) + 1, 2)], frac(scaled)); float offsetScale = 1.0 / 0.75; float weight = ComputeWeight(lerp(-offsetScale, offsetScale, step)); color += texture.SampleLevel(samplerState, iTexCoord0.xy + offset, 0) * weight; weightSum += weight; } return color / weightSum; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/gaussian_blur_3x3.hlsl ================================================ #define KERNEL_SIZE 3 #include "gaussian_blur.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/gaussian_blur_5x5.hlsl ================================================ #define KERNEL_SIZE 5 #include "gaussian_blur.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/gaussian_blur_7x7.hlsl ================================================ #define KERNEL_SIZE 7 #include "gaussian_blur.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/gaussian_blur_9x9.hlsl ================================================ #define KERNEL_SIZE 9 #include "gaussian_blur.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/imgui_common.hlsli ================================================ #pragma once #include "../../imgui/imgui_common.h" struct PushConstants { float2 BoundsMin; float2 BoundsMax; uint GradientTopLeft; uint GradientTopRight; uint GradientBottomRight; uint GradientBottomLeft; uint ShaderModifier; uint Texture2DDescriptorIndex; float2 DisplaySize; float2 InverseDisplaySize; float2 Origin; float2 Scale; float2 ProceduralOrigin; float Outline; }; Texture2D g_Texture2DDescriptorHeap[] : register(t0, space0); SamplerState g_SamplerDescriptorHeap[] : register(s0, space1); [[vk::push_constant]] ConstantBuffer g_PushConstants : register(b0, space2); struct Interpolators { float4 Position : SV_Position; float2 UV : TEXCOORD; float4 Color : COLOR; }; ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/imgui_ps.hlsl ================================================ #include "imgui_common.hlsli" float4 DecodeColor(uint color) { return float4(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, (color >> 24) & 0xFF) / 255.0; } float4 SamplePoint(int2 position) { return 1.0; } float4 SampleLinear(float2 uvTexspace) { int2 integerPart = floor(uvTexspace); float2 fracPart = frac(uvTexspace); float4 topLeft = SamplePoint(integerPart + float2(0, 0)); float4 topRight = SamplePoint(integerPart + float2(1, 0)); float4 bottomLeft = SamplePoint(integerPart + float2(0, 1)); float4 bottomRight = SamplePoint(integerPart + float2(1, 1)); float4 top = lerp(topLeft, topRight, fracPart.x); float4 bottom = lerp(bottomLeft, bottomRight, fracPart.x); return lerp(top, bottom, fracPart.y); } float4 PixelAntialiasing(float2 uvTexspace) { if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0)) uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0; else uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0; float2 seam = floor(uvTexspace + 0.5); uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam; uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5); return SampleLinear(uvTexspace - 0.5); } float median(float r, float g, float b) { return max(min(r, g), min(max(r, g), b)); } float4 SampleSdfFont(float4 color, Texture2D texture, float2 uv, float2 screenTexSize) { float4 textureColor = texture.Sample(g_SamplerDescriptorHeap[0], uv); uint width, height; texture.GetDimensions(width, height); float pxRange = 8.0; float2 unitRange = pxRange / float2(width, height); float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0); float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5; float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 1.5)); if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL) { float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.01)).xy; float3 rimColor = float3(1, 0.8, 0.29); float3 shadowColor = float3(0.84, 0.57, 0); float cosTheta = dot(normal, normalize(float2(1, 1))); float3 gradient = lerp(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta)); color.rgb = lerp(gradient, color.rgb, pow(saturate(sd + 0.77), 32.0)); } else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL) { float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.25)).xy; float cosTheta = dot(normal, normalize(float2(1, 1))); float gradient = 1.0 + cosTheta * 0.5; color.rgb = saturate(color.rgb * gradient); } else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW) { float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.5)).xy; float cosTheta = dot(normal, normalize(float2(1, 1))); float gradient = saturate(1.0 + cosTheta); color.rgb = lerp(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0)); } color.a *= saturate(screenPxDistance + 0.5); color.a *= textureColor.a; return color; } float4 shaderMain(in Interpolators interpolators) : SV_Target { float4 color = interpolators.Color; color *= PixelAntialiasing(interpolators.Position.xy - g_PushConstants.ProceduralOrigin); if (g_PushConstants.Texture2DDescriptorIndex != 0) { Texture2D texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF]; if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0) { if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT) { float scale; float invScale; if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0)) { scale = g_PushConstants.InverseDisplaySize.y * 720.0; invScale = g_PushConstants.DisplaySize.y / 720.0; } else { scale = g_PushConstants.InverseDisplaySize.x * 960.0; invScale = g_PushConstants.DisplaySize.x / 960.0; } float2 lowQualityPosition = (interpolators.Position.xy - 0.5) * scale; float2 fracPart = frac(lowQualityPosition); float2 uvStep = fwidth(interpolators.UV) * invScale; float2 lowQualityUV = interpolators.UV - fracPart * uvStep; float2 screenTexSize = 1.0 / uvStep; float4 topLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, 0), screenTexSize); float4 topRight = SampleSdfFont(color, texture, lowQualityUV + float2(uvStep.x, 0), screenTexSize); float4 bottomLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, uvStep.y), screenTexSize); float4 bottomRight = SampleSdfFont(color, texture, lowQualityUV + uvStep.xy, screenTexSize); float4 top = lerp(topLeft, topRight, fracPart.x); float4 bottom = lerp(bottomLeft, bottomRight, fracPart.x); color = lerp(top, bottom, fracPart.y); } else { color = SampleSdfFont(color, texture, interpolators.UV, 1.0 / fwidth(interpolators.UV)); } } else { color *= texture.Sample(g_SamplerDescriptorHeap[0], interpolators.UV); } } if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE) { float minAlpha = saturate((interpolators.Position.x - g_PushConstants.BoundsMin.x) / g_PushConstants.Scale.x); float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.y); color.a *= minAlpha; color.a *= maxAlpha; } else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE) { float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.x); float maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y); color.a *= minAlpha; color.a *= maxAlpha; } else if (any(g_PushConstants.BoundsMin != g_PushConstants.BoundsMax)) { float2 factor = saturate((interpolators.Position.xy - g_PushConstants.BoundsMin) / (g_PushConstants.BoundsMax - g_PushConstants.BoundsMin)); if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL) { float bevelSize = 0.9; float shadow = saturate((factor.x - bevelSize) / (1.0 - bevelSize)); shadow = max(shadow, saturate((factor.y - bevelSize) / (1.0 - bevelSize))); float rim = saturate((1.0 - factor.x - bevelSize) / (1.0 - bevelSize)); rim = max(rim, saturate((1.0 - factor.y - bevelSize) / (1.0 - bevelSize))); float3 rimColor = float3(1, 0.8, 0.29); float3 shadowColor = float3(0.84, 0.57, 0); color.rgb = lerp(color.rgb, rimColor, smoothstep(0.0, 1.0, rim)); color.rgb = lerp(color.rgb, shadowColor, smoothstep(0.0, 1.0, shadow)); } else { float4 top = lerp(DecodeColor(g_PushConstants.GradientTopLeft), DecodeColor(g_PushConstants.GradientTopRight), smoothstep(0.0, 1.0, factor.x)); float4 bottom = lerp(DecodeColor(g_PushConstants.GradientBottomLeft), DecodeColor(g_PushConstants.GradientBottomRight), smoothstep(0.0, 1.0, factor.x)); color *= lerp(top, bottom, smoothstep(0.0, 1.0, factor.y)); } } if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_GRAYSCALE) color.rgb = dot(color.rgb, float3(0.2126, 0.7152, 0.0722)); return color; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/imgui_vs.hlsl ================================================ #include "imgui_common.hlsli" void shaderMain(in float2 position : POSITION, in float2 uv : TEXCOORD, in float4 color : COLOR, out Interpolators interpolators) { if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW) { if (position.y < g_PushConstants.Origin.y) position.x += g_PushConstants.Scale.x; } else if (g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE && g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE) { position.xy = g_PushConstants.Origin + (position.xy - g_PushConstants.Origin) * g_PushConstants.Scale; } interpolators.Position = float4(position.xy * g_PushConstants.InverseDisplaySize * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); interpolators.UV = uv; interpolators.Color = color; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_color.hlsli ================================================ #pragma once #include "copy_common.hlsli" Texture2DMS g_Texture2DMSDescriptorHeap[] : register(t0, space0); float4 shaderMain(in float4 position : SV_Position) : SV_Target { float4 result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0); [unroll] for (int i = 1; i < SAMPLE_COUNT; i++) result += g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i); return result / SAMPLE_COUNT; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_color_2x.hlsl ================================================ #define SAMPLE_COUNT 2 #include "resolve_msaa_color.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_color_4x.hlsl ================================================ #define SAMPLE_COUNT 4 #include "resolve_msaa_color.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_color_8x.hlsl ================================================ #define SAMPLE_COUNT 8 #include "resolve_msaa_color.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_depth.hlsli ================================================ #pragma once #include "copy_common.hlsli" Texture2DMS g_Texture2DMSDescriptorHeap[] : register(t0, space0); float shaderMain(in float4 position : SV_Position) : SV_Depth { float result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0); [unroll] for (int i = 1; i < SAMPLE_COUNT; i++) result = min(result, g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i)); return result; } ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_depth_2x.hlsl ================================================ #define SAMPLE_COUNT 2 #include "resolve_msaa_depth.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_depth_4x.hlsl ================================================ #define SAMPLE_COUNT 4 #include "resolve_msaa_depth.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/hlsl/resolve_msaa_depth_8x.hlsl ================================================ #define SAMPLE_COUNT 8 #include "resolve_msaa_depth.hlsli" ================================================ FILE: MarathonRecomp/gpu/shader/msl/.gitignore ================================================ *.ir *.metallib *.metal.*.c *.metal.*.h ================================================ FILE: MarathonRecomp/gpu/shader/msl/blend_color_alpha_ps.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #define g_SrcAlpha_DestAlpha (*(reinterpret_cast(g_PushConstants.PixelShaderConstants + 2400))) #define s0_Texture2DDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 0))) #define s0_SamplerDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 192))) struct Interpolators { float4 iTexCoord0 [[user(TEXCOORD0)]]; }; [[fragment]] float4 shaderMain(float4 iPos [[position]], Interpolators input [[stage_in]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { texture2d texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex].tex; sampler samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex].samp; float4 color = texture.sample(samplerState, input.iTexCoord0.xy); if (any(input.iTexCoord0.xy < 0.0 || input.iTexCoord0.xy > 1.0)) color = float4(0.0, 0.0, 0.0, 1.0); color.rgb *= color.a * g_SrcAlpha_DestAlpha.x; color.a = g_SrcAlpha_DestAlpha.y + (1.0 - color.a) * g_SrcAlpha_DestAlpha.x; return color; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/conditional_survey_ps.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" [[fragment]] [[early_fragment_tests]] float4 shaderMain(device AtomicUintBuffer* g_ConditionalSurveyBuffer [[buffer(4)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { atomicFetchAddUint(g_ConditionalSurveyBuffer, g_conditionalSurveyIndex, 1); return float4(0.0, 0.0, 0.0, 0.0); } ================================================ FILE: MarathonRecomp/gpu/shader/msl/copy_color_ps.metal ================================================ #include "copy_common.metali" struct Texture2DDescriptorHeap { texture2d tex; }; [[fragment]] float4 shaderMain(float4 position [[position]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { return g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0); } ================================================ FILE: MarathonRecomp/gpu/shader/msl/copy_common.metali ================================================ #pragma once #include using namespace metal; struct PushConstants { uint ResourceDescriptorIndex; }; ================================================ FILE: MarathonRecomp/gpu/shader/msl/copy_depth_ps.metal ================================================ #include "copy_common.metali" struct Texture2DDescriptorHeap { texture2d tex; }; struct PixelShaderOutput { float oDepth [[depth(any)]]; }; [[fragment]] PixelShaderOutput shaderMain(float4 position [[position]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { PixelShaderOutput output = PixelShaderOutput{}; output.oDepth = g_Texture2DDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0).x; return output; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/copy_vs.metal ================================================ struct Interpolators { float4 position [[position]]; float2 texCoord [[user(TEXCOORD)]]; }; [[vertex]] Interpolators shaderMain(uint vertexId [[vertex_id]]) { Interpolators interpolators; interpolators.texCoord = float2((vertexId << 1) & 2, vertexId & 2); interpolators.position = float4(interpolators.texCoord * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); return interpolators; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/csd_filter_ps.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #define s0_Texture2DDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 0))) #define s0_SamplerDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 192))) struct Interpolators { float4 iPosition [[position]]; float4 iTexCoord0 [[user(TEXCOORD0)]]; float4 iTexCoord1 [[user(TEXCOORD1)]]; }; [[fragment]] float4 shaderMain(Interpolators input [[stage_in]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { texture2d texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex].tex; sampler samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex].samp; uint2 dimensions = getTexture2DDimensions(texture); // https://www.shadertoy.com/view/csX3RH float2 uvTexspace = input.iTexCoord1.xy * float2(dimensions); float2 seam = floor(uvTexspace + 0.5); uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam; uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5); float2 texCoord = uvTexspace / float2(dimensions); float4 color = texture.sample(samplerState, texCoord); color *= input.iTexCoord0; // The game enables alpha test for CSD, but the alpha threshold doesn't seem to be assigned anywhere? Weird. clip(color.a - g_AlphaThreshold); return color; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/csd_no_tex_vs.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #define g_ViewportSize (*(reinterpret_cast(g_PushConstants.VertexShaderConstants + 2880))) #define g_Z (*(reinterpret_cast(g_PushConstants.VertexShaderConstants + 3936))) struct VertexShaderInput { float4 iPosition0 [[attribute(0)]]; float4 iColor0 [[attribute(8)]]; }; struct Interpolators { float4 oPos [[position]]; float4 oTexCoord0 [[user(TEXCOORD0)]]; float4 oTexCoord1 [[user(TEXCOORD1)]]; float4 oTexCoord2 [[user(TEXCOORD2)]]; float4 oTexCoord3 [[user(TEXCOORD3)]]; float4 oTexCoord4 [[user(TEXCOORD4)]]; float4 oTexCoord5 [[user(TEXCOORD5)]]; float4 oTexCoord6 [[user(TEXCOORD6)]]; float4 oTexCoord7 [[user(TEXCOORD7)]]; float4 oTexCoord8 [[user(TEXCOORD8)]]; float4 oTexCoord9 [[user(TEXCOORD9)]]; float4 oTexCoord10 [[user(TEXCOORD10)]]; float4 oTexCoord11 [[user(TEXCOORD11)]]; float4 oTexCoord12 [[user(TEXCOORD12)]]; float4 oTexCoord13 [[user(TEXCOORD13)]]; float4 oTexCoord14 [[user(TEXCOORD14)]]; float4 oTexCoord15 [[user(TEXCOORD15)]]; float4 oColor0 [[user(COLOR0)]]; float4 oColor1 [[user(COLOR1)]]; }; [[vertex]] Interpolators shaderMain(VertexShaderInput input [[stage_in]], constant PushConstants& g_PushConstants [[buffer(8)]]) { Interpolators output; output.oPos.xy = input.iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); output.oPos.z = g_Z.x; output.oPos.w = 1.0; output.oTexCoord0 = input.iColor0.wxyz; output.oTexCoord1 = 0.0; output.oTexCoord2 = 0.0; output.oTexCoord3 = 0.0; output.oTexCoord4 = 0.0; output.oTexCoord5 = 0.0; output.oTexCoord6 = 0.0; output.oTexCoord7 = 0.0; output.oTexCoord8 = 0.0; output.oTexCoord9 = 0.0; output.oTexCoord10 = 0.0; output.oTexCoord11 = 0.0; output.oTexCoord12 = 0.0; output.oTexCoord13 = 0.0; output.oTexCoord14 = 0.0; output.oTexCoord15 = 0.0; output.oColor0 = 0.0; output.oColor1 = 0.0; return output; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/csd_vs.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #define g_ViewportSize (*(reinterpret_cast(g_PushConstants.VertexShaderConstants + 2880))) #define g_Z (*(reinterpret_cast(g_PushConstants.VertexShaderConstants + 3936))) struct VertexShaderInput { float4 iPosition0 [[attribute(0)]]; float4 iColor0 [[attribute(8)]]; float4 iTexCoord0 [[attribute(4)]]; }; struct Interpolators { float4 oPos [[position]]; float4 oTexCoord0 [[user(TEXCOORD0)]]; float4 oTexCoord1 [[user(TEXCOORD1)]]; float4 oTexCoord2 [[user(TEXCOORD2)]]; float4 oTexCoord3 [[user(TEXCOORD3)]]; float4 oTexCoord4 [[user(TEXCOORD4)]]; float4 oTexCoord5 [[user(TEXCOORD5)]]; float4 oTexCoord6 [[user(TEXCOORD6)]]; float4 oTexCoord7 [[user(TEXCOORD7)]]; float4 oTexCoord8 [[user(TEXCOORD8)]]; float4 oTexCoord9 [[user(TEXCOORD9)]]; float4 oTexCoord10 [[user(TEXCOORD10)]]; float4 oTexCoord11 [[user(TEXCOORD11)]]; float4 oTexCoord12 [[user(TEXCOORD12)]]; float4 oTexCoord13 [[user(TEXCOORD13)]]; float4 oTexCoord14 [[user(TEXCOORD14)]]; float4 oTexCoord15 [[user(TEXCOORD15)]]; float4 oColor0 [[user(COLOR0)]]; float4 oColor1 [[user(COLOR1)]]; }; [[vertex]] Interpolators shaderMain(VertexShaderInput input [[stage_in]], constant PushConstants& g_PushConstants [[buffer(8)]]) { Interpolators output; output.oPos.xy = input.iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); output.oPos.z = g_Z.x; output.oPos.w = 1.0; output.oTexCoord0 = input.iColor0.wxyz; output.oTexCoord1.xy = input.iTexCoord0.xy; output.oTexCoord1.zw = 0.0; output.oTexCoord2 = 0.0; output.oTexCoord3 = 0.0; output.oTexCoord4 = 0.0; output.oTexCoord5 = 0.0; output.oTexCoord6 = 0.0; output.oTexCoord7 = 0.0; output.oTexCoord8 = 0.0; output.oTexCoord9 = 0.0; output.oTexCoord10 = 0.0; output.oTexCoord11 = 0.0; output.oTexCoord12 = 0.0; output.oTexCoord13 = 0.0; output.oTexCoord14 = 0.0; output.oTexCoord15 = 0.0; output.oColor0 = 0.0; output.oColor1 = 0.0; return output; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/enhanced_burnout_blur_ps.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #define s0_TextureDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 0))) #define s0_SamplerDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 192))) struct Interpolators { float4 oPos [[position]]; float2 oTexCoord0 [[user(TEXCOORD0)]]; float2 oVelocity [[user(TEXCOORD1)]]; float2 oVelScale [[user(TEXCOORD2)]]; }; [[fragment]] float4 shaderMain(Interpolators input [[stage_in]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { texture2d texture = g_Texture2DDescriptorHeap[s0_TextureDescriptorIndex].tex; sampler samp = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex].samp; float velocityMag = sqrt(dot(input.oVelocity, input.oVelocity)); float blurAmount = saturate(velocityMag - 0.25); if (blurAmount == 0.0) return texture.sample(samp, input.oTexCoord0, level(0)); blurAmount = min(blurAmount * blurAmount, 0.25); float blurStrength = blurAmount * velocityMag; int g_SampleCount = clamp((int)(blurStrength * 256.0), 4, 64); float2 scaledVelStep = input.oVelScale / (float)g_SampleCount; float4 result = float4(0.0); for (int i = 0; i < g_SampleCount; i++) { float2 offset = blurAmount * scaledVelStep * (float)i; float2 samplePos = input.oTexCoord0 + offset; result += texture.sample(samp, samplePos, level(0)); } return result * (1.0 / (float)g_SampleCount); } ================================================ FILE: MarathonRecomp/gpu/shader/msl/enhanced_burnout_blur_vs.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" // #define g_SampleCount (*(reinterpret_cast(g_PushConstants.VertexShaderConstants + 0))) #define g_Velocity (*(reinterpret_cast(g_PushConstants.VertexShaderConstants + 3360))) struct VertexShaderInput { float4 iPosition0 [[attribute(0)]]; float2 iTexCoord0 [[attribute(13)]]; }; struct Interpolators { float4 oPos [[position]]; float2 oTexCoord0 [[user(TEXCOORD0)]]; float2 oVelocity [[user(TEXCOORD1)]]; float2 oVelScale [[user(TEXCOORD2)]]; }; [[vertex]] Interpolators shaderMain(VertexShaderInput input [[stage_in]], constant PushConstants& g_PushConstants [[buffer(8)]]) { Interpolators output; output.oPos = input.iPosition0; output.oTexCoord0 = input.iTexCoord0; float2 centeredUV; centeredUV.x = input.iTexCoord0.y * 2.0 - 1.0; centeredUV.y = input.iTexCoord0.x * 2.0 - 1.0; output.oVelocity.x = -g_Velocity.x - centeredUV.y; output.oVelocity.y = centeredUV.x - g_Velocity.y; float2 scaledVec = output.oVelocity * g_Velocity.w; output.oVelScale.x = scaledVec.x * 0.00002; output.oVelScale.y = -scaledVec.y * 0.00001; return output; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/gamma_correction_ps.metal ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #define g_Gamma (*(reinterpret_cast(g_PushConstants.SharedConstants + 0))) #define g_TextureDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 4))) #define g_ViewportOffset (*(reinterpret_cast(g_PushConstants.SharedConstants + 8))) #define g_ViewportSize (*(reinterpret_cast(g_PushConstants.SharedConstants + 16))) [[fragment]] float4 shaderMain(float4 position [[position]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { texture2d texture = g_Texture2DDescriptorHeap[g_TextureDescriptorIndex].tex; int2 movedPosition = int2(position.xy) - g_ViewportOffset; bool boxed = any(movedPosition < 0) || any(movedPosition >= g_ViewportSize); if (boxed) movedPosition = 0; float4 color = boxed ? 0.0 : texture.read(uint2(movedPosition), 0); color.rgb = pow(color.rgb, g_Gamma); return color; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/gaussian_blur.metali ================================================ #include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" #define g_ViewportSize (*(reinterpret_cast(g_PushConstants.PixelShaderConstants + 384))) #define g_offsets(INDEX) selectWrapper((INDEX) < 74,(*(reinterpret_cast(g_PushConstants.PixelShaderConstants + (150 + min(INDEX, 73)) * 16))), 0.0) #define g_weights (*(reinterpret_cast(g_PushConstants.PixelShaderConstants + 2656))) #define s0_Texture2DDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 0))) #define s0_SamplerDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 192))) #ifdef __INTELLISENSE__ #define KERNEL_SIZE 5 #endif #define PI 3.14159265358979323846 float ComputeWeight(float x) { float std = 0.952; return exp(-(x * x) / (2.0 * std * std)) / (std * sqrt(2.0 * PI)); } struct Interpolators { float4 iTexCoord0 [[user(TEXCOORD0)]]; }; [[fragment]] float4 shaderMain(float4 iPosition [[position]], Interpolators input [[stage_in]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(3)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { texture2d texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex].tex; sampler samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex].samp; float scale; if ((g_ViewportSize.x * g_ViewportSize.w) >= (16.0 / 9.0)) scale = g_ViewportSize.y / 360.0; else scale = g_ViewportSize.x / 640.0; float2 offsets[3]; offsets[0] = g_offsets(0).xy * scale; offsets[1] = g_offsets(0).zw * scale; offsets[2] = g_offsets(1).xy * scale; float4 color = 0.0; float weightSum = 0.0; for (int i = 0; i < KERNEL_SIZE; i++) { float step = i / float(KERNEL_SIZE - 1); float scaled = step * 2; float2 offset = mix(offsets[int(scaled)], offsets[min(int(scaled) + 1, 2)], frac(scaled)); float offsetScale = 1.0 / 0.75; float weight = ComputeWeight(mix(-offsetScale, offsetScale, step)); color += texture.sample(samplerState, input.iTexCoord0.xy + offset, level(0)) * weight; weightSum += weight; } return color / weightSum; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/gaussian_blur_3x3.metal ================================================ #define KERNEL_SIZE 3 #include "gaussian_blur.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/gaussian_blur_5x5.metal ================================================ #define KERNEL_SIZE 5 #include "gaussian_blur.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/gaussian_blur_7x7.metal ================================================ #define KERNEL_SIZE 7 #include "gaussian_blur.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/gaussian_blur_9x9.metal ================================================ #define KERNEL_SIZE 9 #include "gaussian_blur.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/imgui_common.metali ================================================ #pragma once #include using namespace metal; #include "../../imgui/imgui_common.h" struct PushConstants { float2 BoundsMin; float2 BoundsMax; uint GradientTopLeft; uint GradientTopRight; uint GradientBottomRight; uint GradientBottomLeft; uint ShaderModifier; uint Texture2DDescriptorIndex; float2 DisplaySize; float2 InverseDisplaySize; float2 Origin; float2 Scale; float2 ProceduralOrigin; float Outline; }; struct Interpolators { float4 Position [[position]]; float2 UV; float4 Color; }; struct Texture2DDescriptorHeap { texture2d tex; }; struct SamplerDescriptorHeap { sampler samp; }; ================================================ FILE: MarathonRecomp/gpu/shader/msl/imgui_ps.metal ================================================ #include "imgui_common.metali" float4 DecodeColor(uint color) { return float4(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, (color >> 24) & 0xFF) / 255.0; } float4 SamplePoint(int2 position, constant PushConstants& g_PushConstants) { return 1.0; } float4 SampleLinear(float2 uvTexspace, constant PushConstants& g_PushConstants) { int2 integerPart = int2(floor(uvTexspace)); float2 fracPart = fract(uvTexspace); float4 topLeft = SamplePoint(integerPart + int2(0, 0), g_PushConstants); float4 topRight = SamplePoint(integerPart + int2(1, 0), g_PushConstants); float4 bottomLeft = SamplePoint(integerPart + int2(0, 1), g_PushConstants); float4 bottomRight = SamplePoint(integerPart + int2(1, 1), g_PushConstants); float4 top = mix(topLeft, topRight, fracPart.x); float4 bottom = mix(bottomLeft, bottomRight, fracPart.x); return mix(top, bottom, fracPart.y); } float4 PixelAntialiasing(float2 uvTexspace, constant PushConstants& g_PushConstants) { if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0)) uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0; else uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0; float2 seam = floor(uvTexspace + 0.5); uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam; uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5); return SampleLinear(uvTexspace - 0.5, g_PushConstants); } float median(float r, float g, float b) { return max(min(r, g), min(max(r, g), b)); } float4 SampleSdfFont(float4 color, texture2d texture, float2 uv, float2 screenTexSize, constant SamplerDescriptorHeap* g_SamplerDescriptorHeap, constant PushConstants& g_PushConstants) { float4 textureColor = texture.sample(g_SamplerDescriptorHeap[0].samp, uv); uint width = texture.get_width(); uint height = texture.get_height(); float pxRange = 8.0; float2 unitRange = pxRange / float2(width, height); float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0); float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5; float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 1.5)); if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL) { float2 normal = normalize(float3(dfdx(sd), dfdy(sd), 0.01)).xy; float3 rimColor = float3(1, 0.8, 0.29); float3 shadowColor = float3(0.84, 0.57, 0); float cosTheta = dot(normal, normalize(float2(1, 1))); float3 gradient = mix(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta)); color.rgb = mix(gradient, color.rgb, pow(saturate(sd + 0.77), 32.0)); } else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL) { float2 normal = normalize(float3(dfdx(sd), dfdy(sd), 0.25)).xy; float cosTheta = dot(normal, normalize(float2(1, 1))); float gradient = 1.0 + cosTheta * 0.5; color.rgb = saturate(color.rgb * gradient); } else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW) { float2 normal = normalize(float3(dfdx(sd), dfdy(sd), 0.5)).xy; float cosTheta = dot(normal, normalize(float2(1, 1))); float gradient = saturate(1.0 + cosTheta); color.rgb = mix(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0)); } color.a *= saturate(screenPxDistance + 0.5); color.a *= textureColor.a; return color; } [[fragment]] float4 shaderMain(Interpolators interpolators [[stage_in]], constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], constant SamplerDescriptorHeap* g_SamplerDescriptorHeap [[buffer(1)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { float4 color = interpolators.Color; color *= PixelAntialiasing(interpolators.Position.xy - g_PushConstants.ProceduralOrigin, g_PushConstants); if (g_PushConstants.Texture2DDescriptorIndex != 0) { texture2d texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF].tex; if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0) { if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT) { float scale; float invScale; if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0)) { scale = g_PushConstants.InverseDisplaySize.y * 720.0; invScale = g_PushConstants.DisplaySize.y / 720.0; } else { scale = g_PushConstants.InverseDisplaySize.x * 960.0; invScale = g_PushConstants.DisplaySize.x / 960.0; } float2 lowQualityPosition = (interpolators.Position.xy - 0.5) * scale; float2 fracPart = fract(lowQualityPosition); float2 uvStep = fwidth(interpolators.UV) * invScale; float2 lowQualityUV = interpolators.UV - fracPart * uvStep; float2 screenTexSize = 1.0 / uvStep; float4 topLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, 0), screenTexSize, g_SamplerDescriptorHeap, g_PushConstants); float4 topRight = SampleSdfFont(color, texture, lowQualityUV + float2(uvStep.x, 0), screenTexSize, g_SamplerDescriptorHeap, g_PushConstants); float4 bottomLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, uvStep.y), screenTexSize, g_SamplerDescriptorHeap, g_PushConstants); float4 bottomRight = SampleSdfFont(color, texture, lowQualityUV + uvStep.xy, screenTexSize, g_SamplerDescriptorHeap, g_PushConstants); float4 top = mix(topLeft, topRight, fracPart.x); float4 bottom = mix(bottomLeft, bottomRight, fracPart.x); color = mix(top, bottom, fracPart.y); } else { color = SampleSdfFont(color, texture, interpolators.UV, 1.0 / fwidth(interpolators.UV), g_SamplerDescriptorHeap, g_PushConstants); } } else { color *= texture.sample(g_SamplerDescriptorHeap[0].samp, interpolators.UV); } } if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE) { float minAlpha = saturate((interpolators.Position.x - g_PushConstants.BoundsMin.x) / g_PushConstants.Scale.x); float maxAlpha = saturate((g_PushConstants.BoundsMax.x - interpolators.Position.x) / g_PushConstants.Scale.y); color.a *= minAlpha; color.a *= maxAlpha; } else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE) { float minAlpha = saturate((interpolators.Position.y - g_PushConstants.BoundsMin.y) / g_PushConstants.Scale.x); float maxAlpha = saturate((g_PushConstants.BoundsMax.y - interpolators.Position.y) / g_PushConstants.Scale.y); color.a *= minAlpha; color.a *= maxAlpha; } else if (any(g_PushConstants.BoundsMin != g_PushConstants.BoundsMax)) { float2 factor = saturate((interpolators.Position.xy - g_PushConstants.BoundsMin) / (g_PushConstants.BoundsMax - g_PushConstants.BoundsMin)); if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL) { float bevelSize = 0.9; float shadow = saturate((factor.x - bevelSize) / (1.0 - bevelSize)); shadow = max(shadow, saturate((factor.y - bevelSize) / (1.0 - bevelSize))); float rim = saturate((1.0 - factor.x - bevelSize) / (1.0 - bevelSize)); rim = max(rim, saturate((1.0 - factor.y - bevelSize) / (1.0 - bevelSize))); float3 rimColor = float3(1, 0.8, 0.29); float3 shadowColor = float3(0.84, 0.57, 0); color.rgb = mix(color.rgb, rimColor, smoothstep(0.0, 1.0, rim)); color.rgb = mix(color.rgb, shadowColor, smoothstep(0.0, 1.0, shadow)); } else { float4 top = mix(DecodeColor(g_PushConstants.GradientTopLeft), DecodeColor(g_PushConstants.GradientTopRight), smoothstep(0.0, 1.0, factor.x)); float4 bottom = mix(DecodeColor(g_PushConstants.GradientBottomLeft), DecodeColor(g_PushConstants.GradientBottomRight), smoothstep(0.0, 1.0, factor.x)); color *= mix(top, bottom, smoothstep(0.0, 1.0, factor.y)); } } if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_GRAYSCALE) color.rgb = dot(color.rgb, float3(0.2126, 0.7152, 0.0722)); return color; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/imgui_vs.metal ================================================ #include "imgui_common.metali" struct VertexStageIn { float2 position [[attribute(0)]]; float2 uv [[attribute(1)]]; float4 color [[attribute(2)]]; }; [[vertex]] Interpolators shaderMain(VertexStageIn input [[stage_in]], constant PushConstants& g_PushConstants [[buffer(8)]]) { Interpolators interpolators = Interpolators{}; if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW) { if (input.position.y < g_PushConstants.Origin.y) input.position.x += g_PushConstants.Scale.x; } else if (g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE && g_PushConstants.ShaderModifier != IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE) { input.position.xy = g_PushConstants.Origin + (input.position.xy - g_PushConstants.Origin) * g_PushConstants.Scale; } interpolators.Position = float4(input.position.xy * g_PushConstants.InverseDisplaySize * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); interpolators.UV = input.uv; interpolators.Color = input.color; return interpolators; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_color.metali ================================================ #pragma once #include "copy_common.metali" struct Texture2DMSDescriptorHeap { texture2d_ms tex; }; [[fragment]] float4 shaderMain(float4 position [[position]], constant Texture2DMSDescriptorHeap* g_Texture2DMSDescriptorHeap [[buffer(0)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { float4 result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0); for (int i = 1; i < SAMPLE_COUNT; i++) result += g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), i); return result / SAMPLE_COUNT; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_color_2x.metal ================================================ #define SAMPLE_COUNT 2 #include "resolve_msaa_color.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_color_4x.metal ================================================ #define SAMPLE_COUNT 4 #include "resolve_msaa_color.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_color_8x.metal ================================================ #define SAMPLE_COUNT 8 #include "resolve_msaa_color.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_depth.metali ================================================ #pragma once #include "copy_common.metali" struct Texture2DMSDescriptorHeap { texture2d_ms tex; }; struct PixelShaderOutput { float oDepth [[depth(any)]]; }; [[fragment]] PixelShaderOutput shaderMain(float4 position [[position]], constant Texture2DMSDescriptorHeap* g_Texture2DMSDescriptorHeap [[buffer(0)]], constant PushConstants& g_PushConstants [[buffer(8)]]) { PixelShaderOutput output = PixelShaderOutput{}; float result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), 0).x; for (int i = 1; i < SAMPLE_COUNT; i++) result = min(result, g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].tex.read(uint2(position.xy), i).x); output.oDepth = result; return output; } ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_depth_2x.metal ================================================ #define SAMPLE_COUNT 2 #include "resolve_msaa_depth.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_depth_4x.metal ================================================ #define SAMPLE_COUNT 4 #include "resolve_msaa_depth.metali" ================================================ FILE: MarathonRecomp/gpu/shader/msl/resolve_msaa_depth_8x.metal ================================================ #define SAMPLE_COUNT 8 #include "resolve_msaa_depth.metali" ================================================ FILE: MarathonRecomp/gpu/video.cpp ================================================ #include "video.h" #include "imgui/imgui_common.h" #include "imgui/imgui_snapshot.h" #include "imgui/imgui_font_builder.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING) #include #endif #define MARATHON_RECOMP #include "../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef MARATHON_RECOMP_D3D12 #include "shader/hlsl/blend_color_alpha_ps.hlsl.dxil.h" #include "shader/hlsl/conditional_survey_ps.hlsl.dxil.h" #include "shader/hlsl/copy_vs.hlsl.dxil.h" #include "shader/hlsl/copy_color_ps.hlsl.dxil.h" #include "shader/hlsl/copy_depth_ps.hlsl.dxil.h" #include "shader/hlsl/csd_filter_ps.hlsl.dxil.h" #include "shader/hlsl/csd_no_tex_vs.hlsl.dxil.h" #include "shader/hlsl/csd_vs.hlsl.dxil.h" #include "shader/hlsl/enhanced_burnout_blur_vs.hlsl.dxil.h" #include "shader/hlsl/enhanced_burnout_blur_ps.hlsl.dxil.h" #include "shader/hlsl/gamma_correction_ps.hlsl.dxil.h" #include "shader/hlsl/gaussian_blur_3x3.hlsl.dxil.h" #include "shader/hlsl/gaussian_blur_5x5.hlsl.dxil.h" #include "shader/hlsl/gaussian_blur_7x7.hlsl.dxil.h" #include "shader/hlsl/gaussian_blur_9x9.hlsl.dxil.h" #include "shader/hlsl/imgui_ps.hlsl.dxil.h" #include "shader/hlsl/imgui_vs.hlsl.dxil.h" #include "shader/hlsl/resolve_msaa_color_2x.hlsl.dxil.h" #include "shader/hlsl/resolve_msaa_color_4x.hlsl.dxil.h" #include "shader/hlsl/resolve_msaa_color_8x.hlsl.dxil.h" #include "shader/hlsl/resolve_msaa_depth_2x.hlsl.dxil.h" #include "shader/hlsl/resolve_msaa_depth_4x.hlsl.dxil.h" #include "shader/hlsl/resolve_msaa_depth_8x.hlsl.dxil.h" #endif #ifdef MARATHON_RECOMP_METAL #include "shader/msl/blend_color_alpha_ps.metal.metallib.h" #include "shader/msl/conditional_survey_ps.metal.metallib.h" #include "shader/msl/copy_vs.metal.metallib.h" #include "shader/msl/copy_color_ps.metal.metallib.h" #include "shader/msl/copy_depth_ps.metal.metallib.h" #include "shader/msl/csd_filter_ps.metal.metallib.h" #include "shader/msl/csd_no_tex_vs.metal.metallib.h" #include "shader/msl/csd_vs.metal.metallib.h" #include "shader/msl/enhanced_burnout_blur_vs.metal.metallib.h" #include "shader/msl/enhanced_burnout_blur_ps.metal.metallib.h" #include "shader/msl/gamma_correction_ps.metal.metallib.h" #include "shader/msl/gaussian_blur_3x3.metal.metallib.h" #include "shader/msl/gaussian_blur_5x5.metal.metallib.h" #include "shader/msl/gaussian_blur_7x7.metal.metallib.h" #include "shader/msl/gaussian_blur_9x9.metal.metallib.h" #include "shader/msl/imgui_ps.metal.metallib.h" #include "shader/msl/imgui_vs.metal.metallib.h" #include "shader/msl/resolve_msaa_color_2x.metal.metallib.h" #include "shader/msl/resolve_msaa_color_4x.metal.metallib.h" #include "shader/msl/resolve_msaa_color_8x.metal.metallib.h" #include "shader/msl/resolve_msaa_depth_2x.metal.metallib.h" #include "shader/msl/resolve_msaa_depth_4x.metal.metallib.h" #include "shader/msl/resolve_msaa_depth_8x.metal.metallib.h" #endif #include "shader/hlsl/blend_color_alpha_ps.hlsl.spirv.h" #include "shader/hlsl/conditional_survey_ps.hlsl.spirv.h" #include "shader/hlsl/copy_vs.hlsl.spirv.h" #include "shader/hlsl/copy_color_ps.hlsl.spirv.h" #include "shader/hlsl/copy_depth_ps.hlsl.spirv.h" #include "shader/hlsl/csd_filter_ps.hlsl.spirv.h" #include "shader/hlsl/csd_no_tex_vs.hlsl.spirv.h" #include "shader/hlsl/csd_vs.hlsl.spirv.h" #include "shader/hlsl/enhanced_burnout_blur_vs.hlsl.spirv.h" #include "shader/hlsl/enhanced_burnout_blur_ps.hlsl.spirv.h" #include "shader/hlsl/gamma_correction_ps.hlsl.spirv.h" #include "shader/hlsl/gaussian_blur_3x3.hlsl.spirv.h" #include "shader/hlsl/gaussian_blur_5x5.hlsl.spirv.h" #include "shader/hlsl/gaussian_blur_7x7.hlsl.spirv.h" #include "shader/hlsl/gaussian_blur_9x9.hlsl.spirv.h" #include "shader/hlsl/imgui_ps.hlsl.spirv.h" #include "shader/hlsl/imgui_vs.hlsl.spirv.h" #include "shader/hlsl/resolve_msaa_color_2x.hlsl.spirv.h" #include "shader/hlsl/resolve_msaa_color_4x.hlsl.spirv.h" #include "shader/hlsl/resolve_msaa_color_8x.hlsl.spirv.h" #include "shader/hlsl/resolve_msaa_depth_2x.hlsl.spirv.h" #include "shader/hlsl/resolve_msaa_depth_4x.hlsl.spirv.h" #include "shader/hlsl/resolve_msaa_depth_8x.hlsl.spirv.h" #ifdef _WIN32 extern "C" { __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif namespace plume { #ifdef MARATHON_RECOMP_D3D12 extern std::unique_ptr CreateD3D12Interface(); #endif #ifdef MARATHON_RECOMP_METAL extern std::unique_ptr CreateMetalInterface(); #endif #ifdef PLUME_SDL_VULKAN_ENABLED extern std::unique_ptr CreateVulkanInterface(RenderWindow sdlWindow); #else extern std::unique_ptr CreateVulkanInterface(); #endif static std::unique_ptr CreateVulkanInterfaceWrapper() { #ifdef PLUME_SDL_VULKAN_ENABLED return CreateVulkanInterface(GameWindow::s_renderWindow); #else return CreateVulkanInterface(); #endif } } using namespace plume; #pragma pack(push, 1) struct PipelineState { GuestShader* vertexShader = nullptr; GuestShader* pixelShader = nullptr; GuestVertexDeclaration* vertexDeclaration = nullptr; bool zEnable = true; bool zWriteEnable = true; bool stencilEnable = false; bool stencilTwoSided = false; RenderBlend srcBlend = RenderBlend::ONE; RenderBlend destBlend = RenderBlend::ZERO; RenderCullMode cullMode = RenderCullMode::NONE; RenderFrontFace frontFace = RenderFrontFace::CLOCKWISE; RenderComparisonFunction zFunc = RenderComparisonFunction::LESS; RenderComparisonFunction stencilFunc = RenderComparisonFunction::ALWAYS; RenderStencilOp stencilFail = RenderStencilOp::KEEP; RenderStencilOp stencilZFail = RenderStencilOp::KEEP; RenderStencilOp stencilPass = RenderStencilOp::KEEP; RenderComparisonFunction stencilFuncCCW = RenderComparisonFunction::ALWAYS; RenderStencilOp stencilFailCCW = RenderStencilOp::KEEP; RenderStencilOp stencilZFailCCW = RenderStencilOp::KEEP; RenderStencilOp stencilPassCCW = RenderStencilOp::KEEP; uint32_t stencilMask = 0xFFFFFFFF; uint32_t stencilWriteMask = 0xFFFFFFFF; uint32_t stencilRef = 0; bool alphaBlendEnable = false; RenderBlendOperation blendOp = RenderBlendOperation::ADD; float slopeScaledDepthBias = 0.0f; int32_t depthBias = 0; RenderBlend srcBlendAlpha = RenderBlend::ONE; RenderBlend destBlendAlpha = RenderBlend::ZERO; RenderBlendOperation blendOpAlpha = RenderBlendOperation::ADD; uint32_t colorWriteEnable = uint32_t(RenderColorWriteEnable::ALL); RenderPrimitiveTopology primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST; uint8_t vertexStrides[16]{}; RenderFormat renderTargetFormat{}; RenderFormat depthStencilFormat{}; RenderSampleCounts sampleCount = RenderSampleCount::COUNT_1; bool enableAlphaToCoverage = false; bool enableConditionalSurvey = false; uint32_t specConstants = 0; }; #pragma pack(pop) struct UploadAllocation { const RenderBuffer* buffer; uint64_t offset; uint8_t* memory; uint64_t deviceAddress; }; struct SharedConstants { uint32_t texture2DIndices[16]{}; uint32_t texture2DArrayIndices[16]{}; uint32_t textureCubeIndices[16]{}; uint32_t samplerIndices[16]{}; uint32_t booleans{}; uint32_t swappedTexcoords{}; uint32_t swappedNormals{}; uint32_t swappedBinormals{}; uint32_t swappedTangents{}; uint32_t swappedBlendWeights{}; float halfPixelOffsetX{}; float halfPixelOffsetY{}; float clipPlane[4]{}; bool clipPlaneEnabled{}; float alphaThreshold{}; uint32_t conditionalSurveyIndex{}; uint32_t conditionalRenderingIndex{}; }; // Depth bias values here are only used when the render device has // dynamic depth bias capability enabled. Otherwise, they get unused // and the values get assigned in the pipeline state instead. static GuestSurface* g_renderTarget; static GuestSurface* g_depthStencil; static RenderFramebuffer* g_framebuffer; static RenderViewport g_viewport(0.0f, 0.0f, 1280.0f, 720.0f); static PipelineState g_pipelineState; static int32_t g_depthBias; static float g_slopeScaledDepthBias; static uint32_t g_vertexShaderConstants[0x400]; static uint32_t g_pixelShaderConstants[0x380]; static SharedConstants g_sharedConstants; static GuestTexture* g_textures[16]; static RenderSamplerDesc g_samplerDescs[16]; static bool g_scissorTestEnable = false; static RenderRect g_scissorRect; static RenderVertexBufferView g_vertexBufferViews[16]; static RenderInputSlot g_inputSlots[16]; static RenderIndexBufferView g_indexBufferView({}, 0, RenderFormat::R16_UINT); struct DirtyStates { bool renderTargetAndDepthStencil; bool viewport; bool pipelineState; bool depthBias; bool sharedConstants; bool scissorRect; bool vertexShaderConstants; uint8_t vertexStreamFirst; uint8_t vertexStreamLast; bool indices; bool pixelShaderConstants; DirtyStates(bool value) : renderTargetAndDepthStencil(value) , viewport(value) , pipelineState(value) , depthBias(value) , sharedConstants(value) , scissorRect(value) , vertexShaderConstants(value) , vertexStreamFirst(value ? 0 : 255) , vertexStreamLast(value ? 15 : 0) , indices(value) , pixelShaderConstants(value) { } }; static DirtyStates g_dirtyStates(true); template static void SetDirtyValue(bool& dirtyState, T& dest, const T& src) { if (dest != src) { dest = src; dirtyState = true; } } static constexpr size_t PROFILER_VALUE_COUNT = 256; static size_t g_profilerValueIndex; struct Profiler { std::atomic value; double values[PROFILER_VALUE_COUNT]; std::chrono::steady_clock::time_point start; void Begin() { start = std::chrono::steady_clock::now(); } void End() { value = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); } void Set(double v) { value = v; } void Reset() { End(); Begin(); } double UpdateAndReturnAverage() { values[g_profilerValueIndex] = value; return std::accumulate(values, values + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT; } }; static double g_applicationValues[PROFILER_VALUE_COUNT]; static Profiler g_gpuFrameProfiler; static Profiler g_presentProfiler; static Profiler g_frameFenceProfiler; static Profiler g_presentWaitProfiler; static Profiler g_swapChainAcquireProfiler; static bool g_profilerVisible; static bool g_profilerWasToggled; #if !defined(MARATHON_RECOMP_D3D12) && !defined(MARATHON_RECOMP_METAL) static constexpr Backend g_backend = Backend::VULKAN; #else static Backend g_backend; #endif static bool g_triangleStripWorkaround = false; static std::unique_ptr g_interface; static std::unique_ptr g_device; static RenderDeviceCapabilities g_capabilities; static constexpr size_t NUM_FRAMES = 2; static constexpr size_t NUM_QUERIES = 2; static uint32_t g_frame = 0; static uint32_t g_nextFrame = 1; static std::unique_ptr g_queue; static std::unique_ptr g_commandLists[NUM_FRAMES]; static std::unique_ptr g_commandFences[NUM_FRAMES]; static std::unique_ptr g_queryPools[NUM_FRAMES]; static bool g_commandListStates[NUM_FRAMES]; static Mutex g_copyMutex; static std::unique_ptr g_copyQueue; static std::unique_ptr g_copyCommandList; static std::unique_ptr g_copyCommandFence; static Mutex g_discardMutex; static std::unique_ptr g_discardCommandList; static std::unique_ptr g_discardCommandFence; static std::unique_ptr g_swapChain; static bool g_swapChainValid; static constexpr RenderFormat BACKBUFFER_FORMAT = RenderFormat::B8G8R8A8_UNORM; static std::unique_ptr g_acquireSemaphores[NUM_FRAMES]; static std::unique_ptr g_renderSemaphores[NUM_FRAMES]; static uint32_t g_backBufferIndex; static std::unique_ptr g_backBufferHolder; static GuestSurface* g_backBuffer; static std::unique_ptr g_intermediaryBackBufferTexture; static uint32_t g_intermediaryBackBufferTextureWidth; static uint32_t g_intermediaryBackBufferTextureHeight; static uint32_t g_intermediaryBackBufferTextureDescriptorIndex; static std::unique_ptr g_gammaCorrectionPipeline; static std::unique_ptr g_textureDescriptorSet; static std::unique_ptr g_samplerDescriptorSet; static constexpr uint32_t CONDITIONAL_SURVEY_MAX = 64; static std::unique_ptr g_conditionalSurveyBuffer; static std::unique_ptr g_conditionalSurveyDescriptorSet; enum { TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D, TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D_ARRAY, TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE, TEXTURE_DESCRIPTOR_NULL_COUNT }; struct TextureDescriptorAllocator { Mutex mutex; uint32_t capacity = TEXTURE_DESCRIPTOR_NULL_COUNT; std::vector freed; uint32_t allocate() { std::lock_guard lock(mutex); uint32_t value; if (!freed.empty()) { value = freed.back(); freed.pop_back(); } else { value = capacity; ++capacity; } return value; } void free(uint32_t value) { assert(value != NULL); std::lock_guard lock(mutex); freed.push_back(value); } }; static std::unique_ptr g_blankTextures[TEXTURE_DESCRIPTOR_NULL_COUNT]; static std::unique_ptr g_blankTextureViews[TEXTURE_DESCRIPTOR_NULL_COUNT]; static TextureDescriptorAllocator g_textureDescriptorAllocator; static std::unique_ptr g_pipelineLayout; static xxHashMap> g_pipelines; #ifdef ASYNC_PSO_DEBUG static std::atomic g_pipelinesCreatedInRenderThread; static std::atomic g_pipelinesCreatedAsynchronously; static std::atomic g_pipelinesDropped; static std::atomic g_pipelinesCurrentlyCompiling; static std::string g_pipelineDebugText; static Mutex g_debugMutex; #endif #ifdef PSO_CACHING static xxHashMap g_pipelineStatesToCache; static Mutex g_pipelineCacheMutex; #endif static std::atomic g_compilingPipelineTaskCount; static std::atomic g_pendingPipelineTaskCount; enum class PipelineTaskType { Null, DatabaseData, PrecompilePipelines, RecompilePipelines }; struct PipelineTask { PipelineTaskType type{}; // boost::shared_ptr databaseData; }; static Mutex g_pipelineTaskMutex; static std::vector g_pipelineTaskQueue; //static void EnqueuePipelineTask(PipelineTaskType type, const boost::shared_ptr& databaseData) //{ // // Precompiled pipelines deliberately do not increment // // this counter to overlap the compilation with intro logos. // if (type != PipelineTaskType::PrecompilePipelines) // ++g_compilingPipelineTaskCount; // // { // std::lock_guard lock(g_pipelineTaskMutex); // g_pipelineTaskQueue.emplace_back(type, databaseData); // } // // if ((++g_pendingPipelineTaskCount) == 1) // g_pendingPipelineTaskCount.notify_one(); //} static const PipelineState g_pipelineStateCache[] = { #include "cache/pipeline_state_cache.h" }; #include "cache/vertex_element_cache.h" static uint8_t* const g_vertexDeclarationCache[] = { #include "cache/vertex_declaration_cache.h" }; static xxHashMap>> g_samplerStates; static Mutex g_vertexDeclarationMutex; static xxHashMap g_vertexDeclarations; struct UploadBuffer { static constexpr size_t SIZE = 16 * 1024 * 1024; std::unique_ptr buffer; uint8_t* memory = nullptr; uint64_t deviceAddress = 0; }; struct UploadAllocator { std::vector buffers; uint32_t index = 0; uint32_t offset = 0; UploadAllocation allocate(uint32_t size, uint32_t alignment) { assert(size <= UploadBuffer::SIZE); offset = (offset + alignment - 1) & ~(alignment - 1); if (offset + size > UploadBuffer::SIZE) { ++index; offset = 0; } if (buffers.size() <= index) buffers.resize(index + 1); auto& buffer = buffers[index]; if (buffer.buffer == nullptr) { buffer.buffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(UploadBuffer::SIZE, RenderBufferFlag::CONSTANT | RenderBufferFlag::VERTEX | RenderBufferFlag::INDEX | RenderBufferFlag::DEVICE_ADDRESSABLE)); buffer.memory = reinterpret_cast(buffer.buffer->map()); buffer.deviceAddress = buffer.buffer->getDeviceAddress(); } auto ref = buffer.buffer->at(offset); offset += size; return { ref.ref, ref.offset, buffer.memory + ref.offset, buffer.deviceAddress + ref.offset }; } template UploadAllocation allocate(const T* memory, uint32_t size, uint32_t alignment) { auto result = allocate(size, alignment); if constexpr (TByteSwap) { auto destination = reinterpret_cast(result.memory); for (size_t i = 0; i < size; i += sizeof(T)) { *destination = ByteSwap(*memory); ++destination; ++memory; } } else { memcpy(result.memory, memory, size); } return result; } void reset() { index = 0; offset = 0; } }; static UploadAllocator g_uploadAllocators[NUM_FRAMES]; struct IntermediaryUploadAllocator { static constexpr size_t SIZE = 16 * 1024 * 1024; std::vector> buffers; uint32_t index = 0; uint32_t offset = 0; uint8_t* allocate(uint32_t size) { assert(size <= SIZE); if (offset + size > SIZE) { ++index; offset = 0; } if (buffers.size() <= index) buffers.resize(index + 1); auto& buffer = buffers[index]; if (buffer == nullptr) buffer = std::make_unique_for_overwrite(SIZE); auto result = buffer.get() + offset; offset += ((size + 0xF) & ~0xF); return result; } uint8_t* allocate(const void* memory, uint32_t size) { auto result = allocate(size); memcpy(result, memory, size); return result; } void reset() { index = 0; offset = 0; } }; static IntermediaryUploadAllocator g_intermediaryUploadAllocator; static std::vector g_tempResources[NUM_FRAMES]; static std::vector> g_tempBuffers[NUM_FRAMES]; template struct PrimitiveIndexData { std::vector indexData; RenderBufferReference indexBuffer; uint32_t currentIndexCount = 0; uint32_t prepare(uint32_t guestPrimCount) { uint32_t primCount; uint32_t indexCountPerPrimitive; switch (PrimitiveType) { case D3DPT_TRIANGLEFAN: primCount = guestPrimCount - 2; indexCountPerPrimitive = 3; break; case D3DPT_QUADLIST: primCount = guestPrimCount / 4; indexCountPerPrimitive = 6; break; default: assert(false && "Unknown primitive type."); break; } uint32_t indexCount = primCount * indexCountPerPrimitive; if (indexData.size() < indexCount) { const size_t oldPrimCount = indexData.size() / indexCountPerPrimitive; indexData.resize(indexCount); for (size_t i = oldPrimCount; i < primCount; i++) { switch (PrimitiveType) { case D3DPT_TRIANGLEFAN: { indexData[i * 3 + 0] = 0; indexData[i * 3 + 1] = static_cast(i + 1); indexData[i * 3 + 2] = static_cast(i + 2); break; } case D3DPT_QUADLIST: { indexData[i * 6 + 0] = static_cast(i * 4 + 0); indexData[i * 6 + 1] = static_cast(i * 4 + 1); indexData[i * 6 + 2] = static_cast(i * 4 + 2); indexData[i * 6 + 3] = static_cast(i * 4 + 0); indexData[i * 6 + 4] = static_cast(i * 4 + 2); indexData[i * 6 + 5] = static_cast(i * 4 + 3); break; } default: assert(false && "Unknown primitive type."); break; } } } if (indexBuffer == NULL || currentIndexCount < indexCount) { auto allocation = g_uploadAllocators[g_frame].allocate(indexData.data(), indexCount * 2, 2); indexBuffer = allocation.buffer->at(allocation.offset); currentIndexCount = indexCount; } SetDirtyValue(g_dirtyStates.indices, g_indexBufferView.buffer, indexBuffer); SetDirtyValue(g_dirtyStates.indices, g_indexBufferView.size, indexCount * 2); SetDirtyValue(g_dirtyStates.indices, g_indexBufferView.format, RenderFormat::R16_UINT); return indexCount; } void reset() { indexBuffer = {}; currentIndexCount = 0; } }; static PrimitiveIndexData g_triangleFanIndexData; static PrimitiveIndexData g_quadIndexData; static void DestructTempResources() { for (auto resource : g_tempResources[g_frame]) { switch (resource->type) { case ResourceType::Texture: case ResourceType::VolumeTexture: case ResourceType::ArrayTexture: { const auto texture = reinterpret_cast(resource); if (texture->mappedMemory != nullptr) { g_userHeap.Free(texture->mappedMemory); } g_textureDescriptorSet->setTexture(texture->descriptorIndex, nullptr, {}); g_textureDescriptorAllocator.free(texture->descriptorIndex); if (texture->patchedTexture != nullptr) { g_textureDescriptorSet->setTexture(texture->patchedTexture->descriptorIndex, nullptr, {}); g_textureDescriptorAllocator.free(texture->patchedTexture->descriptorIndex); } texture->~GuestTexture(); break; } case ResourceType::VertexBuffer: case ResourceType::IndexBuffer: { const auto buffer = reinterpret_cast(resource); if (buffer->mappedMemory != nullptr) g_userHeap.Free(buffer->mappedMemory); buffer->~GuestBuffer(); break; } case ResourceType::RenderTarget: case ResourceType::DepthStencil: { const auto surface = reinterpret_cast(resource); if (surface->descriptorIndex != NULL) { g_textureDescriptorSet->setTexture(surface->descriptorIndex, nullptr, {}); g_textureDescriptorAllocator.free(surface->descriptorIndex); } surface->~GuestSurface(); break; } case ResourceType::VertexDeclaration: reinterpret_cast(resource)->~GuestVertexDeclaration(); break; case ResourceType::VertexShader: case ResourceType::PixelShader: { reinterpret_cast(resource)->~GuestShader(); break; } } g_userHeap.Free(resource); } g_tempResources[g_frame].clear(); g_tempBuffers[g_frame].clear(); } static std::thread::id g_presentThreadId = std::this_thread::get_id(); static std::atomic g_readyForCommands; // PPC_FUNC_IMPL(__imp__sub_824ECA00); // PPC_FUNC(sub_824ECA00) // { // g_readyForCommands.wait(false); // g_presentThreadId = std::this_thread::get_id(); // __imp__sub_824ECA00(ctx, base); // } static ankerl::unordered_dense::map g_barrierMap; static void AddBarrier(GuestBaseTexture* texture, RenderTextureLayout layout) { if (texture != nullptr && texture->layout != layout) { g_barrierMap[texture->texture] = layout; texture->layout = layout; } } static std::vector g_barriers; static void FlushBarriers() { if (!g_barrierMap.empty()) { for (auto& [texture, layout] : g_barrierMap) g_barriers.emplace_back(texture, layout); g_commandLists[g_frame]->barriers(RenderBarrierStage::GRAPHICS | RenderBarrierStage::COPY, g_barriers); g_barrierMap.clear(); g_barriers.clear(); } } static std::unique_ptr g_shaderCache; static std::unique_ptr g_buttonBcDiff; static void LoadEmbeddedResources() { switch (g_backend) { case Backend::VULKAN: g_shaderCache = std::make_unique(g_spirvCacheDecompressedSize); ZSTD_decompress(g_shaderCache.get(), g_spirvCacheDecompressedSize, g_compressedSpirvCache, g_spirvCacheCompressedSize); break; #if defined(MARATHON_RECOMP_D3D12) case Backend::D3D12: g_shaderCache = std::make_unique(g_dxilCacheDecompressedSize); ZSTD_decompress(g_shaderCache.get(), g_dxilCacheDecompressedSize, g_compressedDxilCache, g_dxilCacheCompressedSize); break; #elif defined(MARATHON_RECOMP_METAL) case Backend::METAL: g_shaderCache = std::make_unique(g_airCacheDecompressedSize); ZSTD_decompress(g_shaderCache.get(), g_airCacheDecompressedSize, g_compressedAirCache, g_airCacheCompressedSize); break; #endif default: assert(false); } g_buttonBcDiff = decompressZstd(g_button_bc_diff, g_button_bc_diff_uncompressed_size); } enum class CsdFilterState { Unknown, On, Off }; static CsdFilterState g_csdFilterState; static ankerl::unordered_dense::set g_pendingSurfaceCopies; static ankerl::unordered_dense::set g_pendingResolves; enum class RenderCommandType { SetRenderState, DestructResource, UnlockTextureRect, UnlockBuffer16, UnlockBuffer32, DrawImGui, ExecuteCommandList, BeginCommandList, StretchRect, SetRenderTarget, SetDepthStencilSurface, ExecutePendingStretchRectCommands, Clear, SetViewport, SetTexture, SetScissorRect, SetSamplerState, SetBooleans, SetVertexShaderConstants, SetPixelShaderConstants, AddPipeline, DrawPrimitive, DrawIndexedPrimitive, DrawPrimitiveUP, SetVertexDeclaration, SetVertexShader, SetStreamSource, SetIndices, SetPixelShader, SetConditionalSurvey, SetConditionalRendering, }; struct RenderCommand { RenderCommandType type; union { struct { GuestRenderState type; uint32_t value; } setRenderState; struct { GuestResource* resource; } destructResource; struct { GuestTexture* texture; } unlockTextureRect; struct { GuestBuffer* buffer; } unlockBuffer; struct { GuestDevice* device; uint32_t flags; GuestTexture* texture; uint32_t destSliceOrFace; } stretchRect; struct { GuestSurface* renderTarget; } setRenderTarget; struct { GuestSurface* depthStencil; } setDepthStencilSurface; struct { uint32_t flags; float color[4]; float z; uint32_t stencil; } clear; struct { float x; float y; float width; float height; float minDepth; float maxDepth; } setViewport; struct { uint32_t index; GuestTexture* texture; } setTexture; struct { int32_t left; int32_t top; int32_t right; int32_t bottom; } setScissorRect; struct { uint32_t index; uint32_t data0; uint32_t data3; uint32_t data5; } setSamplerState; struct { uint32_t booleans; } setBooleans; struct { uint8_t* memory; uint32_t index; uint32_t size; } setVertexShaderConstants; struct { uint8_t* memory; uint32_t index; uint32_t size; } setPixelShaderConstants; struct { XXH64_hash_t hash; RenderPipeline* pipeline; } addPipeline; struct { uint32_t primitiveType; uint32_t startVertex; uint32_t primitiveCount; } drawPrimitive; struct { uint32_t primitiveType; int32_t baseVertexIndex; uint32_t startIndex; uint32_t primCount; } drawIndexedPrimitive; struct { uint32_t primitiveType; uint32_t primitiveCount; uint8_t* vertexStreamZeroData; uint32_t vertexStreamZeroSize; uint32_t vertexStreamZeroStride; CsdFilterState csdFilterState; } drawPrimitiveUP; struct { GuestVertexDeclaration* vertexDeclaration; } setVertexDeclaration; struct { GuestShader* shader; } setVertexShader; struct { uint32_t index; GuestBuffer* buffer; uint32_t offset; uint32_t stride; } setStreamSource; struct { GuestBuffer* buffer; } setIndices; struct { GuestShader* shader; } setPixelShader; struct { bool enabled; uint32_t index; } setConditionalSurvey; struct { bool enabled; uint32_t index; } setConditionalRendering; }; }; static moodycamel::BlockingConcurrentQueue g_renderQueue; template static void SetRenderState(GuestDevice* device, uint32_t value) { RenderCommand cmd; cmd.type = RenderCommandType::SetRenderState; cmd.setRenderState.type = TType; cmd.setRenderState.value = value; g_renderQueue.enqueue(cmd); } static void SetRenderStateUnimplemented(GuestDevice* device, uint32_t value) { LOGF_WARNING("{:x}\n", value); } static void SetAlphaTestMode(bool enable) { uint32_t specConstants = 0; bool enableAlphaToCoverage = false; if (enable) { enableAlphaToCoverage = Config::TransparencyAntiAliasing && g_renderTarget != nullptr && g_renderTarget->sampleCount != RenderSampleCount::COUNT_1; if (enableAlphaToCoverage) specConstants = SPEC_CONSTANT_ALPHA_TO_COVERAGE; else specConstants = SPEC_CONSTANT_ALPHA_TEST; } specConstants |= (g_pipelineState.specConstants & ~(SPEC_CONSTANT_ALPHA_TEST | SPEC_CONSTANT_ALPHA_TO_COVERAGE)); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.enableAlphaToCoverage, enableAlphaToCoverage); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.specConstants, specConstants); } static RenderBlend ConvertBlendMode(uint32_t blendMode) { switch (blendMode) { case D3DBLEND_ZERO: return RenderBlend::ZERO; case D3DBLEND_ONE: return RenderBlend::ONE; case D3DBLEND_SRCCOLOR: return RenderBlend::SRC_COLOR; case D3DBLEND_INVSRCCOLOR: return RenderBlend::INV_SRC_COLOR; case D3DBLEND_SRCALPHA: return RenderBlend::SRC_ALPHA; case D3DBLEND_INVSRCALPHA: return RenderBlend::INV_SRC_ALPHA; case D3DBLEND_DESTCOLOR: return RenderBlend::DEST_COLOR; case D3DBLEND_INVDESTCOLOR: return RenderBlend::INV_DEST_COLOR; case D3DBLEND_DESTALPHA: return RenderBlend::DEST_ALPHA; case D3DBLEND_INVDESTALPHA: return RenderBlend::INV_DEST_ALPHA; default: assert(false && "Invalid blend mode"); return RenderBlend::ZERO; } } static RenderBlendOperation ConvertBlendOp(uint32_t blendOp) { switch (blendOp) { case D3DBLENDOP_ADD: return RenderBlendOperation::ADD; case D3DBLENDOP_SUBTRACT: return RenderBlendOperation::SUBTRACT; case D3DBLENDOP_REVSUBTRACT: return RenderBlendOperation::REV_SUBTRACT; case D3DBLENDOP_MIN: return RenderBlendOperation::MIN; case D3DBLENDOP_MAX: return RenderBlendOperation::MAX; default: assert(false && "Unknown blend operation"); return RenderBlendOperation::ADD; } } static RenderComparisonFunction ConvertCompareFunc(uint32_t compareFunc) { switch (compareFunc) { case D3DCMP_NEVER: return RenderComparisonFunction::NEVER; case D3DCMP_LESS: return RenderComparisonFunction::LESS; case D3DCMP_EQUAL: return RenderComparisonFunction::EQUAL; case D3DCMP_LESSEQUAL: return RenderComparisonFunction::LESS_EQUAL; case D3DCMP_GREATER: return RenderComparisonFunction::GREATER; case D3DCMP_NOTEQUAL: return RenderComparisonFunction::NOT_EQUAL; case D3DCMP_GREATEREQUAL: return RenderComparisonFunction::GREATER_EQUAL; case D3DCMP_ALWAYS: return RenderComparisonFunction::ALWAYS; default: assert(false && "Unknown comparison function"); return RenderComparisonFunction::NEVER; } } static RenderStencilOp ConvertStencilOp(uint32_t stencilOp) { switch (stencilOp) { case D3DSTENCILOP_KEEP: return RenderStencilOp::KEEP; case D3DSTENCILOP_ZERO: return RenderStencilOp::ZERO; case D3DSTENCILOP_REPLACE: return RenderStencilOp::REPLACE; case D3DSTENCILOP_INCRSAT: return RenderStencilOp::INCREMENT_AND_CLAMP; case D3DSTENCILOP_DECRSAT: return RenderStencilOp::DECREMENT_AND_CLAMP; case D3DSTENCILOP_INVERT: return RenderStencilOp::INVERT; case D3DSTENCILOP_INCR: return RenderStencilOp::INCREMENT_AND_WRAP; case D3DSTENCILOP_DECR: return RenderStencilOp::DECREMENT_AND_WRAP; default: assert(false && "Unknown stencil op"); return RenderStencilOp::KEEP; } } static void ProcSetRenderState(const RenderCommand& cmd) { uint32_t value = cmd.setRenderState.value; switch (cmd.setRenderState.type) { case D3DRS_ZENABLE: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.zEnable, value != 0); g_dirtyStates.renderTargetAndDepthStencil |= g_dirtyStates.pipelineState; break; } case D3DRS_ZWRITEENABLE: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.zWriteEnable, value != 0); break; } case D3DRS_STENCILENABLE: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilEnable, value != 0); g_dirtyStates.renderTargetAndDepthStencil |= g_dirtyStates.pipelineState; break; } case D3DRS_TWOSIDEDSTENCILMODE: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilTwoSided, value != 0); break; } case D3DRS_ALPHATESTENABLE: { SetAlphaTestMode(value != 0); break; } case D3DRS_SRCBLEND: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.srcBlend, ConvertBlendMode(value)); break; } case D3DRS_DESTBLEND: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.destBlend, ConvertBlendMode(value)); break; } case D3DRS_CULLMODE: { RenderCullMode cullMode; switch (value) { case D3DCULL_NONE_CCW: case D3DCULL_NONE_CW: cullMode = RenderCullMode::NONE; break; case D3DCULL_FRONT_CCW: case D3DCULL_FRONT_CW: cullMode = RenderCullMode::FRONT; break; case D3DCULL_BACK_CCW: case D3DCULL_BACK_CW: cullMode = RenderCullMode::BACK; break; default: assert(false && "Invalid cull mode"); cullMode = RenderCullMode::NONE; break; } SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.cullMode, cullMode); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.frontFace, value < D3DCULL_NONE_CW ? RenderFrontFace::COUNTER_CLOCKWISE : RenderFrontFace::CLOCKWISE); break; } case D3DRS_ZFUNC: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.zFunc, ConvertCompareFunc(value)); break; } case D3DRS_STENCILFUNC: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilFunc, ConvertCompareFunc(value)); break; } case D3DRS_STENCILFAIL: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilFail, ConvertStencilOp(value)); break; } case D3DRS_STENCILZFAIL: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilZFail, ConvertStencilOp(value)); break; } case D3DRS_STENCILPASS: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilPass, ConvertStencilOp(value)); break; } case D3DRS_CCW_STENCILFUNC: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilFuncCCW, ConvertCompareFunc(value)); break; } case D3DRS_CCW_STENCILFAIL: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilFailCCW, ConvertStencilOp(value)); break; } case D3DRS_CCW_STENCILZFAIL: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilZFailCCW, ConvertStencilOp(value)); break; } case D3DRS_CCW_STENCILPASS: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilPassCCW, ConvertStencilOp(value)); break; } case D3DRS_STENCILREF: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilRef, value); break; } case D3DRS_STENCILMASK: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilMask, value); break; } case D3DRS_STENCILWRITEMASK: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.stencilWriteMask, value); break; } case D3DRS_ALPHAREF: { SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.alphaThreshold, float(value) / 256.0f); break; } case D3DRS_ALPHABLENDENABLE: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.alphaBlendEnable, value != 0); break; } case D3DRS_BLENDOP: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.blendOp, ConvertBlendOp(value)); break; } case D3DRS_SCISSORTESTENABLE: { // HACK: Ignore scissor test on depth-only draws to allow CSM:3 to properly scale if (g_pipelineState.depthStencilFormat == RenderFormat::UNKNOWN || g_pipelineState.renderTargetFormat != RenderFormat::UNKNOWN) SetDirtyValue(g_dirtyStates.scissorRect, g_scissorTestEnable, value != 0); break; } case D3DRS_SLOPESCALEDEPTHBIAS: { if (g_capabilities.dynamicDepthBias) SetDirtyValue(g_dirtyStates.depthBias, g_slopeScaledDepthBias, *reinterpret_cast(&value)); else SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.slopeScaledDepthBias, *reinterpret_cast(&value)); break; } case D3DRS_DEPTHBIAS: { if (g_capabilities.dynamicDepthBias) SetDirtyValue(g_dirtyStates.depthBias, g_depthBias, int32_t(*reinterpret_cast(&value) * (1 << 24))); else SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.depthBias, int32_t(*reinterpret_cast(&value)* (1 << 24))); break; } case D3DRS_SRCBLENDALPHA: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.srcBlendAlpha, ConvertBlendMode(value)); break; } case D3DRS_DESTBLENDALPHA: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.destBlendAlpha, ConvertBlendMode(value)); break; } case D3DRS_BLENDOPALPHA: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.blendOpAlpha, ConvertBlendOp(value)); break; } case D3DRS_COLORWRITEENABLE: { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.colorWriteEnable, value); g_dirtyStates.renderTargetAndDepthStencil |= g_dirtyStates.pipelineState; break; } case D3DRS_CLIPPLANEENABLE: { // HACK: Only check for clip pane 0 SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.clipPlaneEnabled, (value & 1) == 1); } } } static const std::pair g_setRenderStateFunctions[] = { { D3DRS_ZENABLE, HostToGuestFunction> }, { D3DRS_ZWRITEENABLE, HostToGuestFunction> }, { D3DRS_ALPHATESTENABLE, HostToGuestFunction> }, { D3DRS_SRCBLEND, HostToGuestFunction> }, { D3DRS_DESTBLEND, HostToGuestFunction> }, { D3DRS_CULLMODE, HostToGuestFunction> }, { D3DRS_ZFUNC, HostToGuestFunction> }, { D3DRS_ALPHAREF, HostToGuestFunction> }, { D3DRS_ALPHABLENDENABLE, HostToGuestFunction> }, { D3DRS_BLENDOP, HostToGuestFunction> }, { D3DRS_SCISSORTESTENABLE, HostToGuestFunction> }, { D3DRS_SLOPESCALEDEPTHBIAS, HostToGuestFunction> }, { D3DRS_DEPTHBIAS, HostToGuestFunction> }, { D3DRS_SRCBLENDALPHA, HostToGuestFunction> }, { D3DRS_DESTBLENDALPHA, HostToGuestFunction> }, { D3DRS_BLENDOPALPHA, HostToGuestFunction> }, { D3DRS_COLORWRITEENABLE, HostToGuestFunction> }, { D3DRS_STENCILENABLE, HostToGuestFunction> }, { D3DRS_TWOSIDEDSTENCILMODE, HostToGuestFunction> }, { D3DRS_STENCILFAIL, HostToGuestFunction> }, { D3DRS_STENCILZFAIL, HostToGuestFunction> }, { D3DRS_STENCILPASS, HostToGuestFunction> }, { D3DRS_STENCILFUNC, HostToGuestFunction> }, { D3DRS_STENCILREF, HostToGuestFunction> }, { D3DRS_STENCILMASK, HostToGuestFunction> }, { D3DRS_STENCILWRITEMASK, HostToGuestFunction> }, { D3DRS_CCW_STENCILFAIL, HostToGuestFunction> }, { D3DRS_CCW_STENCILZFAIL, HostToGuestFunction> }, { D3DRS_CCW_STENCILPASS, HostToGuestFunction> }, { D3DRS_CCW_STENCILFUNC, HostToGuestFunction> }, { D3DRS_CLIPPLANEENABLE, HostToGuestFunction> } }; static std::unique_ptr g_copyShader; static std::unique_ptr g_copyColorShader; static ankerl::unordered_dense::map> g_copyColorPipelines; static std::unique_ptr g_copyDepthPipeline; static std::unique_ptr g_resolveMsaaColorShaders[3]; static ankerl::unordered_dense::map, 3>> g_resolveMsaaColorPipelines; static std::unique_ptr g_resolveMsaaDepthPipelines[3]; enum { GAUSSIAN_BLUR_3X3, GAUSSIAN_BLUR_5X5, GAUSSIAN_BLUR_7X7, GAUSSIAN_BLUR_9X9, GAUSSIAN_BLUR_COUNT }; static std::unique_ptr g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT]; static std::unique_ptr g_csdFilterShader; static GuestShader* g_csdShader; static std::unique_ptr g_enhancedBurnoutBlurVSShader; static std::unique_ptr g_enhancedBurnoutBlurPSShader; static std::unique_ptr g_conditionalSurveyPSShader; #if defined(MARATHON_RECOMP_D3D12) #define CREATE_SHADER(NAME) \ g_device->createShader( \ (g_backend == Backend::VULKAN) ? g_##NAME##_spirv : g_##NAME##_dxil, \ (g_backend == Backend::VULKAN) ? sizeof(g_##NAME##_spirv) : sizeof(g_##NAME##_dxil), \ "shaderMain", \ (g_backend == Backend::VULKAN) ? RenderShaderFormat::SPIRV : RenderShaderFormat::DXIL) #elif defined(MARATHON_RECOMP_METAL) #define CREATE_SHADER(NAME) \ g_device->createShader( \ (g_backend == Backend::VULKAN) ? g_##NAME##_spirv : g_##NAME##_air, \ (g_backend == Backend::VULKAN) ? sizeof(g_##NAME##_spirv) : sizeof(g_##NAME##_air), \ "shaderMain", \ (g_backend == Backend::VULKAN) ? RenderShaderFormat::SPIRV : RenderShaderFormat::METAL) #else #define CREATE_SHADER(NAME) \ g_device->createShader(g_##NAME##_spirv, sizeof(g_##NAME##_spirv), "shaderMain", RenderShaderFormat::SPIRV) #endif #ifdef _WIN32 static bool DetectWine() { HMODULE dllHandle = GetModuleHandle("ntdll.dll"); return dllHandle != nullptr && GetProcAddress(dllHandle, "wine_get_version") != nullptr; } #endif static constexpr size_t TEXTURE_DESCRIPTOR_SIZE = 32768; static constexpr size_t SAMPLER_DESCRIPTOR_SIZE = 1024; static std::unique_ptr g_imFontTexture; static std::unique_ptr g_imPipelineLayout; static std::unique_ptr g_imPipeline; static std::unique_ptr g_imAdditivePipeline; template static void ExecuteCopyCommandList(const T& function) { std::lock_guard lock(g_copyMutex); g_copyCommandList->begin(); function(); g_copyCommandList->end(); g_copyQueue->executeCommandLists(g_copyCommandList.get(), g_copyCommandFence.get()); g_copyQueue->waitForCommandFence(g_copyCommandFence.get()); } static constexpr uint32_t PITCH_ALIGNMENT = 0x100; static constexpr uint32_t PLACEMENT_ALIGNMENT = 0x200; struct ImGuiPushConstants { ImVec2 boundsMin{}; ImVec2 boundsMax{}; ImU32 gradientTopLeft{}; ImU32 gradientTopRight{}; ImU32 gradientBottomRight{}; ImU32 gradientBottomLeft{}; uint32_t shaderModifier{}; uint32_t texture2DDescriptorIndex{}; ImVec2 displaySize{}; ImVec2 inverseDisplaySize{}; ImVec2 origin{ 0.0f, 0.0f }; ImVec2 scale{ 1.0f, 1.0f }; ImVec2 proceduralOrigin{ 0.0f, 0.0f }; float outline{}; }; extern ImFontBuilderIO g_fontBuilderIO; static void CreateImGuiBackend() { ImGuiIO& io = ImGui::GetIO(); io.IniFilename = nullptr; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; #ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT IM_DELETE(io.Fonts); io.Fonts = ImFontAtlasSnapshot::Load(); #else io.Fonts->AddFontDefault(); ImFontAtlasSnapshot::GenerateGlyphRanges(); #endif InitImGuiUtils(); OptionsMenu::Init(); InstallerWizard::Init(); ImGui_ImplSDL2_InitForOther(GameWindow::s_pWindow); #ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT g_imFontTexture = LoadTexture( decompressZstd(g_im_font_atlas_texture, g_im_font_atlas_texture_uncompressed_size).get(), g_im_font_atlas_texture_uncompressed_size); #else io.Fonts->FontBuilderIO = &g_fontBuilderIO; io.Fonts->Build(); g_imFontTexture = std::make_unique(ResourceType::Texture); uint8_t* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); RenderTextureDesc textureDesc; textureDesc.dimension = RenderTextureDimension::TEXTURE_2D; textureDesc.width = width; textureDesc.height = height; textureDesc.depth = 1; textureDesc.mipLevels = 1; textureDesc.arraySize = 1; textureDesc.format = RenderFormat::R8G8B8A8_UNORM; g_imFontTexture->textureHolder = g_device->createTexture(textureDesc); g_imFontTexture->texture = g_imFontTexture->textureHolder.get(); uint32_t rowPitch = (width * 4 + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); uint32_t slicePitch = (rowPitch * height + PLACEMENT_ALIGNMENT - 1) & ~(PLACEMENT_ALIGNMENT - 1); auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(slicePitch)); uint8_t* mappedMemory = reinterpret_cast(uploadBuffer->map()); if (rowPitch == (width * 4)) { memcpy(mappedMemory, pixels, slicePitch); } else { for (size_t i = 0; i < height; i++) { memcpy(mappedMemory, pixels, width * 4); pixels += width * 4; mappedMemory += rowPitch; } } uploadBuffer->unmap(); ExecuteCopyCommandList([&] { g_copyCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(g_imFontTexture->texture, RenderTextureLayout::COPY_DEST)); g_copyCommandList->copyTextureRegion( RenderTextureCopyLocation::Subresource(g_imFontTexture->texture, 0), RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); }); g_imFontTexture->layout = RenderTextureLayout::COPY_DEST; RenderTextureViewDesc textureViewDesc; textureViewDesc.format = textureDesc.format; textureViewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; textureViewDesc.mipLevels = 1; g_imFontTexture->textureView = g_imFontTexture->texture->createTextureView(textureViewDesc); g_imFontTexture->descriptorIndex = g_textureDescriptorAllocator.allocate(); g_textureDescriptorSet->setTexture(g_imFontTexture->descriptorIndex, g_imFontTexture->texture, RenderTextureLayout::SHADER_READ, g_imFontTexture->textureView.get()); #endif io.Fonts->SetTexID(g_imFontTexture.get()); RenderPipelineLayoutBuilder pipelineLayoutBuilder; pipelineLayoutBuilder.begin(false, true); RenderDescriptorSetBuilder descriptorSetBuilder; descriptorSetBuilder.begin(); descriptorSetBuilder.addTexture(0, TEXTURE_DESCRIPTOR_SIZE); descriptorSetBuilder.end(true, TEXTURE_DESCRIPTOR_SIZE); pipelineLayoutBuilder.addDescriptorSet(descriptorSetBuilder); descriptorSetBuilder.begin(); descriptorSetBuilder.addSampler(0, SAMPLER_DESCRIPTOR_SIZE); descriptorSetBuilder.end(true, SAMPLER_DESCRIPTOR_SIZE); pipelineLayoutBuilder.addDescriptorSet(descriptorSetBuilder); pipelineLayoutBuilder.addPushConstant(0, 2, sizeof(ImGuiPushConstants), RenderShaderStageFlag::VERTEX | RenderShaderStageFlag::PIXEL); pipelineLayoutBuilder.end(); g_imPipelineLayout = pipelineLayoutBuilder.create(g_device.get()); auto vertexShader = CREATE_SHADER(imgui_vs); auto pixelShader = CREATE_SHADER(imgui_ps); RenderInputElement inputElements[3]; inputElements[0] = RenderInputElement("POSITION", 0, 0, RenderFormat::R32G32_FLOAT, 0, offsetof(ImDrawVert, pos)); inputElements[1] = RenderInputElement("TEXCOORD", 0, 1, RenderFormat::R32G32_FLOAT, 0, offsetof(ImDrawVert, uv)); inputElements[2] = RenderInputElement("COLOR", 0, 2, RenderFormat::R8G8B8A8_UNORM, 0, offsetof(ImDrawVert, col)); RenderInputSlot inputSlot(0, sizeof(ImDrawVert)); RenderGraphicsPipelineDesc pipelineDesc; pipelineDesc.pipelineLayout = g_imPipelineLayout.get(); pipelineDesc.vertexShader = vertexShader.get(); pipelineDesc.pixelShader = pixelShader.get(); pipelineDesc.renderTargetFormat[0] = BACKBUFFER_FORMAT; pipelineDesc.renderTargetBlend[0] = RenderBlendDesc::AlphaBlend(); pipelineDesc.renderTargetCount = 1; pipelineDesc.inputElements = inputElements; pipelineDesc.inputElementsCount = std::size(inputElements); pipelineDesc.inputSlots = &inputSlot; pipelineDesc.inputSlotsCount = 1; g_imPipeline = g_device->createGraphicsPipeline(pipelineDesc); pipelineDesc.renderTargetBlend[0].dstBlend = RenderBlend::ONE; g_imAdditivePipeline = g_device->createGraphicsPipeline(pipelineDesc); #ifndef ENABLE_IM_FONT_ATLAS_SNAPSHOT ImFontAtlasSnapshot snapshot; snapshot.Snap(); FILE* file = fopen("im_font_atlas.bin", "wb"); if (file) { fwrite(snapshot.data.data(), 1, snapshot.data.size(), file); fclose(file); } ddspp::Header header; ddspp::HeaderDXT10 headerDX10; ddspp::encode_header(ddspp::R8G8B8A8_UNORM, width, height, 1, ddspp::Texture2D, 1, 1, header, headerDX10); file = fopen("im_font_atlas.dds", "wb"); if (file) { fwrite(&ddspp::DDS_MAGIC, 4, 1, file); fwrite(&header, sizeof(header), 1, file); fwrite(&headerDX10, sizeof(headerDX10), 1, file); fwrite(pixels, 4, width * height, file); fclose(file); } #endif } static void CheckSwapChain() { g_swapChain->setVsyncEnabled(Config::VSync); g_swapChainValid &= !g_swapChain->needsResize(); if (!g_swapChainValid) { Video::WaitForGPU(); g_backBuffer->framebuffers.clear(); g_swapChainValid = g_swapChain->resize(); g_needsResize = g_swapChainValid; } if (g_swapChainValid) { g_swapChainAcquireProfiler.Begin(); g_swapChainValid = g_swapChain->acquireTexture(g_acquireSemaphores[g_frame].get(), &g_backBufferIndex); g_swapChainAcquireProfiler.End(); } if (g_needsResize) Video::ComputeViewportDimensions(); g_backBuffer->width = Video::s_viewportWidth; g_backBuffer->height = Video::s_viewportHeight; } static void BeginCommandList() { g_renderTarget = g_backBuffer; g_depthStencil = nullptr; g_framebuffer = nullptr; g_pipelineState.renderTargetFormat = BACKBUFFER_FORMAT; g_pipelineState.depthStencilFormat = RenderFormat::UNKNOWN; if (g_swapChainValid) { uint32_t width = Video::s_viewportWidth; uint32_t height = Video::s_viewportHeight; if (g_intermediaryBackBufferTextureWidth != width || g_intermediaryBackBufferTextureHeight != height) { if (g_intermediaryBackBufferTextureDescriptorIndex == NULL) g_intermediaryBackBufferTextureDescriptorIndex = g_textureDescriptorAllocator.allocate(); Video::WaitForGPU(); // Fine to wait for GPU, this'll only happen during resize. g_intermediaryBackBufferTexture = g_device->createTexture(RenderTextureDesc::Texture2D(width, height, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET)); g_textureDescriptorSet->setTexture(g_intermediaryBackBufferTextureDescriptorIndex, g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ); g_intermediaryBackBufferTextureWidth = width; g_intermediaryBackBufferTextureHeight = height; g_backBuffer->framebuffers.clear(); } g_backBuffer->texture = g_intermediaryBackBufferTexture.get(); } else { g_backBuffer->texture = g_backBuffer->textureHolder.get(); } g_backBuffer->layout = RenderTextureLayout::UNKNOWN; for (size_t i = 0; i < 16; i++) { g_sharedConstants.texture2DIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D; g_sharedConstants.texture2DArrayIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D_ARRAY; g_sharedConstants.textureCubeIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE; } memset(g_textures, 0, sizeof(g_textures)); auto& commandList = g_commandLists[g_frame]; commandList->begin(); commandList->resetQueryPool(g_queryPools[g_frame].get(), 0, NUM_QUERIES); commandList->writeTimestamp(g_queryPools[g_frame].get(), 0); commandList->setGraphicsPipelineLayout(g_pipelineLayout.get()); commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0); commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 1); commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 2); commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 3); commandList->setGraphicsDescriptorSet(g_conditionalSurveyDescriptorSet.get(), 4); g_readyForCommands = true; g_readyForCommands.notify_one(); } template static void ApplyLowEndDefault(ConfigDef &configDef, T newDefault, bool &changed) { if (configDef.IsDefaultValue() && !configDef.IsLoadedFromConfig) { configDef = newDefault; changed = true; } configDef.DefaultValue = newDefault; } static void ApplyLowEndDefaults() { bool changed = false; ApplyLowEndDefault(Config::AntiAliasing, EAntiAliasing::MSAA2x, changed); ApplyLowEndDefault(Config::ShadowResolution, EShadowResolution::x1024, changed); ApplyLowEndDefault(Config::ReflectionResolution, EReflectionResolution::Quarter, changed); ApplyLowEndDefault(Config::TransparencyAntiAliasing, false, changed); if (changed) { Config::Save(); } } bool Video::CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry) { for (uint32_t i = 0; i < 16; i++) g_inputSlots[i].index = i; IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImPlot::CreateContext(); GameWindow::Init(sdlVideoDriver); #if defined(MARATHON_RECOMP_D3D12) g_backend = (DetectWine() || Config::GraphicsAPI == EGraphicsAPI::Vulkan) ? Backend::VULKAN : Backend::D3D12; #elif defined(MARATHON_RECOMP_METAL) g_backend = Config::GraphicsAPI == EGraphicsAPI::Vulkan ? Backend::VULKAN : Backend::METAL; #endif // Attempt to create the possible backends using a vector of function pointers. Whichever succeeds first will be the chosen API. using RenderInterfaceFunction = std::unique_ptr(void); std::vector interfaceFunctions; #ifdef MARATHON_RECOMP_D3D12 bool allowVulkanRedirection = true; if (graphicsApiRetry) { // If we are attempting to create again after a reboot due to a crash, swap the order. g_backend = (g_backend == Backend::VULKAN) ? Backend::D3D12 : Backend::VULKAN; // Don't allow redirection to Vulkan if we are retrying after a crash, // so the user can at least boot the game with D3D12 if Vulkan fails to work. allowVulkanRedirection = false; } interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterfaceWrapper : CreateD3D12Interface); interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateD3D12Interface : CreateVulkanInterfaceWrapper); #elif defined(MARATHON_RECOMP_METAL) interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateVulkanInterfaceWrapper : CreateMetalInterface); interfaceFunctions.push_back((g_backend == Backend::VULKAN) ? CreateMetalInterface : CreateVulkanInterfaceWrapper); #else interfaceFunctions.push_back(CreateVulkanInterfaceWrapper); #endif for (size_t i = 0; i < interfaceFunctions.size(); i++) { RenderInterfaceFunction* interfaceFunction = interfaceFunctions[i]; #ifdef MARATHON_RECOMP_D3D12 // Wrap the device creation in __try/__except to survive from driver crashes. __try #endif { g_interface = interfaceFunction(); if (g_interface == nullptr) { continue; } g_device = g_interface->createDevice(Config::GraphicsDevice); if (g_device != nullptr) { const RenderDeviceDescription &deviceDescription = g_device->getDescription(); #if defined(MARATHON_RECOMP_D3D12) if (interfaceFunction == CreateD3D12Interface) { if (allowVulkanRedirection) { bool redirectToVulkan = false; // ... // There used to be driver redirections here, but they are all free from Vulkan purgatory for now... // ... if (redirectToVulkan) { g_device.reset(); g_interface.reset(); // In case Vulkan fails to initialize, we will try D3D12 again afterwards, // just to get the game to boot. This only really happens in very old Intel GPU drivers. if (g_backend != Backend::VULKAN) { interfaceFunctions.push_back(CreateD3D12Interface); allowVulkanRedirection = false; } continue; } } } g_backend = (interfaceFunction == CreateVulkanInterfaceWrapper) ? Backend::VULKAN : Backend::D3D12; #elif defined(MARATHON_RECOMP_METAL) g_backend = (interfaceFunction == CreateVulkanInterfaceWrapper) ? Backend::VULKAN : Backend::METAL; #endif // Enable triangle strip workaround if we are on AMD, as there is a bug where // restart indices cause triangles to be culled incorrectly. Converting them to degenerate triangles fixes it. g_triangleStripWorkaround = (deviceDescription.vendor == RenderDeviceVendor::AMD); break; } } #ifdef MARATHON_RECOMP_D3D12 __except (EXCEPTION_EXECUTE_HANDLER) { if (graphicsApiRetry) { // If we were retrying, and this also failed, then we'll show the user neither of the graphics APIs succeeded. return false; } else { // If this is the first crash we ran into, reboot and try the other graphics API. os::process::StartProcess(os::process::GetExecutablePath(), { "--graphics-api-retry" }); std::_Exit(0); } } #endif } if (g_device == nullptr) { return false; } #ifdef MARATHON_RECOMP_D3D12 if (graphicsApiRetry) { // If we managed to create a device after retrying it in a reboot, remember the one we picked. Config::GraphicsAPI = g_backend == Backend::VULKAN ? EGraphicsAPI::Vulkan : EGraphicsAPI::D3D12; } #endif g_capabilities = g_device->getCapabilities(); LoadEmbeddedResources(); constexpr uint64_t LowEndMemoryLimit = 2048ULL * 1024ULL * 1024ULL; RenderDeviceDescription deviceDescription = g_device->getDescription(); bool lowEndType = deviceDescription.type != RenderDeviceType::UNKNOWN && deviceDescription.type != RenderDeviceType::DISCRETE; bool lowEndMemory = deviceDescription.dedicatedVideoMemory < LowEndMemoryLimit; bool lowEndUMA = deviceDescription.type == RenderDeviceType::UNKNOWN && g_capabilities.uma; if (lowEndType || lowEndMemory || lowEndUMA) { // Switch to low end defaults if a non-discrete GPU was detected or a low amount of VRAM was detected. // Checking for UMA on D3D12 seems to be a reliable way to detect integrated GPUs. ApplyLowEndDefaults(); } const RenderSampleCounts colourSampleCount = g_device->getSampleCountsSupported(RenderFormat::R16G16B16A16_FLOAT); const RenderSampleCounts depthSampleCount = g_device->getSampleCountsSupported(RenderFormat::D32_FLOAT); const RenderSampleCounts commonSampleCount = colourSampleCount & depthSampleCount; // Disable specific MSAA levels if they are not supported. if ((commonSampleCount & RenderSampleCount::COUNT_2) == 0) Config::AntiAliasing.InaccessibleValues.emplace(EAntiAliasing::MSAA2x); if ((commonSampleCount & RenderSampleCount::COUNT_4) == 0) Config::AntiAliasing.InaccessibleValues.emplace(EAntiAliasing::MSAA4x); if ((commonSampleCount & RenderSampleCount::COUNT_8) == 0) Config::AntiAliasing.InaccessibleValues.emplace(EAntiAliasing::MSAA8x); // Set Anti-Aliasing to nearest supported level. Config::AntiAliasing.SnapToNearestAccessibleValue(false); g_queue = g_device->createCommandQueue(RenderCommandListType::DIRECT); for (auto& commandList : g_commandLists) commandList = g_queue->createCommandList(); for (auto& commandFence : g_commandFences) commandFence = g_device->createCommandFence(); for (auto& queryPool : g_queryPools) queryPool = g_device->createQueryPool(NUM_QUERIES); g_copyQueue = g_device->createCommandQueue(RenderCommandListType::COPY); g_copyCommandList = g_copyQueue->createCommandList(); g_copyCommandFence = g_device->createCommandFence(); if (g_backend == Backend::D3D12) { g_discardCommandList = g_queue->createCommandList(); g_discardCommandFence = g_device->createCommandFence(); } uint32_t bufferCount = 2; switch (Config::TripleBuffering) { case ETripleBuffering::Auto: switch (g_backend) { case Backend::VULKAN: // Defaulting to 3 is fine if presentWait as supported, as the maximum frame latency allowed is only 1. bufferCount = g_device->getCapabilities().presentWait ? 3 : 2; break; case Backend::D3D12: // Defaulting to 3 is fine on D3D12 thanks to flip discard model. bufferCount = 3; break; case Backend::METAL: bufferCount = 2; break; } break; case ETripleBuffering::On: bufferCount = 3; break; case ETripleBuffering::Off: bufferCount = 2; break; } RenderSwapChainDesc swapChainDesc; swapChainDesc.renderWindow = GameWindow::s_renderWindow; swapChainDesc.textureCount = bufferCount; swapChainDesc.format = BACKBUFFER_FORMAT; swapChainDesc.maxFrameLatency = Config::MaxFrameLatency; swapChainDesc.enablePresentWait = g_capabilities.presentWait; g_swapChain = g_queue->createSwapChain(swapChainDesc); g_swapChain->setVsyncEnabled(Config::VSync); g_swapChainValid = !g_swapChain->needsResize(); for (auto& acquireSemaphore : g_acquireSemaphores) acquireSemaphore = g_device->createCommandSemaphore(); for (auto& renderSemaphore : g_renderSemaphores) renderSemaphore = g_device->createCommandSemaphore(); RenderPipelineLayoutBuilder pipelineLayoutBuilder; pipelineLayoutBuilder.begin(false, true); RenderDescriptorSetBuilder descriptorSetBuilder; descriptorSetBuilder.begin(); descriptorSetBuilder.addTexture(0, TEXTURE_DESCRIPTOR_SIZE); descriptorSetBuilder.end(true, TEXTURE_DESCRIPTOR_SIZE); g_textureDescriptorSet = descriptorSetBuilder.create(g_device.get()); for (size_t i = 0; i < TEXTURE_DESCRIPTOR_NULL_COUNT; i++) { auto& texture = g_blankTextures[i]; auto& textureView = g_blankTextureViews[i]; RenderTextureDesc desc; desc.width = 1; desc.height = 1; desc.depth = 1; desc.mipLevels = 1; desc.format = RenderFormat::R8_UNORM; RenderTextureViewDesc viewDesc; viewDesc.format = desc.format; viewDesc.componentMapping = RenderComponentMapping(RenderSwizzle::ZERO, RenderSwizzle::ZERO, RenderSwizzle::ZERO, RenderSwizzle::ZERO); viewDesc.mipLevels = 1; switch (i) { case TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D: desc.dimension = RenderTextureDimension::TEXTURE_2D; desc.arraySize = 1; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; break; case TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D_ARRAY: desc.dimension = RenderTextureDimension::TEXTURE_2D; desc.arraySize = 1; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; break; case TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE: desc.dimension = RenderTextureDimension::TEXTURE_2D; desc.arraySize = 6; desc.flags = RenderTextureFlag::CUBE; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_CUBE; break; default: assert(false && "Unknown null descriptor dimension"); break; } texture = g_device->createTexture(desc); textureView = texture->createTextureView(viewDesc); g_textureDescriptorSet->setTexture(i, texture.get(), RenderTextureLayout::SHADER_READ, textureView.get()); } pipelineLayoutBuilder.addDescriptorSet(descriptorSetBuilder); pipelineLayoutBuilder.addDescriptorSet(descriptorSetBuilder); pipelineLayoutBuilder.addDescriptorSet(descriptorSetBuilder); descriptorSetBuilder.begin(); descriptorSetBuilder.addSampler(0, SAMPLER_DESCRIPTOR_SIZE); descriptorSetBuilder.end(true, SAMPLER_DESCRIPTOR_SIZE); g_samplerDescriptorSet = descriptorSetBuilder.create(g_device.get()); auto& [descriptorIndex, sampler] = g_samplerStates[XXH3_64bits(&g_samplerDescs[0], sizeof(RenderSamplerDesc))]; descriptorIndex = 1; sampler = g_device->createSampler(g_samplerDescs[0]); g_samplerDescriptorSet->setSampler(0, sampler.get()); pipelineLayoutBuilder.addDescriptorSet(descriptorSetBuilder); RenderBufferDesc conditionalSurveyBufferDesc; conditionalSurveyBufferDesc.size = CONDITIONAL_SURVEY_MAX * sizeof(uint32_t); conditionalSurveyBufferDesc.heapType = RenderHeapType::DEFAULT; conditionalSurveyBufferDesc.flags = RenderBufferFlag::STORAGE | RenderBufferFlag::UNORDERED_ACCESS; g_conditionalSurveyBuffer = g_device->createBuffer(conditionalSurveyBufferDesc); RenderDescriptorSetBuilder conditionalSurveyDescriptorSetBuilder; conditionalSurveyDescriptorSetBuilder.begin(); conditionalSurveyDescriptorSetBuilder.addReadWriteStructuredBuffer(0); conditionalSurveyDescriptorSetBuilder.end(); g_conditionalSurveyDescriptorSet = conditionalSurveyDescriptorSetBuilder.create(g_device.get()); RenderBufferStructuredView conditionalSurveyStructuredView(sizeof(uint32_t)); g_conditionalSurveyDescriptorSet->setBuffer(0, g_conditionalSurveyBuffer.get(), 0, &conditionalSurveyStructuredView); pipelineLayoutBuilder.addDescriptorSet(conditionalSurveyDescriptorSetBuilder); if (g_backend != Backend::D3D12) { pipelineLayoutBuilder.addPushConstant(0, 4, 24, RenderShaderStageFlag::VERTEX | RenderShaderStageFlag::PIXEL); } else { pipelineLayoutBuilder.addRootDescriptor(0, 4, RenderRootDescriptorType::CONSTANT_BUFFER); pipelineLayoutBuilder.addRootDescriptor(1, 4, RenderRootDescriptorType::CONSTANT_BUFFER); pipelineLayoutBuilder.addRootDescriptor(2, 4, RenderRootDescriptorType::CONSTANT_BUFFER); pipelineLayoutBuilder.addPushConstant(3, 4, 4, RenderShaderStageFlag::PIXEL); // For copy/resolve shaders. } pipelineLayoutBuilder.end(); g_pipelineLayout = pipelineLayoutBuilder.create(g_device.get()); g_copyShader = CREATE_SHADER(copy_vs); g_copyColorShader = CREATE_SHADER(copy_color_ps); auto copyDepthShader = CREATE_SHADER(copy_depth_ps); RenderGraphicsPipelineDesc desc; desc.pipelineLayout = g_pipelineLayout.get(); desc.vertexShader = g_copyShader.get(); desc.pixelShader = copyDepthShader.get(); desc.depthFunction = RenderComparisonFunction::ALWAYS; desc.depthEnabled = true; desc.depthWriteEnabled = true; desc.depthTargetFormat = RenderFormat::D32_FLOAT_S8_UINT; g_copyDepthPipeline = g_device->createGraphicsPipeline(desc); g_resolveMsaaColorShaders[0] = CREATE_SHADER(resolve_msaa_color_2x); g_resolveMsaaColorShaders[1] = CREATE_SHADER(resolve_msaa_color_4x); g_resolveMsaaColorShaders[2] = CREATE_SHADER(resolve_msaa_color_8x); for (size_t i = 0; i < std::size(g_resolveMsaaDepthPipelines); i++) { std::unique_ptr pixelShader; switch (i) { case 0: pixelShader = CREATE_SHADER(resolve_msaa_depth_2x); break; case 1: pixelShader = CREATE_SHADER(resolve_msaa_depth_4x); break; case 2: pixelShader = CREATE_SHADER(resolve_msaa_depth_8x); break; } desc = {}; desc.pipelineLayout = g_pipelineLayout.get(); desc.vertexShader = g_copyShader.get(); desc.pixelShader = pixelShader.get(); desc.depthFunction = RenderComparisonFunction::ALWAYS; desc.depthEnabled = true; desc.depthWriteEnabled = true; desc.depthTargetFormat = RenderFormat::D32_FLOAT_S8_UINT; g_resolveMsaaDepthPipelines[i] = g_device->createGraphicsPipeline(desc); } for (auto& shader : g_gaussianBlurShaders) shader = std::make_unique(ResourceType::PixelShader); g_gaussianBlurShaders[GAUSSIAN_BLUR_3X3]->shader = CREATE_SHADER(gaussian_blur_3x3); g_gaussianBlurShaders[GAUSSIAN_BLUR_5X5]->shader = CREATE_SHADER(gaussian_blur_5x5); g_gaussianBlurShaders[GAUSSIAN_BLUR_7X7]->shader = CREATE_SHADER(gaussian_blur_7x7); g_gaussianBlurShaders[GAUSSIAN_BLUR_9X9]->shader = CREATE_SHADER(gaussian_blur_9x9); g_csdFilterShader = std::make_unique(ResourceType::PixelShader); g_csdFilterShader->shader = CREATE_SHADER(csd_filter_ps); g_enhancedBurnoutBlurVSShader = std::make_unique(ResourceType::VertexShader); g_enhancedBurnoutBlurVSShader->shader = CREATE_SHADER(enhanced_burnout_blur_vs); g_enhancedBurnoutBlurPSShader = std::make_unique(ResourceType::PixelShader); g_enhancedBurnoutBlurPSShader->shader = CREATE_SHADER(enhanced_burnout_blur_ps); g_conditionalSurveyPSShader = std::make_unique(ResourceType::PixelShader); g_conditionalSurveyPSShader->shader = CREATE_SHADER(conditional_survey_ps); CreateImGuiBackend(); auto gammaCorrectionShader = CREATE_SHADER(gamma_correction_ps); desc = {}; desc.pipelineLayout = g_pipelineLayout.get(); desc.vertexShader = g_copyShader.get(); desc.pixelShader = gammaCorrectionShader.get(); desc.renderTargetFormat[0] = BACKBUFFER_FORMAT; desc.renderTargetBlend[0] = RenderBlendDesc::Copy(); desc.renderTargetCount = 1; g_gammaCorrectionPipeline = g_device->createGraphicsPipeline(desc); // NOTE: We initially allocate this on host memory to make the installer work, even if the 4 GB memory allocation fails. g_backBufferHolder = std::make_unique(ResourceType::RenderTarget); g_backBuffer = g_backBufferHolder.get(); g_backBuffer->width = 1280; g_backBuffer->height = 720; g_backBuffer->format = BACKBUFFER_FORMAT; g_backBuffer->textureHolder = g_device->createTexture(RenderTextureDesc::Texture2D(1, 1, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET)); Video::ComputeViewportDimensions(); CheckSwapChain(); BeginCommandList(); RenderTextureBarrier blankTextureBarriers[TEXTURE_DESCRIPTOR_NULL_COUNT]; for (size_t i = 0; i < TEXTURE_DESCRIPTOR_NULL_COUNT; i++) blankTextureBarriers[i] = RenderTextureBarrier(g_blankTextures[i].get(), RenderTextureLayout::SHADER_READ); g_commandLists[g_frame]->barriers(RenderBarrierStage::NONE, blankTextureBarriers, std::size(blankTextureBarriers)); return true; } static uint32_t g_waitForGPUCount = 0; void Video::WaitForGPU() { g_waitForGPUCount++; // Wait for all queued frames to finish. for (size_t i = 0; i < NUM_FRAMES; i++) { if (g_commandListStates[i]) { g_queue->waitForCommandFence(g_commandFences[i].get()); g_commandListStates[i] = false; } } // Execute an empty command list and wait for it to end to guarantee that any remaining presentation has finished. g_commandLists[0]->begin(); g_commandLists[0]->end(); g_queue->executeCommandLists(g_commandLists[0].get(), g_commandFences[0].get()); g_queue->waitForCommandFence(g_commandFences[0].get()); } static uint32_t getSetAddress(uint32_t base, int index) { uint32_t entryOffset = index * 0xC; uint32_t entryAddress = base + entryOffset; uint32_t setAddress = entryAddress + sizeof(uint32_t); return setAddress; } static uint32_t CreateDevice(uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5, be* a6) { LOGF_WARNING("{:p} {:p} {:p} {:p} {:p} {:p}\n", reinterpret_cast(a1), reinterpret_cast(a2), reinterpret_cast(a3), reinterpret_cast(a4), reinterpret_cast(a5), reinterpret_cast(a6)); g_xdbfTextureCache = std::unordered_map(); for (auto &achievement : g_xdbfWrapper.GetAchievements(XDBF_LANGUAGE_ENGLISH)) { if (!achievement.pImageBuffer || !achievement.ImageBufferSize) continue; g_xdbfTextureCache[achievement.ID] = LoadTexture((uint8_t *)achievement.pImageBuffer, achievement.ImageBufferSize).release(); } // Move backbuffer to guest memory. assert(!g_memory.IsInMemoryRange(g_backBuffer) && g_backBufferHolder != nullptr); g_backBuffer = g_userHeap.AllocPhysical(std::move(*g_backBufferHolder)); // Check for stale reference. BeginCommandList() gets called before CreateDevice() which is where the assignment happens. if (g_renderTarget == g_backBufferHolder.get()) g_renderTarget = g_backBuffer; if (g_depthStencil == g_backBufferHolder.get()) g_depthStencil = g_backBuffer; // Free the host backbuffer. g_backBufferHolder = nullptr; auto device = g_userHeap.AllocPhysical(); memset(device, 0, sizeof(*device)); // Append render state functions to the end of guest function table. uint32_t functionOffsetUnimplemented = PPC_CODE_BASE + PPC_CODE_SIZE; g_memory.InsertFunction(functionOffsetUnimplemented, HostToGuestFunction); uint32_t functionOffset = 0x82B79868; for (size_t i = 0; i < std::size(device->setRenderStateFunctions); i++) { device->setRenderStateFunctions[i] = functionOffsetUnimplemented; } // InsertFucntion doesn't work, so we have to do this manually in the end for (auto& [state, function] : g_setRenderStateFunctions) { auto funcOffset = getSetAddress(functionOffset, state/4); uint32_t addr = __builtin_bswap32(*(uint32_t*)g_memory.Translate(funcOffset)); printf("state %d of %x is %x\n", state, funcOffset, addr); g_memory.InsertFunction(addr, function); device->setRenderStateFunctions[state / 4] = addr; } for (size_t i = 0; i < std::size(device->setSamplerStateFunctions); i++) device->setSamplerStateFunctions[i] = *reinterpret_cast(g_memory.Translate(0x82B79CFC + i * 0xC)); device->viewport.width = 1280.0f; device->viewport.height = 720.0f; device->viewport.maxZ = 1.0f; *a6 = g_memory.MapVirtual(device); return 0; } static void DestructResource(GuestResource* resource) { // Needed for hack in CreateSurface (remove if fix it) if (resource->type == ResourceType::RenderTarget || resource->type == ResourceType::DepthStencil) { const auto surface = reinterpret_cast(resource); if (surface->wasCached) { return; } } RenderCommand cmd; cmd.type = RenderCommandType::DestructResource; cmd.destructResource.resource = resource; g_renderQueue.enqueue(cmd); } static void ProcDestructResource(const RenderCommand& cmd) { const auto& args = cmd.destructResource; g_tempResources[g_frame].push_back(args.resource); } static uint32_t ComputeTexturePitch(GuestTexture* texture) { return (texture->width * RenderFormatSize(texture->format) + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); } static void LockTextureRect(GuestTexture* texture, uint32_t, GuestLockedRect* lockedRect) { uint32_t pitch = ComputeTexturePitch(texture); uint32_t slicePitch = pitch * texture->height; if (texture->mappedMemory == nullptr) texture->mappedMemory = g_userHeap.AllocPhysical(slicePitch, 0x10); lockedRect->pitch = pitch; lockedRect->bits = g_memory.MapVirtual(texture->mappedMemory); } static void UnlockTextureRect(GuestTexture* texture) { assert(std::this_thread::get_id() == g_presentThreadId); RenderCommand cmd; cmd.type = RenderCommandType::UnlockTextureRect; cmd.unlockTextureRect.texture = texture; g_renderQueue.enqueue(cmd); } static void ProcUnlockTextureRect(const RenderCommand& cmd) { const auto& args = cmd.unlockTextureRect; AddBarrier(args.texture, RenderTextureLayout::COPY_DEST); FlushBarriers(); uint32_t pitch = ComputeTexturePitch(args.texture); uint32_t slicePitch = pitch * args.texture->height; auto allocation = g_uploadAllocators[g_frame].allocate(slicePitch, PLACEMENT_ALIGNMENT); memcpy(allocation.memory, args.texture->mappedMemory, slicePitch); g_commandLists[g_frame]->copyTextureRegion( RenderTextureCopyLocation::Subresource(args.texture->texture, 0), RenderTextureCopyLocation::PlacedFootprint(allocation.buffer, args.texture->format, args.texture->width, args.texture->height, 1, pitch / RenderFormatSize(args.texture->format), allocation.offset)); } static void* LockBuffer(GuestBuffer* buffer, uint32_t flags) { buffer->lockedReadOnly = (flags & 0x10) != 0; if (buffer->mappedMemory == nullptr) buffer->mappedMemory = g_userHeap.AllocPhysical(buffer->dataSize, 0x10); return buffer->mappedMemory; } static void* LockVertexBuffer(GuestBuffer* buffer, uint32_t, uint32_t, uint32_t flags) { return LockBuffer(buffer, flags); } static std::atomic g_bufferUploadCount = 0; template static void UnlockBuffer(GuestBuffer* buffer, bool useCopyQueue) { auto copyBuffer = [&](T* dest) { auto src = reinterpret_cast(buffer->mappedMemory); for (size_t i = 0; i < buffer->dataSize; i += sizeof(T)) { *dest = ByteSwap(*src); ++dest; ++src; } }; if (useCopyQueue && g_capabilities.gpuUploadHeap) { copyBuffer(reinterpret_cast(buffer->buffer->map())); buffer->buffer->unmap(); } else { auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(buffer->dataSize)); copyBuffer(reinterpret_cast(uploadBuffer->map())); uploadBuffer->unmap(); if (useCopyQueue) { ExecuteCopyCommandList([&] { g_copyCommandList->copyBufferRegion(buffer->buffer->at(0), uploadBuffer->at(0), buffer->dataSize); }); } else { auto& commandList = g_commandLists[g_frame]; commandList->barriers(RenderBarrierStage::COPY, RenderBufferBarrier(buffer->buffer.get(), RenderBufferAccess::WRITE)); commandList->copyBufferRegion(buffer->buffer->at(0), uploadBuffer->at(0), buffer->dataSize); commandList->barriers(RenderBarrierStage::GRAPHICS, RenderBufferBarrier(buffer->buffer.get(), RenderBufferAccess::READ)); g_tempBuffers[g_frame].emplace_back(std::move(uploadBuffer)); } } g_bufferUploadCount++; } template static void UnlockBuffer(GuestBuffer* buffer) { if (!buffer->lockedReadOnly) { UnlockBuffer(buffer, true); } } static void ProcUnlockBuffer16(const RenderCommand& cmd) { UnlockBuffer(cmd.unlockBuffer.buffer, false); } static void ProcUnlockBuffer32(const RenderCommand& cmd) { UnlockBuffer(cmd.unlockBuffer.buffer, false); } static void UnlockVertexBuffer(GuestBuffer* buffer) { UnlockBuffer(buffer); } static void GetVertexBufferDesc(GuestBuffer* buffer, GuestBufferDesc* desc) { desc->size = buffer->dataSize; } static void* LockIndexBuffer(GuestBuffer* buffer, uint32_t, uint32_t, uint32_t flags) { return LockBuffer(buffer, flags); } static void UnlockIndexBuffer(GuestBuffer* buffer) { if (buffer->guestFormat == D3DFMT_INDEX32) UnlockBuffer(buffer); else UnlockBuffer(buffer); } static void GetIndexBufferDesc(GuestBuffer* buffer, GuestBufferDesc* desc) { desc->format = buffer->guestFormat; desc->size = buffer->dataSize; } static void GetSurfaceDesc(GuestSurface* surface, GuestSurfaceDesc* desc) { if (surface->width == 0 && surface->height == 0) { LOGF_WARNING("{:p} {:d} {:d} \n", reinterpret_cast(desc), surface->width, surface->height); __builtin_trap(); } desc->width = surface->width; desc->height = surface->height; desc->format = surface->guestFormat; desc->type = 4; // D3DRTYPE_SURFACE // desc->multiSampleType = 0; if (surface->sampleCount == RenderSampleCount::COUNT_1) { desc->multiSampleType = 0; } else if (surface->sampleCount == RenderSampleCount::COUNT_2) { desc->multiSampleType = 1; } else { desc->multiSampleType = 2; } desc->multiSampleQuality = 0; desc->usage = 0; } static void GetVertexDeclaration(GuestVertexDeclaration* vertexDeclaration, GuestVertexElement* vertexElements, be* count) { memcpy(vertexElements, vertexDeclaration->vertexElements.get(), vertexDeclaration->vertexElementCount * sizeof(GuestVertexElement)); *count = vertexDeclaration->vertexElementCount; } static uint32_t HashVertexDeclaration(uint32_t vertexDeclaration) { // Vertex declarations are cached on host side, so the pointer itself can be used. return vertexDeclaration; } static const char *DeviceTypeName(RenderDeviceType type) { switch (type) { case RenderDeviceType::INTEGRATED: return "Integrated"; case RenderDeviceType::DISCRETE: return "Discrete"; case RenderDeviceType::VIRTUAL: return "Virtual"; case RenderDeviceType::CPU: return "CPU"; default: return "Unknown"; } } static void DrawProfiler() { bool toggleProfiler = SDL_GetKeyboardState(nullptr)[SDL_SCANCODE_F1] != 0; if (!g_profilerWasToggled && toggleProfiler) { g_profilerVisible = !g_profilerVisible; GameWindow::SetFullscreenCursorVisibility(App::s_isInit ? g_profilerVisible : true); } g_profilerWasToggled = toggleProfiler; if (!g_profilerVisible) return; ImFont* font = ImFontAtlasSnapshot::GetFont("FOT-RodinPro-DB.otf"); float defaultScale = font->Scale; font->Scale = ImGui::GetDefaultFont()->FontSize / font->FontSize; ImGui::PushFont(font); #define IMGUI_GENERIC_ROW(name, value, ...) \ ImGui::TableNextColumn(); \ ImGui::Text(name); \ ImGui::TableNextColumn(); \ ImGui::Text(value, __VA_ARGS__); if (ImGui::Begin("Profiler", &g_profilerVisible)) { g_applicationValues[g_profilerValueIndex] = App::s_deltaTime * 1000.0; const double applicationAvg = std::accumulate(g_applicationValues, g_applicationValues + PROFILER_VALUE_COUNT, 0.0) / PROFILER_VALUE_COUNT; double gpuFrameAvg = g_gpuFrameProfiler.UpdateAndReturnAverage(); double presentAvg = g_presentProfiler.UpdateAndReturnAverage(); double frameFenceAvg = g_frameFenceProfiler.UpdateAndReturnAverage(); double presentWaitAvg = g_presentWaitProfiler.UpdateAndReturnAverage(); double swapChainAcquireAvg = g_swapChainAcquireProfiler.UpdateAndReturnAverage(); if (ImGui::CollapsingHeader("Performance", ImGuiTreeNodeFlags_DefaultOpen)) { if (ImPlot::BeginPlot("Frame Time")) { ImPlot::SetupAxisLimits(ImAxis_Y1, 0.0, 20.0); ImPlot::SetupAxis(ImAxis_Y1, "ms", ImPlotAxisFlags_None); ImPlot::PlotLine("Application", g_applicationValues, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); ImPlot::PlotLine("GPU Frame", g_gpuFrameProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); ImPlot::PlotLine("Present", g_presentProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); ImPlot::PlotLine("Present Wait", g_presentWaitProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); ImPlot::PlotLine("Frame Fence", g_frameFenceProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); ImPlot::PlotLine("Swap Chain Acquire", g_swapChainAcquireProfiler.values, PROFILER_VALUE_COUNT, 1.0, 0.0, ImPlotLineFlags_None, g_profilerValueIndex); ImPlot::EndPlot(); } g_profilerValueIndex = (g_profilerValueIndex + 1) % PROFILER_VALUE_COUNT; if (ImGui::BeginTable("Performance", 5)) { ImGui::TableSetupColumn("Name"); ImGui::TableSetupColumn("Current Time"); ImGui::TableSetupColumn("Average Time"); ImGui::TableSetupColumn("Current FPS"); ImGui::TableSetupColumn("Average FPS"); ImGui::TableHeadersRow(); auto drawPerfRow = [](const char* name, double ms, double msAvg, bool showFPS = false, double fps = 0, double fpsAvg = 0) { ImGui::TableNextColumn(); ImGui::Text("%s", name); ImGui::TableNextColumn(); ImGui::Text("%g ms", ms); ImGui::TableNextColumn(); ImGui::Text("%g ms", msAvg); ImGui::TableNextColumn(); if (showFPS) ImGui::Text("%g FPS", fps); ImGui::TableNextColumn(); if (showFPS) ImGui::Text("%g FPS", fpsAvg); }; // -------- Name ---------------- Current Time --------------------------- Average Time -------- Current FPS ----------------------------- Average FPS ---------- // drawPerfRow("Application", App::s_deltaTime * 1000.0, applicationAvg, true, 1.0 / App::s_deltaTime, 1000.0 / applicationAvg); drawPerfRow("GPU Frame", g_gpuFrameProfiler.value.load(), gpuFrameAvg, true, 1000.0 / g_gpuFrameProfiler.value.load(), 1000.0 / gpuFrameAvg ); drawPerfRow("Present", g_presentProfiler.value.load(), presentAvg, true, 1000.0 / g_presentProfiler.value.load(), 1000.0 / presentAvg ); drawPerfRow("Present Wait", g_presentWaitProfiler.value.load(), presentWaitAvg ); drawPerfRow("Frame Fence", g_frameFenceProfiler.value.load(), frameFenceAvg ); drawPerfRow("Swap Chain Acquire", g_swapChainAcquireProfiler.value.load(), swapChainAcquireAvg ); ImGui::EndTable(); } ImGui::Separator(); ImGui::Checkbox("Show FPS", &Config::ShowFPS.Value); } if (g_userHeap.heap != nullptr && g_userHeap.physicalHeap != nullptr) { if (ImGui::CollapsingHeader("Memory", ImGuiTreeNodeFlags_DefaultOpen)) { O1HeapDiagnostics diagnostics, physicalDiagnostics; { std::lock_guard lock(g_userHeap.mutex); diagnostics = o1heapGetDiagnostics(g_userHeap.heap); } { std::lock_guard lock(g_userHeap.physicalMutex); physicalDiagnostics = o1heapGetDiagnostics(g_userHeap.physicalHeap); } if (ImGui::BeginTable("Memory", 2)) { IMGUI_GENERIC_ROW("Heap Allocated", "%d MB", int32_t(diagnostics.allocated / (1024 * 1024))); IMGUI_GENERIC_ROW("Physical Heap Allocated", "%d MB", int32_t(diagnostics.allocated / (1024 * 1024))); ImGui::EndTable(); } } } if (ImGui::CollapsingHeader("GPU", ImGuiTreeNodeFlags_DefaultOpen)) { std::string backend; switch (g_backend) { case Backend::VULKAN: backend = "Vulkan"; break; case Backend::D3D12: backend = "D3D12"; break; case Backend::METAL: backend = "Metal"; break; } if (ImGui::BeginTable("GPU", 2)) { IMGUI_GENERIC_ROW("API", "%s", backend.c_str()); if (auto pSDLVideoDriver = SDL_GetCurrentVideoDriver()) { IMGUI_GENERIC_ROW("SDL Video Driver", "%s", pSDLVideoDriver); } IMGUI_GENERIC_ROW("Device", "%s", g_device->getDescription().name.c_str()); IMGUI_GENERIC_ROW("Device Type", "%s", DeviceTypeName(g_device->getDescription().type)); IMGUI_GENERIC_ROW("VRAM", "%.2f MiB", (double)(g_device->getDescription().dedicatedVideoMemory) / (1024.0 * 1024.0)); IMGUI_GENERIC_ROW("GPU Waits", "%d", int32_t(g_waitForGPUCount)); IMGUI_GENERIC_ROW("Buffer Uploads", "%d", int32_t(g_bufferUploadCount)); IMGUI_GENERIC_ROW("Resolution", "%dx%d (%dx%d)", Video::s_viewportWidth, Video::s_viewportHeight, uint32_t(round(Video::s_viewportWidth * Config::ResolutionScale)), uint32_t(round(Video::s_viewportHeight * Config::ResolutionScale))); ImGui::EndTable(); } ImGui::Separator(); if (ImGui::TreeNode("Devices")) { ImGui::Indent(); if (ImGui::BeginTable("Devices", 2)) { auto deviceIndex = 0; for (const auto& deviceName : g_interface->getDeviceNames()) { ImGui::TableNextColumn(); ImGui::Text("Device #%d", deviceIndex++); ImGui::TableNextColumn(); ImGui::Text("%s", deviceName.c_str()); ImGui::SameLine(); } ImGui::EndTable(); } ImGui::Unindent(); ImGui::TreePop(); } if (ImGui::TreeNode("Features")) { ImGui::Indent(); if (ImGui::BeginTable("Features", 2)) { IMGUI_GENERIC_ROW("Dynamic Depth Bias", "%s", g_capabilities.dynamicDepthBias ? "Supported" : "Unsupported"); IMGUI_GENERIC_ROW("GPU Upload Heap", "%s", g_capabilities.gpuUploadHeap ? "Supported" : "Unsupported"); IMGUI_GENERIC_ROW("Hardware Resolve Modes", "%s", g_capabilities.resolveModes ? "Supported" : "Unsupported"); IMGUI_GENERIC_ROW("Present Wait", "%s", g_capabilities.presentWait ? "Supported" : "Unsupported"); IMGUI_GENERIC_ROW("Triangle Fan", "%s", g_capabilities.triangleFan ? "Supported" : "Unsupported"); IMGUI_GENERIC_ROW("Triangle Strip Workaround", "%s", g_triangleStripWorkaround ? "Enabled" : "Disabled"); IMGUI_GENERIC_ROW("UMA", "%s", g_capabilities.uma ? "Supported" : "Unsupported"); ImGui::EndTable(); } ImGui::Unindent(); ImGui::TreePop(); } } } #undef IMGUI_GENERIC_ROW ImGui::End(); ImGui::PopFont(); font->Scale = defaultScale; } static void DrawFPS() { if (!Config::ShowFPS) return; double time = ImGui::GetTime(); static double updateTime = time; static double fps = 0; static double totalDeltaTime = 0.0; static uint32_t totalDeltaCount = 0; totalDeltaTime += g_presentProfiler.value.load(); totalDeltaCount++; if (time - updateTime >= 1.0f) { fps = 1000.0 / std::max(totalDeltaTime / double(totalDeltaCount), 1.0); updateTime = time; totalDeltaTime = 0.0; totalDeltaCount = 0; } auto drawList = ImGui::GetBackgroundDrawList(); auto fmt = fmt::format("FPS: {:.2f}", fps); auto font = ImFontAtlasSnapshot::GetFont("FOT-RodinPro-DB.otf"); auto fontSize = Scale(10); auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, fmt.c_str()); ImVec2 min = { Scale(40), Scale(30) }; ImVec2 max = { min.x + std::max(Scale(75), textSize.x + Scale(10)), min.y + Scale(15) }; ImVec2 textPos = { min.x + Scale(2), CENTRE_TEXT_VERT(min, max, textSize) + Scale(0.2f) }; drawList->AddRectFilled(min, max, IM_COL32(0, 0, 0, 200)); drawList->AddText(font, fontSize, textPos, IM_COL32_WHITE, fmt.c_str()); } static void DrawImGui() { ImGui_ImplSDL2_NewFrame(); auto& io = ImGui::GetIO(); io.DisplaySize = { float(Video::s_viewportWidth), float(Video::s_viewportHeight) }; // ImGui doesn't know that we center the screen for specific aspect ratio // settings, which causes mouse events to not work correctly. To fix this, // we can adjust the mouse events before ImGui processes them. uint32_t width = g_swapChain->getWidth(); uint32_t height = g_swapChain->getHeight(); float mousePosScaleX = float(width) / float(GameWindow::s_width); float mousePosScaleY = float(height) / float(GameWindow::s_height); float mousePosOffsetX = (width - Video::s_viewportWidth) / 2.0f; float mousePosOffsetY = (height - Video::s_viewportHeight) / 2.0f; for (int i = 0; i < io.Ctx->InputEventsQueue.Size; i++) { auto& e = io.Ctx->InputEventsQueue[i]; if (e.Type == ImGuiInputEventType_MousePos) { if (e.MousePos.PosX != -FLT_MAX) { e.MousePos.PosX *= mousePosScaleX; e.MousePos.PosX -= mousePosOffsetX; } if (e.MousePos.PosY != -FLT_MAX) { e.MousePos.PosY *= mousePosScaleY; e.MousePos.PosY -= mousePosOffsetY; } } } ImGui::NewFrame(); ResetImGuiCallbacks(); #ifdef ASYNC_PSO_DEBUG if (ImGui::Begin("Async PSO Stats")) { ImGui::Text("Pipelines Created In Render Thread: %d", g_pipelinesCreatedInRenderThread.load()); ImGui::Text("Pipelines Created Asynchronously: %d", g_pipelinesCreatedAsynchronously.load()); ImGui::Text("Pipelines Dropped: %d", g_pipelinesDropped.load()); ImGui::Text("Pipelines Currently Compiling: %d", g_pipelinesCurrentlyCompiling.load()); ImGui::Text("Compiling Pipeline Task Count: %d", g_compilingPipelineTaskCount.load()); ImGui::Text("Pending Pipeline Task Count: %d", g_pendingPipelineTaskCount.load()); std::lock_guard lock(g_debugMutex); ImGui::TextUnformatted(g_pipelineDebugText.c_str()); } ImGui::End(); #endif UpdateImGuiUtils(); AchievementMenu::Draw(); OptionsMenu::Draw(); InstallerWizard::Draw(); ButtonWindow::Draw(); MessageWindow::Draw(); AchievementOverlay::Draw(); Fader::Draw(); BlackBar::Draw(); assert(ImGui::GetBackgroundDrawList()->_ClipRectStack.Size == 1 && "Some clip rects were not removed from the stack!"); DrawFPS(); DrawProfiler(); ImGui::Render(); auto drawData = ImGui::GetDrawData(); if (drawData->CmdListsCount != 0) { RenderCommand cmd; cmd.type = RenderCommandType::DrawImGui; g_renderQueue.enqueue(cmd); } } static void SetFramebuffer(GuestSurface *renderTarget, GuestSurface *depthStencil, bool settingForClear); static void ProcDrawImGui(const RenderCommand& cmd) { // Make sure the backbuffer is the current target. AddBarrier(g_backBuffer, RenderTextureLayout::COLOR_WRITE); FlushBarriers(); SetFramebuffer(g_backBuffer, nullptr, false); auto& commandList = g_commandLists[g_frame]; auto pipeline = g_imPipeline.get(); commandList->setGraphicsPipelineLayout(g_imPipelineLayout.get()); commandList->setPipeline(pipeline); commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0); commandList->setGraphicsDescriptorSet(g_samplerDescriptorSet.get(), 1); auto& drawData = *ImGui::GetDrawData(); commandList->setViewports(RenderViewport(drawData.DisplayPos.x, drawData.DisplayPos.y, drawData.DisplaySize.x, drawData.DisplaySize.y)); ImGuiPushConstants pushConstants{}; pushConstants.displaySize = drawData.DisplaySize; pushConstants.inverseDisplaySize = { 1.0f / drawData.DisplaySize.x, 1.0f / drawData.DisplaySize.y }; commandList->setGraphicsPushConstants(0, &pushConstants); size_t pushConstantRangeMin = ~0; size_t pushConstantRangeMax = 0; auto setPushConstants = [&](void* destination, const void* source, size_t size) { bool dirty = memcmp(destination, source, size) != 0; memcpy(destination, source, size); if (dirty) { size_t offset = reinterpret_cast(destination) - reinterpret_cast(&pushConstants); pushConstantRangeMin = std::min(pushConstantRangeMin, offset); pushConstantRangeMax = std::max(pushConstantRangeMax, offset + size); } }; ImRect clipRect{}; for (int i = 0; i < drawData.CmdListsCount; i++) { auto& drawList = drawData.CmdLists[i]; auto vertexBufferAllocation = g_uploadAllocators[g_frame].allocate(drawList->VtxBuffer.Data, drawList->VtxBuffer.Size * sizeof(ImDrawVert), alignof(ImDrawVert)); auto indexBufferAllocation = g_uploadAllocators[g_frame].allocate(drawList->IdxBuffer.Data, drawList->IdxBuffer.Size * sizeof(uint16_t), alignof(uint16_t)); const RenderVertexBufferView vertexBufferView(vertexBufferAllocation.buffer->at(vertexBufferAllocation.offset), drawList->VtxBuffer.Size * sizeof(ImDrawVert)); const RenderInputSlot inputSlot(0, sizeof(ImDrawVert)); commandList->setVertexBuffers(0, &vertexBufferView, 1, &inputSlot); const RenderIndexBufferView indexBufferView(indexBufferAllocation.buffer->at(indexBufferAllocation.offset), drawList->IdxBuffer.Size * sizeof(uint16_t), RenderFormat::R16_UINT); commandList->setIndexBuffer(&indexBufferView); for (int j = 0; j < drawList->CmdBuffer.Size; j++) { auto& drawCmd = drawList->CmdBuffer[j]; if (drawCmd.UserCallback != nullptr) { auto callbackData = reinterpret_cast(drawCmd.UserCallbackData); switch (static_cast(reinterpret_cast(drawCmd.UserCallback))) { case ImGuiCallback::SetGradient: setPushConstants(&pushConstants.boundsMin, &callbackData->setGradient, sizeof(callbackData->setGradient)); break; case ImGuiCallback::SetShaderModifier: setPushConstants(&pushConstants.shaderModifier, &callbackData->setShaderModifier, sizeof(callbackData->setShaderModifier)); break; case ImGuiCallback::SetOrigin: setPushConstants(&pushConstants.origin, &callbackData->setOrigin, sizeof(callbackData->setOrigin)); break; case ImGuiCallback::SetScale: setPushConstants(&pushConstants.scale, &callbackData->setScale, sizeof(callbackData->setScale)); break; case ImGuiCallback::SetMarqueeFade: setPushConstants(&pushConstants.boundsMin, &callbackData->setMarqueeFade, sizeof(callbackData->setMarqueeFade)); break; case ImGuiCallback::SetOutline: setPushConstants(&pushConstants.outline, &callbackData->setOutline, sizeof(callbackData->setOutline)); break; case ImGuiCallback::SetProceduralOrigin: setPushConstants(&pushConstants.proceduralOrigin, &callbackData->setProceduralOrigin, sizeof(callbackData->setProceduralOrigin)); break; case ImGuiCallback::SetAdditive: { auto pipelineToSet = callbackData->setAdditive.enabled ? g_imAdditivePipeline.get() : g_imPipeline.get(); if (pipeline != pipelineToSet) { commandList->setPipeline(pipelineToSet); pipeline = pipelineToSet; } break; } default: assert(false && "Unknown ImGui callback type."); break; } } else { if (drawCmd.ClipRect.z <= drawCmd.ClipRect.x || drawCmd.ClipRect.w <= drawCmd.ClipRect.y) continue; auto texture = reinterpret_cast(drawCmd.TextureId); uint32_t descriptorIndex = TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D; if (texture != nullptr) { if (texture->layout != RenderTextureLayout::SHADER_READ) { commandList->barriers(RenderBarrierStage::GRAPHICS | RenderBarrierStage::COPY, RenderTextureBarrier(texture->texture, RenderTextureLayout::SHADER_READ)); texture->layout = RenderTextureLayout::SHADER_READ; } descriptorIndex = texture->descriptorIndex; if (texture == g_imFontTexture.get()) descriptorIndex |= 0x80000000; setPushConstants(&pushConstants.texture2DDescriptorIndex, &descriptorIndex, sizeof(descriptorIndex)); } if (pushConstantRangeMin < pushConstantRangeMax) { commandList->setGraphicsPushConstants(0, reinterpret_cast(&pushConstants) + pushConstantRangeMin, pushConstantRangeMin, pushConstantRangeMax - pushConstantRangeMin); pushConstantRangeMin = ~0; pushConstantRangeMax = 0; } if (memcmp(&clipRect, &drawCmd.ClipRect, sizeof(clipRect)) != 0) { commandList->setScissors(RenderRect(int32_t(drawCmd.ClipRect.x), int32_t(drawCmd.ClipRect.y), int32_t(drawCmd.ClipRect.z), int32_t(drawCmd.ClipRect.w))); clipRect = drawCmd.ClipRect; } commandList->drawIndexedInstanced(drawCmd.ElemCount, 1, drawCmd.IdxOffset, drawCmd.VtxOffset, 0); } } } } // We have to check for this to properly handle the following situation: // 1. Wait on swap chain. // 2. Create loading thread. // 3. Loading thread also waits on swap chain. // 4. Loading thread presents and quits. // 5. After the loading thread quits, application also presents. static bool g_pendingWaitOnSwapChain = true; void Video::WaitOnSwapChain() { if (g_pendingWaitOnSwapChain) { if (g_swapChainValid) { g_presentWaitProfiler.Begin(); g_swapChain->wait(); g_presentWaitProfiler.End(); } g_pendingWaitOnSwapChain = false; } } static bool g_shouldPrecompilePipelines; static std::atomic g_executedCommandList; void Video::Present() { g_readyForCommands = false; RenderCommand cmd; cmd.type = RenderCommandType::ExecutePendingStretchRectCommands; g_renderQueue.enqueue(cmd); DrawImGui(); cmd.type = RenderCommandType::ExecuteCommandList; g_renderQueue.enqueue(cmd); // All the shaders are available at this point. We can precompile embedded PSOs then. if (g_shouldPrecompilePipelines) { // EnqueuePipelineTask(PipelineTaskType::PrecompilePipelines, {}); g_shouldPrecompilePipelines = false; } g_executedCommandList.wait(false); g_executedCommandList = false; if (g_swapChainValid) { if (g_pendingWaitOnSwapChain) { g_presentWaitProfiler.Begin(); g_swapChain->wait(); // Never gonna happen outside loading threads as explained above. g_presentWaitProfiler.End(); } RenderCommandSemaphore* signalSemaphores[] = { g_renderSemaphores[g_frame].get() }; g_swapChainValid = g_swapChain->present(g_backBufferIndex, signalSemaphores, std::size(signalSemaphores)); } g_pendingWaitOnSwapChain = true; g_frame = g_nextFrame; g_nextFrame = (g_frame + 1) % NUM_FRAMES; if (g_commandListStates[g_frame]) { g_frameFenceProfiler.Begin(); g_queue->waitForCommandFence(g_commandFences[g_frame].get()); g_frameFenceProfiler.End(); g_commandListStates[g_frame] = false; // Update the GPU profiler with the results from the timestamps of the frame. g_queryPools[g_frame]->queryResults(); const uint64_t *frameTimestamps = g_queryPools[g_frame]->getResults(); g_gpuFrameProfiler.Set(double(frameTimestamps[1] - frameTimestamps[0]) / 1000000.0); } g_dirtyStates = DirtyStates(true); g_uploadAllocators[g_frame].reset(); g_intermediaryUploadAllocator.reset(); g_triangleFanIndexData.reset(); g_quadIndexData.reset(); CheckSwapChain(); cmd.type = RenderCommandType::BeginCommandList; g_renderQueue.enqueue(cmd); if (Config::FPS >= FPS_MIN && Config::FPS < FPS_MAX) { using namespace std::chrono_literals; static std::chrono::steady_clock::time_point s_next; auto now = std::chrono::steady_clock::now(); if (now < s_next) { std::this_thread::sleep_for(std::chrono::floor(s_next - now - 2ms)); while ((now = std::chrono::steady_clock::now()) < s_next) std::this_thread::yield(); } else { s_next = now; } s_next += 1000000000ns / Config::FPS; } g_presentProfiler.Reset(); } void Video::StartPipelinePrecompilation() { g_shouldPrecompilePipelines = true; } static void SetRootDescriptor(const UploadAllocation& allocation, size_t index) { auto& commandList = g_commandLists[g_frame]; if (g_backend != Backend::D3D12) commandList->setGraphicsPushConstants(0, &allocation.deviceAddress, 8 * index, 8); else commandList->setGraphicsRootDescriptor(allocation.buffer->at(allocation.offset), index); } static void ProcExecuteCommandList(const RenderCommand& cmd) { if (g_swapChainValid) { auto swapChainTexture = g_swapChain->getTexture(g_backBufferIndex); if (g_backBuffer->texture == g_intermediaryBackBufferTexture.get()) { struct { float gamma; uint32_t textureDescriptorIndex; int32_t viewportOffsetX; int32_t viewportOffsetY; int32_t viewportWidth; int32_t viewportHeight; } constants; constants.gamma = 0.85f; float offset = (Config::Brightness - 0.5f) * 1.2f; constants.gamma = 1.0f / std::clamp(constants.gamma + offset, 0.1f, 4.0f); constants.textureDescriptorIndex = g_intermediaryBackBufferTextureDescriptorIndex; constants.viewportOffsetX = (int32_t(g_swapChain->getWidth()) - int32_t(Video::s_viewportWidth)) / 2; constants.viewportOffsetY = (int32_t(g_swapChain->getHeight()) - int32_t(Video::s_viewportHeight)) / 2; constants.viewportWidth = Video::s_viewportWidth; constants.viewportHeight = Video::s_viewportHeight; auto &framebuffer = g_backBuffer->framebuffers[swapChainTexture]; if (!framebuffer) { RenderFramebufferDesc desc; desc.colorAttachments = const_cast(&swapChainTexture); desc.colorAttachmentsCount = 1; framebuffer = g_device->createFramebuffer(desc); } RenderTextureBarrier srcBarriers[] = { RenderTextureBarrier(g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ), RenderTextureBarrier(swapChainTexture, RenderTextureLayout::COLOR_WRITE) }; auto &commandList = g_commandLists[g_frame]; commandList->barriers(RenderBarrierStage::GRAPHICS, srcBarriers, std::size(srcBarriers)); commandList->setGraphicsPipelineLayout(g_pipelineLayout.get()); commandList->setPipeline(g_gammaCorrectionPipeline.get()); commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0); SetRootDescriptor(g_uploadAllocators[g_frame].allocate(&constants, sizeof(constants), 0x100), 2); commandList->setFramebuffer(framebuffer.get()); commandList->setViewports(RenderViewport(0.0f, 0.0f, g_swapChain->getWidth(), g_swapChain->getHeight())); commandList->setScissors(RenderRect(0, 0, g_swapChain->getWidth(), g_swapChain->getHeight())); commandList->drawInstanced(6, 1, 0, 0); commandList->barriers(RenderBarrierStage::GRAPHICS, RenderTextureBarrier(swapChainTexture, RenderTextureLayout::PRESENT)); } else { AddBarrier(g_backBuffer, RenderTextureLayout::PRESENT); FlushBarriers(); } } auto &commandList = g_commandLists[g_frame]; commandList->writeTimestamp(g_queryPools[g_frame].get(), 1); commandList->end(); if (g_swapChainValid) { const RenderCommandList *commandLists[] = { commandList.get() }; RenderCommandSemaphore *waitSemaphores[] = { g_acquireSemaphores[g_frame].get() }; RenderCommandSemaphore *signalSemaphores[] = { g_renderSemaphores[g_frame].get() }; g_queue->executeCommandLists( commandLists, std::size(commandLists), waitSemaphores, std::size(waitSemaphores), signalSemaphores, std::size(signalSemaphores), g_commandFences[g_frame].get()); } else { g_queue->executeCommandLists(commandList.get(), g_commandFences[g_frame].get()); } g_commandListStates[g_frame] = true; g_executedCommandList = true; g_executedCommandList.notify_one(); } static void ProcBeginCommandList(const RenderCommand& cmd) { DestructTempResources(); BeginCommandList(); } static GuestSurface* GetBackBuffer() { g_backBuffer->AddRef(); return g_backBuffer; } static GuestSurface* GetDepthStencil() { g_depthStencil->AddRef(); return g_depthStencil; } void Video::ComputeViewportDimensions() { uint32_t width = g_swapChain->getWidth(); uint32_t height = g_swapChain->getHeight(); float aspectRatio = float(width) / float(height); switch (Config::AspectRatio) { case EAspectRatio::Original: { if (aspectRatio > WIDE_ASPECT_RATIO) { s_viewportWidth = height * 16 / 9; s_viewportHeight = height; } else { s_viewportWidth = width; s_viewportHeight = width * 9 / 16; } break; } default: s_viewportWidth = width; s_viewportHeight = height; break; } AspectRatioPatches::ComputeOffsets(); } static RenderFormat ConvertFormat(uint32_t format) { switch (format) { case D3DFMT_A16B16G16R16F: case D3DFMT_A16B16G16R16F_2: case D3DFMT_A16B16G16R16F_EXPAND: return RenderFormat::R16G16B16A16_FLOAT; case D3DFMT_LIN_A8R8G8B8: return RenderFormat::B8G8R8A8_UNORM; case D3DFMT_A8B8G8R8: case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: case D3DFMT_LE_X8R8G8B8: return RenderFormat::R8G8B8A8_UNORM; case D3DFMT_R32F: return RenderFormat::R32_FLOAT; case D3DFMT_D24FS8: case D3DFMT_D24S8: return RenderFormat::D32_FLOAT_S8_UINT; case D3DFMT_G16R16F: case D3DFMT_G16R16F_2: return RenderFormat::R16G16_FLOAT; case D3DFMT_INDEX16: return RenderFormat::R16_UINT; case D3DFMT_INDEX32: return RenderFormat::R32_UINT; case D3DFMT_A8: case D3DFMT_L8: case D3DFMT_L8_2: return RenderFormat::R8_UNORM; case D3DFMT_DXT1: return RenderFormat::BC1_UNORM; case D3DFMT_DXT4: return RenderFormat::BC3_UNORM; default: LOGF_WARNING("{:x}\n", format); assert(false && "Unknown format"); return RenderFormat::R16G16B16A16_FLOAT; } } static void DiscardTexture(GuestBaseTexture* texture, RenderTextureLayout layout) { if (g_backend == Backend::D3D12) { std::lock_guard lock(g_discardMutex); g_discardCommandList->begin(); if (texture->layout != layout) { g_discardCommandList->barriers(RenderBarrierStage::GRAPHICS, RenderTextureBarrier(texture->texture, layout)); texture->layout = layout; } g_discardCommandList->discardTexture(texture->texture); g_discardCommandList->end(); g_queue->executeCommandLists(g_discardCommandList.get(), g_discardCommandFence.get()); g_queue->waitForCommandFence(g_discardCommandFence.get()); } } static GuestTexture* CreateTexture(uint32_t width, uint32_t height, uint32_t depth, uint32_t levels, uint32_t usage, uint32_t format, uint32_t pool, uint32_t type) { ResourceType resourceType; switch (type) { case 17: resourceType = ResourceType::VolumeTexture; break; case 19: resourceType = ResourceType::ArrayTexture; break; default: resourceType = ResourceType::Texture; break; } const auto texture = g_userHeap.AllocPhysical(resourceType); RenderTextureDesc desc; desc.dimension = texture->type == ResourceType::VolumeTexture ? RenderTextureDimension::TEXTURE_3D : RenderTextureDimension::TEXTURE_2D; desc.width = width; desc.height = height; desc.mipLevels = levels; desc.format = ConvertFormat(format); if (texture->type == ResourceType::ArrayTexture) { desc.arraySize = depth; desc.depth = 1; } else { desc.depth = depth; desc.arraySize = 1; } if (RenderFormatIsDepth(desc.format)) desc.flags = RenderTextureFlag::DEPTH_TARGET; else if (usage != 0) desc.flags = RenderTextureFlag::RENDER_TARGET; else desc.flags = RenderTextureFlag::NONE; texture->textureHolder = g_device->createTexture(desc); texture->texture = texture->textureHolder.get(); RenderTextureViewDesc viewDesc; viewDesc.format = desc.format; viewDesc.dimension = texture->type == ResourceType::VolumeTexture ? RenderTextureViewDimension::TEXTURE_3D : RenderTextureViewDimension::TEXTURE_2D; viewDesc.mipLevels = levels; switch (format) { case D3DFMT_D24FS8: case D3DFMT_D24S8: case D3DFMT_L8: case D3DFMT_L8_2: viewDesc.componentMapping = RenderComponentMapping(RenderSwizzle::R, RenderSwizzle::R, RenderSwizzle::R, RenderSwizzle::ONE); break; case D3DFMT_X8R8G8B8: viewDesc.componentMapping = RenderComponentMapping(RenderSwizzle::G, RenderSwizzle::B, RenderSwizzle::A, RenderSwizzle::ONE); break; } texture->textureView = texture->texture->createTextureView(viewDesc); texture->width = width; texture->height = height; texture->depth = depth; texture->format = desc.format; texture->mipLevels = viewDesc.mipLevels; texture->viewDimension = viewDesc.dimension; texture->descriptorIndex = g_textureDescriptorAllocator.allocate(); g_textureDescriptorSet->setTexture(texture->descriptorIndex, texture->texture, RenderTextureLayout::SHADER_READ, texture->textureView.get()); #ifdef _DEBUG texture->texture->setName(fmt::format("Texture {:X}", g_memory.MapVirtual(texture))); #endif if (desc.flags != RenderTextureFlag::NONE) { DiscardTexture(texture, desc.flags == RenderTextureFlag::RENDER_TARGET ? RenderTextureLayout::COLOR_WRITE : RenderTextureLayout::DEPTH_WRITE); } // printf("CreateTexture: w: %d, h: %d, depth: %d, levels: %d, usage: %d, format: %d, pool: %d, type: %d - %x\n", width, height, depth, levels, usage, format, pool, type, texture); return texture; } static RenderHeapType GetBufferHeapType() { return g_capabilities.gpuUploadHeap ? RenderHeapType::GPU_UPLOAD : RenderHeapType::DEFAULT; } static GuestBuffer* CreateVertexBuffer(uint32_t length) { auto buffer = g_userHeap.AllocPhysical(ResourceType::VertexBuffer); buffer->buffer = g_device->createBuffer(RenderBufferDesc::VertexBuffer(length, GetBufferHeapType(), RenderBufferFlag::INDEX)); buffer->dataSize = length; #ifdef _DEBUG buffer->buffer->setName(fmt::format("Vertex Buffer {:X}", g_memory.MapVirtual(buffer))); #endif return buffer; } static GuestBuffer* CreateIndexBuffer(uint32_t length, uint32_t, uint32_t format) { auto buffer = g_userHeap.AllocPhysical(ResourceType::IndexBuffer); buffer->buffer = g_device->createBuffer(RenderBufferDesc::IndexBuffer(length, GetBufferHeapType())); buffer->dataSize = length; buffer->format = ConvertFormat(format); buffer->guestFormat = format; #ifdef _DEBUG buffer->buffer->setName(fmt::format("Index Buffer {:X}", g_memory.MapVirtual(buffer))); #endif return buffer; } static std::vector> g_surfaceCache; // TODO: Singleplayer (possibly) uses the same memory location in EDRAM for HDR and FB0 surfaces, // so we just remember who was created first and use that instead of creating a new one. static GuestSurface* CreateSurface(uint32_t width, uint32_t height, uint32_t format, uint32_t multiSample, GuestSurfaceCreateParams* params) { GuestSurface* surface = nullptr; uint32_t baseValue = params ? params->base.get() : -1; if (params) { for (auto& entry : g_surfaceCache) { GuestSurface* cachedSurface = entry.first; uint32_t cachedBase = entry.second; if (cachedSurface && cachedSurface->width == width && cachedSurface->height == height && cachedSurface->guestFormat == format && cachedBase == baseValue) { surface = cachedSurface; break; } } } if (!surface) { // printf("CreateSurface: w: %d, h: %d, f: %d, ms: %d\n", width, height, format, multiSample); RenderTextureDesc desc; desc.dimension = RenderTextureDimension::TEXTURE_2D; desc.width = width; desc.height = height; desc.depth = 1; desc.mipLevels = 1; desc.arraySize = 1; // desc.multisampling.sampleCount = multiSample != 0 && Config::AntiAliasing != EAntiAliasing::None ? int32_t(Config::AntiAliasing.Value) : RenderSampleCount::COUNT_1; if (multiSample == 0) { desc.multisampling.sampleCount = RenderSampleCount::COUNT_1; } else { desc.multisampling.sampleCount = multiSample == 1 ? RenderSampleCount::COUNT_2 : RenderSampleCount::COUNT_4; } desc.format = ConvertFormat(format); desc.flags = RenderFormatIsDepth(desc.format) ? RenderTextureFlag::DEPTH_TARGET : RenderTextureFlag::RENDER_TARGET; surface = g_userHeap.AllocPhysical(RenderFormatIsDepth(desc.format) ? ResourceType::DepthStencil : ResourceType::RenderTarget); surface->textureHolder = g_device->createTexture(desc); surface->texture = surface->textureHolder.get(); surface->width = width; surface->height = height; surface->format = desc.format; surface->guestFormat = format; surface->sampleCount = desc.multisampling.sampleCount; RenderTextureViewDesc viewDesc; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; viewDesc.format = desc.format; viewDesc.mipLevels = 1; surface->textureView = surface->textureHolder->createTextureView(viewDesc); surface->descriptorIndex = g_textureDescriptorAllocator.allocate(); g_textureDescriptorSet->setTexture(surface->descriptorIndex, surface->textureHolder.get(), RenderTextureLayout::SHADER_READ, surface->textureView.get()); #ifdef _DEBUG surface->texture->setName(fmt::format("{} {:X}", desc.flags & RenderTextureFlag::RENDER_TARGET ? "Render Target" : "Depth Stencil", g_memory.MapVirtual(surface))); #endif DiscardTexture(surface, desc.flags == RenderTextureFlag::RENDER_TARGET ? RenderTextureLayout::COLOR_WRITE : RenderTextureLayout::DEPTH_WRITE); if (params) { surface->wasCached = true; g_surfaceCache.emplace_back(surface, baseValue); } } return surface; } static void FlushViewport() { auto& commandList = g_commandLists[g_frame]; if (g_dirtyStates.viewport) { auto viewport = g_viewport; // if (viewport.minDepth > viewport.maxDepth) // std::swap(viewport.minDepth, viewport.maxDepth); commandList->setViewports(viewport); g_dirtyStates.viewport = false; } if (g_dirtyStates.scissorRect) { auto scissorRect = g_scissorTestEnable ? g_scissorRect : RenderRect( g_viewport.x, g_viewport.y, g_viewport.x + g_viewport.width, g_viewport.y + g_viewport.height); commandList->setScissors(scissorRect); g_dirtyStates.scissorRect = false; } } static void StretchRect(GuestDevice* device, uint32_t flags, uint32_t, GuestTexture* texture, uint32_t, uint32_t, uint32_t destSliceOrFace) { // printf("StretchRect %x\n", texture); RenderCommand cmd; cmd.type = RenderCommandType::StretchRect; cmd.stretchRect.flags = flags; cmd.stretchRect.texture = texture; cmd.stretchRect.destSliceOrFace = destSliceOrFace; g_renderQueue.enqueue(cmd); } static void SetTextureInRenderThread(uint32_t index, GuestTexture* texture); static void SetSurface(uint32_t index, GuestSurface* surface); static void ProcStretchRect(const RenderCommand& cmd) { const auto& args = cmd.stretchRect; const bool isDepthStencil = (args.flags & 0x4) != 0; const auto surface = isDepthStencil ? g_depthStencil : g_renderTarget; // Erase previous pending command so it doesn't cause the texture to be overriden. if (args.texture->sourceSurface != nullptr) args.texture->sourceSurface->destinationTextures.erase(args.texture); args.texture->sourceSurface = surface; // printf("ProcStretchRect: surface - %x %x ? (%x : %x)\n", surface, isDepthStencil, g_depthStencil, g_renderTarget); surface->destinationTextures.emplace(args.texture, args.destSliceOrFace); // If the texture is assigned to any slots, set it again. This'll also push the barrier. for (uint32_t i = 0; i < std::size(g_textures); i++) { if (g_textures[i] == args.texture) { // TODO: Render depth directly to slice and avoid copy // Set the original texture for MSAA and surface-to-array textures as they always get resolved. if (surface->sampleCount != RenderSampleCount::COUNT_1 || args.texture->type == ResourceType::ArrayTexture) { SetTextureInRenderThread(i, args.texture); g_pendingResolves.emplace(surface); } else { SetSurface(i, surface); } } } // Remember to clear later. g_pendingSurfaceCopies.emplace(surface); } static void SetDefaultViewport(GuestDevice* device, GuestSurface* surface) { if (surface != nullptr) { RenderCommand cmd; cmd.type = RenderCommandType::SetViewport; cmd.setViewport.x = 0.0f; cmd.setViewport.y = 0.0f; cmd.setViewport.width = float(surface->width); cmd.setViewport.height = float(surface->height); cmd.setViewport.minDepth = 0.0f; cmd.setViewport.maxDepth = 1.0f; g_renderQueue.enqueue(cmd); device->viewport.x = 0.0f; device->viewport.y = 0.0f; device->viewport.width = float(surface->width); device->viewport.height = float(surface->height); device->viewport.minZ = 0.0f; device->viewport.maxZ = 1.0f; } } static void SetRenderTarget(GuestDevice* device, uint32_t index, GuestSurface* renderTarget) { if (index == 0) { RenderCommand cmd; cmd.type = RenderCommandType::SetRenderTarget; cmd.setRenderTarget.renderTarget = renderTarget; g_renderQueue.enqueue(cmd); SetDefaultViewport(device, renderTarget); } else { // Multiple targets are not currently handled. Make sure any attempt to set them is nullptr. assert(renderTarget == nullptr); } } static void ProcSetRenderTarget(const RenderCommand& cmd) { const auto& args = cmd.setRenderTarget; SetDirtyValue(g_dirtyStates.renderTargetAndDepthStencil, g_renderTarget, args.renderTarget); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.renderTargetFormat, args.renderTarget != nullptr ? args.renderTarget->format : RenderFormat::UNKNOWN); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.sampleCount, args.renderTarget != nullptr ? args.renderTarget->sampleCount : RenderSampleCount::COUNT_1); // When alpha to coverage is enabled, update the alpha test mode as it's dependent on sample count. SetAlphaTestMode((g_pipelineState.specConstants & (SPEC_CONSTANT_ALPHA_TEST | SPEC_CONSTANT_ALPHA_TO_COVERAGE)) != 0); } static void SetDepthStencilSurface(GuestDevice* device, GuestSurface* depthStencil) { RenderCommand cmd; cmd.type = RenderCommandType::SetDepthStencilSurface; cmd.setDepthStencilSurface.depthStencil = depthStencil; g_renderQueue.enqueue(cmd); SetDefaultViewport(device, depthStencil); } static void ProcSetDepthStencilSurface(const RenderCommand& cmd) { const auto& args = cmd.setDepthStencilSurface; SetDirtyValue(g_dirtyStates.renderTargetAndDepthStencil, g_depthStencil, args.depthStencil); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.depthStencilFormat, args.depthStencil != nullptr ? args.depthStencil->format : RenderFormat::UNKNOWN); } static bool PopulateBarriersForStretchRect(GuestSurface* renderTarget, GuestSurface* depthStencil) { bool addedAny = false; for (const auto surface : { renderTarget, depthStencil }) { if (surface != nullptr && !surface->destinationTextures.empty()) { const bool multiSampling = surface->sampleCount != RenderSampleCount::COUNT_1; RenderTextureLayout srcLayout; RenderTextureLayout dstLayout; bool shaderResolve = true; if (multiSampling) { if (!RenderFormatIsDepth(surface->format) || g_capabilities.resolveModes) { srcLayout = RenderTextureLayout::RESOLVE_SOURCE; dstLayout = RenderTextureLayout::RESOLVE_DEST; shaderResolve = false; } } if (shaderResolve) { srcLayout = RenderTextureLayout::SHADER_READ; dstLayout = (RenderFormatIsDepth(surface->format) ? RenderTextureLayout::DEPTH_WRITE : RenderTextureLayout::COLOR_WRITE); } AddBarrier(surface, srcLayout); for (const auto [texture, _] : surface->destinationTextures) AddBarrier(texture, dstLayout); addedAny = true; } } return addedAny; } static void ExecutePendingStretchRectCommands(GuestSurface* renderTarget, GuestSurface* depthStencil) { auto& commandList = g_commandLists[g_frame]; for (const auto surface : { renderTarget, depthStencil }) { if (surface != nullptr && !surface->destinationTextures.empty()) { const bool multiSampling = surface->sampleCount != RenderSampleCount::COUNT_1; const bool isDepthStencil = RenderFormatIsDepth(surface->format); for (const auto [texture, slice] : surface->destinationTextures) { bool shaderResolve = true; if (multiSampling) { if (!isDepthStencil || g_capabilities.resolveModes) { if (isDepthStencil) commandList->resolveTextureRegion(texture->texture, 0, 0, surface->texture, nullptr, RenderResolveMode::MIN); else commandList->resolveTexture(texture->texture, surface->texture); shaderResolve = false; } } if (shaderResolve) { RenderPipeline* pipeline = nullptr; if (multiSampling) { uint32_t pipelineIndex = 0; switch (surface->sampleCount) { case RenderSampleCount::COUNT_2: pipelineIndex = 0; break; case RenderSampleCount::COUNT_4: pipelineIndex = 1; break; case RenderSampleCount::COUNT_8: pipelineIndex = 2; break; default: assert(false && "Unsupported MSAA sample count"); break; } if (isDepthStencil) { pipeline = g_resolveMsaaDepthPipelines[pipelineIndex].get(); } else { auto& resolveMsaaColorPipeline = g_resolveMsaaColorPipelines[surface->format][pipelineIndex]; if (resolveMsaaColorPipeline == nullptr) { RenderGraphicsPipelineDesc desc; desc.pipelineLayout = g_pipelineLayout.get(); desc.vertexShader = g_copyShader.get(); desc.pixelShader = g_resolveMsaaColorShaders[pipelineIndex].get(); desc.renderTargetFormat[0] = texture->format; desc.renderTargetBlend[0] = RenderBlendDesc::Copy(); desc.renderTargetCount = 1; resolveMsaaColorPipeline = g_device->createGraphicsPipeline(desc); } pipeline = resolveMsaaColorPipeline.get(); } } else { if (isDepthStencil) { pipeline = g_copyDepthPipeline.get(); } else { auto& copyColorPipeline = g_copyColorPipelines[texture->format]; if (copyColorPipeline == nullptr) { RenderGraphicsPipelineDesc desc; desc.pipelineLayout = g_pipelineLayout.get(); desc.vertexShader = g_copyShader.get(); desc.pixelShader = g_copyColorShader.get(); desc.renderTargetFormat[0] = texture->format; desc.renderTargetBlend[0] = RenderBlendDesc::Copy(); desc.renderTargetCount = 1; copyColorPipeline = g_device->createGraphicsPipeline(desc); } pipeline = copyColorPipeline.get(); } } auto& framebuffer = texture->framebuffers[slice]; if (framebuffer == nullptr) { if (isDepthStencil) { RenderTextureViewDesc viewDesc; viewDesc.format = texture->format; viewDesc.dimension = texture->viewDimension; viewDesc.mipLevels = texture->mipLevels; viewDesc.arrayIndex = slice; viewDesc.arraySize = 1; auto& view = texture->framebufferViews.emplace_back(texture->texture->createTextureView(viewDesc)); RenderFramebufferDesc desc; desc.depthAttachmentView = view.get(); framebuffer = g_device->createFramebuffer(desc); } else { RenderFramebufferDesc desc; desc.colorAttachments = const_cast(&texture->texture); desc.colorAttachmentsCount = 1; framebuffer = g_device->createFramebuffer(desc); } } if (g_framebuffer != framebuffer.get()) { commandList->setFramebuffer(framebuffer.get()); g_framebuffer = framebuffer.get(); } commandList->setPipeline(pipeline); commandList->setViewports(RenderViewport(0.0f, 0.0f, float(texture->width), float(texture->height), 0.0f, 1.0f)); commandList->setScissors(RenderRect(0, 0, texture->width, texture->height)); commandList->setGraphicsPushConstants(0, &surface->descriptorIndex, 0, sizeof(uint32_t)); commandList->drawInstanced(6, 1, 0, 0); g_dirtyStates.renderTargetAndDepthStencil = true; g_dirtyStates.viewport = true; g_dirtyStates.pipelineState = true; g_dirtyStates.scissorRect = true; if (g_backend != Backend::D3D12) { g_dirtyStates.vertexShaderConstants = true; // The push constant call invalidates vertex shader constants. g_dirtyStates.depthBias = true; // Static depth bias in copy pipeline invalidates dynamic depth bias. } } texture->sourceSurface = nullptr; // Check if any texture slots had this texture assigned, and make it point back at the original texture. for (uint32_t i = 0; i < std::size(g_textures); i++) { if (g_textures[i] == texture) SetTextureInRenderThread(i, texture); } } surface->destinationTextures.clear(); } } } static void ProcExecutePendingStretchRectCommands(const RenderCommand& cmd) { bool foundAny = false; for (const auto surface : g_pendingSurfaceCopies) { // Depth stencil textures in this game are guaranteed to be transient. if (!RenderFormatIsDepth(surface->format)) foundAny |= PopulateBarriersForStretchRect(surface, nullptr); } if (foundAny) { FlushBarriers(); for (const auto surface : g_pendingSurfaceCopies) { if (!RenderFormatIsDepth(surface->format)) ExecutePendingStretchRectCommands(surface, nullptr); for (const auto [texture, _] : surface->destinationTextures) texture->sourceSurface = nullptr; surface->destinationTextures.clear(); } } g_pendingSurfaceCopies.clear(); g_pendingResolves.clear(); } static void SetFramebuffer(GuestSurface* renderTarget, GuestSurface* depthStencil, bool settingForClear) { if (settingForClear || g_dirtyStates.renderTargetAndDepthStencil) { // printf("SetFramebuffer %x %x\n", renderTarget, depthStencil); GuestSurface* framebufferContainer = nullptr; RenderTexture* framebufferKey = nullptr; if (renderTarget != nullptr && depthStencil != nullptr) { framebufferContainer = depthStencil; // Backbuffer texture changes per frame so we can't use the depth stencil as the key. framebufferKey = renderTarget->texture; } else if (renderTarget != nullptr && depthStencil == nullptr) { framebufferContainer = renderTarget; framebufferKey = renderTarget->texture; // Backbuffer texture changes per frame so we can't assume nullptr for it. } else if (renderTarget == nullptr && depthStencil != nullptr) { framebufferContainer = depthStencil; framebufferKey = nullptr; } auto& commandList = g_commandLists[g_frame]; if (framebufferContainer != nullptr) { auto& framebuffer = framebufferContainer->framebuffers[framebufferKey]; if (framebuffer == nullptr) { RenderFramebufferDesc desc; if (renderTarget != nullptr) { desc.colorAttachments = const_cast(&renderTarget->texture); desc.colorAttachmentsCount = 1; } if (depthStencil != nullptr) desc.depthAttachment = depthStencil->texture; framebuffer = g_device->createFramebuffer(desc); } if (g_framebuffer != framebuffer.get()) { commandList->setFramebuffer(framebuffer.get()); g_framebuffer = framebuffer.get(); } } else if (g_framebuffer != nullptr) { commandList->setFramebuffer(nullptr); g_framebuffer = nullptr; } if (g_framebuffer != nullptr) { SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.halfPixelOffsetX, 1.0f / float(g_framebuffer->getWidth())); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.halfPixelOffsetY, -1.0f / float(g_framebuffer->getHeight())); } g_dirtyStates.renderTargetAndDepthStencil = settingForClear; } } static void Clear(GuestDevice* device, uint32_t flags, uint32_t, be* color, double z, uint32_t stencil) { RenderCommand cmd; cmd.type = RenderCommandType::Clear; cmd.clear.flags = flags; cmd.clear.color[0] = color[0]; cmd.clear.color[1] = color[1]; cmd.clear.color[2] = color[2]; cmd.clear.color[3] = color[3]; cmd.clear.z = float(z); cmd.clear.stencil = stencil; g_renderQueue.enqueue(cmd); } static void ProcClear(const RenderCommand& cmd) { const auto& args = cmd.clear; if (PopulateBarriersForStretchRect(g_renderTarget, g_depthStencil)) { FlushBarriers(); ExecutePendingStretchRectCommands(g_renderTarget, g_depthStencil); } AddBarrier(g_renderTarget, RenderTextureLayout::COLOR_WRITE); AddBarrier(g_depthStencil, RenderTextureLayout::DEPTH_WRITE); FlushBarriers(); bool canClearInOnePass = (g_renderTarget == nullptr) || (g_depthStencil == nullptr) || (g_renderTarget->width == g_depthStencil->width && g_renderTarget->height == g_depthStencil->height); if (canClearInOnePass) { SetFramebuffer(g_renderTarget, g_depthStencil, true); } auto& commandList = g_commandLists[g_frame]; if (g_renderTarget != nullptr && (args.flags & D3DCLEAR_TARGET) != 0) { if (!canClearInOnePass) { SetFramebuffer(g_renderTarget, nullptr, true); } commandList->clearColor(0, RenderColor(args.color[0], args.color[1], args.color[2], args.color[3])); } const bool clearDepth = (args.flags & D3DCLEAR_ZBUFFER) != 0; const bool clearStencil = (args.flags & D3DCLEAR_STENCIL) != 0; if (g_depthStencil != nullptr && (clearDepth || clearStencil)) { if (!canClearInOnePass) { SetFramebuffer(nullptr, g_depthStencil, true); } commandList->clearDepthStencil(clearDepth, clearStencil, args.z, args.stencil); } } static void SetViewport(GuestDevice* device, GuestViewport* viewport) { RenderCommand cmd; cmd.type = RenderCommandType::SetViewport; cmd.setViewport.x = viewport->x; cmd.setViewport.y = viewport->y; cmd.setViewport.width = viewport->width; cmd.setViewport.height = viewport->height; cmd.setViewport.minDepth = viewport->minZ; cmd.setViewport.maxDepth = viewport->maxZ; g_renderQueue.enqueue(cmd); device->viewport.x = float(viewport->x); device->viewport.y = float(viewport->y); device->viewport.width = float(viewport->width); device->viewport.height = float(viewport->height); device->viewport.minZ = viewport->minZ; device->viewport.maxZ = viewport->maxZ; } static void ProcSetViewport(const RenderCommand& cmd) { const auto& args = cmd.setViewport; SetDirtyValue(g_dirtyStates.viewport, g_viewport.x, args.x); SetDirtyValue(g_dirtyStates.viewport, g_viewport.y, args.y); SetDirtyValue(g_dirtyStates.viewport, g_viewport.width, args.width); SetDirtyValue(g_dirtyStates.viewport, g_viewport.height, args.height); SetDirtyValue(g_dirtyStates.viewport, g_viewport.minDepth, args.minDepth); SetDirtyValue(g_dirtyStates.viewport, g_viewport.maxDepth, args.maxDepth); uint32_t specConstants = g_pipelineState.specConstants; if (args.minDepth > args.maxDepth) specConstants |= SPEC_CONSTANT_REVERSE_Z; else specConstants &= ~SPEC_CONSTANT_REVERSE_Z; SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.specConstants, specConstants); g_dirtyStates.scissorRect |= g_dirtyStates.viewport; } static void SetTexture(GuestDevice* device, uint32_t index, GuestTexture* texture) { // printf("SetTexture: %x %d %x\n", device, index, texture); if (Config::IsControllerIconsPS3() && texture != nullptr && texture->patchedTexture != nullptr) texture = texture->patchedTexture.get(); RenderCommand cmd; cmd.type = RenderCommandType::SetTexture; cmd.setTexture.index = index; cmd.setTexture.texture = texture; g_renderQueue.enqueue(cmd); } static void SetTextureInRenderThread(uint32_t index, GuestTexture* texture) { AddBarrier(texture, RenderTextureLayout::SHADER_READ); auto viewDimension = texture != nullptr ? texture->viewDimension : RenderTextureViewDimension::UNKNOWN; SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.texture2DIndices[index], viewDimension == RenderTextureViewDimension::TEXTURE_2D ? texture->descriptorIndex : TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.texture2DArrayIndices[index], texture != nullptr && viewDimension == RenderTextureViewDimension::TEXTURE_2D ? texture->descriptorIndex : TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D_ARRAY); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.textureCubeIndices[index], texture != nullptr && viewDimension == RenderTextureViewDimension::TEXTURE_CUBE ? texture->descriptorIndex : TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE); } static void SetSurface(uint32_t index, GuestSurface* surface) { AddBarrier(surface, RenderTextureLayout::SHADER_READ); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.texture2DIndices[index], surface->descriptorIndex); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.texture2DArrayIndices[index], uint32_t(TEXTURE_DESCRIPTOR_NULL_TEXTURE_2D_ARRAY)); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.textureCubeIndices[index], uint32_t(TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE)); } static void ProcSetTexture(const RenderCommand& cmd) { const auto& args = cmd.setTexture; // If a pending copy operation is detected, set the source surface. The indices will be fixed later if flushing is necessary. bool shouldSetTexture = true; if (args.texture != nullptr && args.texture->sourceSurface != nullptr) { // TODO: Render depth directly to slice and avoid copy // MSAA surfaces or surface-to-array need to be resolved and cannot be used directly. if (args.texture->sourceSurface->sampleCount != RenderSampleCount::COUNT_1 || args.texture->type == ResourceType::ArrayTexture) { g_pendingResolves.emplace(args.texture->sourceSurface); } else { SetSurface(args.index, args.texture->sourceSurface); shouldSetTexture = false; } } if (shouldSetTexture) SetTextureInRenderThread(args.index, args.texture); g_textures[args.index] = args.texture; } static void SetScissorRect(GuestDevice* device, GuestRect* rect) { RenderCommand cmd; cmd.type = RenderCommandType::SetScissorRect; cmd.setScissorRect.top = rect->top; cmd.setScissorRect.left = rect->left; cmd.setScissorRect.bottom = rect->bottom; cmd.setScissorRect.right = rect->right; g_renderQueue.enqueue(cmd); } static void ProcSetScissorRect(const RenderCommand& cmd) { const auto& args = cmd.setScissorRect; SetDirtyValue(g_dirtyStates.scissorRect, g_scissorRect.top, args.top); SetDirtyValue(g_dirtyStates.scissorRect, g_scissorRect.left, args.left); SetDirtyValue(g_dirtyStates.scissorRect, g_scissorRect.bottom, args.bottom); SetDirtyValue(g_dirtyStates.scissorRect, g_scissorRect.right, args.right); } static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specConstants) { if (g_backend != Backend::D3D12 || guestShader->shaderCacheEntry == nullptr || guestShader->shaderCacheEntry->specConstantsMask == 0) { std::lock_guard lock(guestShader->mutex); if (guestShader->shader == nullptr) { assert(guestShader->shaderCacheEntry != nullptr); switch (g_backend) { case Backend::VULKAN: { auto compressedSpirvData = g_shaderCache.get() + guestShader->shaderCacheEntry->spirvOffset; std::vector decoded(smolv::GetDecodedBufferSize(compressedSpirvData, guestShader->shaderCacheEntry->spirvSize)); bool result = smolv::Decode(compressedSpirvData, guestShader->shaderCacheEntry->spirvSize, decoded.data(), decoded.size()); assert(result); guestShader->shader = g_device->createShader(decoded.data(), decoded.size(), "shaderMain", RenderShaderFormat::SPIRV); break; } case Backend::D3D12: { guestShader->shader = g_device->createShader(g_shaderCache.get() + guestShader->shaderCacheEntry->dxilOffset, guestShader->shaderCacheEntry->dxilSize, "shaderMain", RenderShaderFormat::DXIL); break; } case Backend::METAL: { guestShader->shader = g_device->createShader(g_shaderCache.get() + guestShader->shaderCacheEntry->airOffset, guestShader->shaderCacheEntry->airSize, "shaderMain", RenderShaderFormat::METAL); break; } } #ifdef _DEBUG guestShader->shader->setName(fmt::format("{}:{:x}", guestShader->shaderCacheEntry->filename, guestShader->shaderCacheEntry->hash)); #endif } return guestShader->shader.get(); } specConstants &= guestShader->shaderCacheEntry->specConstantsMask; RenderShader* shader; { std::lock_guard lock(guestShader->mutex); shader = guestShader->linkedShaders[specConstants].get(); } #ifdef MARATHON_RECOMP_D3D12 if (shader == nullptr) { static Mutex g_compiledSpecConstantLibraryBlobMutex; static ankerl::unordered_dense::map> g_compiledSpecConstantLibraryBlobs; thread_local ComPtr s_dxcCompiler; thread_local ComPtr s_dxcLinker; thread_local ComPtr s_dxcUtils; wchar_t specConstantsLibName[0x100]; swprintf_s(specConstantsLibName, L"SpecConstants_%d", specConstants); ComPtr specConstantLibraryBlob; { std::lock_guard lock(g_compiledSpecConstantLibraryBlobMutex); specConstantLibraryBlob = g_compiledSpecConstantLibraryBlobs[specConstants]; } if (specConstantLibraryBlob == nullptr) { if (s_dxcCompiler == nullptr) { HRESULT hr = DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(s_dxcCompiler.GetAddressOf())); assert(SUCCEEDED(hr) && s_dxcCompiler != nullptr); } char libraryHlsl[0x100]; sprintf_s(libraryHlsl, "export uint g_SpecConstants() { return %d; }", specConstants); DxcBuffer buffer{}; buffer.Ptr = libraryHlsl; buffer.Size = strlen(libraryHlsl); const wchar_t* args[1]; args[0] = L"-T lib_6_3"; ComPtr result; HRESULT hr = s_dxcCompiler->Compile(&buffer, args, std::size(args), nullptr, IID_PPV_ARGS(result.GetAddressOf())); assert(SUCCEEDED(hr) && result != nullptr); hr = result->GetResult(specConstantLibraryBlob.GetAddressOf()); assert(SUCCEEDED(hr) && specConstantLibraryBlob != nullptr); std::lock_guard lock(g_compiledSpecConstantLibraryBlobMutex); g_compiledSpecConstantLibraryBlobs.emplace(specConstants, specConstantLibraryBlob); } if (s_dxcLinker == nullptr) { HRESULT hr = DxcCreateInstance(CLSID_DxcLinker, IID_PPV_ARGS(s_dxcLinker.GetAddressOf())); assert(SUCCEEDED(hr) && s_dxcLinker != nullptr); } s_dxcLinker->RegisterLibrary(specConstantsLibName, specConstantLibraryBlob.Get()); wchar_t shaderLibName[0x100]; swprintf_s(shaderLibName, L"Shader_%d", guestShader->shaderCacheEntry->dxilOffset); ComPtr shaderLibraryBlob; { std::lock_guard lock(guestShader->mutex); shaderLibraryBlob = guestShader->libraryBlob; } if (shaderLibraryBlob == nullptr) { if (s_dxcUtils == nullptr) { HRESULT hr = DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(s_dxcUtils.GetAddressOf())); assert(SUCCEEDED(hr) && s_dxcUtils != nullptr); } HRESULT hr = s_dxcUtils->CreateBlobFromPinned( g_shaderCache.get() + guestShader->shaderCacheEntry->dxilOffset, guestShader->shaderCacheEntry->dxilSize, DXC_CP_ACP, shaderLibraryBlob.GetAddressOf()); assert(SUCCEEDED(hr) && shaderLibraryBlob != nullptr); std::lock_guard lock(guestShader->mutex); guestShader->libraryBlob = shaderLibraryBlob; } s_dxcLinker->RegisterLibrary(shaderLibName, shaderLibraryBlob.Get()); const wchar_t* libraryNames[] = { specConstantsLibName, shaderLibName }; ComPtr result; HRESULT hr = s_dxcLinker->Link(L"shaderMain", guestShader->type == ResourceType::VertexShader ? L"vs_6_0" : L"ps_6_0", libraryNames, std::size(libraryNames), nullptr, 0, result.GetAddressOf()); assert(SUCCEEDED(hr) && result != nullptr); ComPtr blob; hr = result->GetResult(blob.GetAddressOf()); assert(SUCCEEDED(hr) && blob != nullptr); { std::lock_guard lock(guestShader->mutex); auto& linkedShader = guestShader->linkedShaders[specConstants]; if (linkedShader == nullptr) { linkedShader = g_device->createShader(blob->GetBufferPointer(), blob->GetBufferSize(), "shaderMain", RenderShaderFormat::DXIL); guestShader->shaderBlobs.push_back(std::move(blob)); } shader = linkedShader.get(); #ifdef _DEBUG shader->setName(fmt::format("{}:{:x}", guestShader->shaderCacheEntry->filename, guestShader->shaderCacheEntry->hash)); #endif } } #endif return shader; } static void SanitizePipelineState(PipelineState& pipelineState) { if (!pipelineState.zEnable && !pipelineState.stencilEnable) { pipelineState.depthStencilFormat = RenderFormat::UNKNOWN; } if (!pipelineState.zEnable) { pipelineState.zWriteEnable = false; pipelineState.zFunc = RenderComparisonFunction::LESS; pipelineState.slopeScaledDepthBias = 0.0f; pipelineState.depthBias = 0; } if (!pipelineState.stencilEnable) { pipelineState.stencilTwoSided = false; pipelineState.stencilFunc = RenderComparisonFunction::ALWAYS; pipelineState.stencilFail = RenderStencilOp::KEEP; pipelineState.stencilZFail = RenderStencilOp::KEEP; pipelineState.stencilPass = RenderStencilOp::KEEP; pipelineState.stencilMask = 0xFFFFFFFF; pipelineState.stencilWriteMask = 0xFFFFFFFF; pipelineState.stencilRef = 0; } if (!pipelineState.stencilTwoSided) { pipelineState.stencilFuncCCW = pipelineState.stencilFunc; pipelineState.stencilFailCCW = pipelineState.stencilFail; pipelineState.stencilZFailCCW = pipelineState.stencilZFail; pipelineState.stencilPassCCW = pipelineState.stencilPass; } if (pipelineState.slopeScaledDepthBias == 0.0f) pipelineState.slopeScaledDepthBias = 0.0f; // Remove sign. if (!pipelineState.colorWriteEnable) { pipelineState.alphaBlendEnable = false; pipelineState.renderTargetFormat = RenderFormat::UNKNOWN; } if (!pipelineState.alphaBlendEnable) { pipelineState.srcBlend = RenderBlend::ONE; pipelineState.destBlend = RenderBlend::ZERO; pipelineState.blendOp = RenderBlendOperation::ADD; pipelineState.srcBlendAlpha = RenderBlend::ONE; pipelineState.destBlendAlpha = RenderBlend::ZERO; pipelineState.blendOpAlpha = RenderBlendOperation::ADD; } for (size_t i = 0; i < 16; i++) { if (!pipelineState.vertexDeclaration->vertexStreams[i]) pipelineState.vertexStrides[i] = 0; } uint32_t specConstantsMask = 0; if (pipelineState.vertexShader->shaderCacheEntry != nullptr) specConstantsMask |= pipelineState.vertexShader->shaderCacheEntry->specConstantsMask; if (pipelineState.pixelShader != nullptr && pipelineState.pixelShader->shaderCacheEntry != nullptr) specConstantsMask |= pipelineState.pixelShader->shaderCacheEntry->specConstantsMask; pipelineState.specConstants &= specConstantsMask; } static std::unique_ptr CreateGraphicsPipeline(const PipelineState& pipelineState) { #ifdef ASYNC_PSO_DEBUG ++g_pipelinesCurrentlyCompiling; #endif RenderGraphicsPipelineDesc desc; desc.pipelineLayout = g_pipelineLayout.get(); desc.vertexShader = GetOrLinkShader(pipelineState.vertexShader, pipelineState.specConstants); if (pipelineState.enableConditionalSurvey) desc.pixelShader = GetOrLinkShader(g_conditionalSurveyPSShader.get(), pipelineState.specConstants); else if (pipelineState.pixelShader != nullptr) desc.pixelShader = GetOrLinkShader(pipelineState.pixelShader, pipelineState.specConstants); else desc.pixelShader = nullptr; desc.depthFunction = pipelineState.zFunc; desc.depthEnabled = pipelineState.zEnable; desc.depthWriteEnabled = pipelineState.zWriteEnable; desc.depthBias = pipelineState.depthBias; desc.stencilEnabled = pipelineState.stencilEnable; desc.stencilReadMask = pipelineState.stencilMask; desc.stencilWriteMask = pipelineState.stencilWriteMask; desc.stencilReference = pipelineState.stencilRef; desc.stencilFrontFace.compareFunction = pipelineState.stencilFunc; desc.stencilFrontFace.failOp = pipelineState.stencilFail; desc.stencilFrontFace.depthFailOp = pipelineState.stencilZFail; desc.stencilFrontFace.passOp = pipelineState.stencilPass; if (pipelineState.stencilTwoSided) { desc.stencilBackFace.compareFunction = pipelineState.stencilFuncCCW; desc.stencilBackFace.failOp = pipelineState.stencilFailCCW; desc.stencilBackFace.depthFailOp = pipelineState.stencilZFailCCW; desc.stencilBackFace.passOp = pipelineState.stencilPassCCW; } else { desc.stencilBackFace = desc.stencilFrontFace; } desc.slopeScaledDepthBias = pipelineState.slopeScaledDepthBias; desc.dynamicDepthBiasEnabled = g_capabilities.dynamicDepthBias; desc.depthClipEnabled = true; desc.primitiveTopology = pipelineState.primitiveTopology; desc.cullMode = pipelineState.cullMode; desc.frontFace = pipelineState.frontFace; desc.renderTargetFormat[0] = pipelineState.renderTargetFormat; desc.renderTargetBlend[0].blendEnabled = pipelineState.alphaBlendEnable; desc.renderTargetBlend[0].srcBlend = pipelineState.srcBlend; desc.renderTargetBlend[0].dstBlend = pipelineState.destBlend; desc.renderTargetBlend[0].blendOp = pipelineState.blendOp; desc.renderTargetBlend[0].srcBlendAlpha = pipelineState.srcBlendAlpha; desc.renderTargetBlend[0].dstBlendAlpha = pipelineState.destBlendAlpha; desc.renderTargetBlend[0].blendOpAlpha = pipelineState.blendOpAlpha; desc.renderTargetBlend[0].renderTargetWriteMask = pipelineState.colorWriteEnable; desc.renderTargetCount = pipelineState.renderTargetFormat != RenderFormat::UNKNOWN ? 1 : 0; desc.depthTargetFormat = pipelineState.depthStencilFormat; desc.multisampling.sampleCount = pipelineState.sampleCount; desc.alphaToCoverageEnabled = pipelineState.enableAlphaToCoverage; desc.inputElements = pipelineState.vertexDeclaration->inputElements.get(); desc.inputElementsCount = pipelineState.vertexDeclaration->inputElementCount; RenderSpecConstant specConstant{}; specConstant.value = pipelineState.specConstants; if (pipelineState.specConstants != 0) { desc.specConstants = &specConstant; desc.specConstantsCount = 1; } RenderInputSlot inputSlots[16]{}; uint32_t inputSlotIndices[16]{}; uint32_t inputSlotCount = 0; for (size_t i = 0; i < pipelineState.vertexDeclaration->inputElementCount; i++) { auto& inputElement = pipelineState.vertexDeclaration->inputElements[i]; auto& inputSlotIndex = inputSlotIndices[inputElement.slotIndex]; if (inputSlotIndex == NULL) inputSlotIndex = ++inputSlotCount; auto& inputSlot = inputSlots[inputSlotIndex - 1]; inputSlot.index = inputElement.slotIndex; inputSlot.stride = pipelineState.vertexStrides[inputElement.slotIndex]; inputSlot.classification = RenderInputSlotClassification::PER_VERTEX_DATA; } desc.inputSlots = inputSlots; desc.inputSlotsCount = inputSlotCount; auto pipeline = g_device->createGraphicsPipeline(desc); #ifdef ASYNC_PSO_DEBUG --g_pipelinesCurrentlyCompiling; #endif return pipeline; } static RenderPipeline* CreateGraphicsPipelineInRenderThread(PipelineState pipelineState) { SanitizePipelineState(pipelineState); XXH64_hash_t hash = XXH3_64bits(&pipelineState, sizeof(pipelineState)); auto& pipeline = g_pipelines[hash]; if (pipeline == nullptr) { pipeline = CreateGraphicsPipeline(pipelineState); #ifdef ASYNC_PSO_DEBUG bool loading = *SWA::SGlobals::ms_IsLoading; if (loading) ++g_pipelinesCreatedAsynchronously; else ++g_pipelinesCreatedInRenderThread; pipeline->setName(fmt::format("{} {} {} {:X}", loading ? "ASYNC" : "", pipelineState.vertexShader->name, pipelineState.pixelShader != nullptr ? pipelineState.pixelShader->name : "", hash)); if (!loading) { std::lock_guard lock(g_debugMutex); g_pipelineDebugText = fmt::format( "PipelineState {:X}:\n" " vertexShader: {}\n" " pixelShader: {}\n" " vertexDeclaration: {:X}\n" " zEnable: {}\n" " zWriteEnable: {}\n" " stencilEnable: {}\n" " stencilTwoSided: {}\n" " srcBlend: {}\n" " destBlend: {}\n" " cullMode: {}\n" " frontFace: {}\n" " zFunc: {}\n" " stencilFunc: {}\n" " stencilFail: {}\n" " stencilZFail: {}\n" " stencilPass: {}\n" " stencilFuncCCW: {}\n" " stencilFailCCW: {}\n" " stencilZFailCCW: {}\n" " stencilPassCCW: {}\n" " stencilMask: {}\n" " stencilWriteMask: {}\n" " stencilRef: {}\n" " alphaBlendEnable: {}\n" " blendOp: {}\n" " slopeScaledDepthBias: {}\n" " depthBias: {}\n" " srcBlendAlpha: {}\n" " destBlendAlpha: {}\n" " blendOpAlpha: {}\n" " colorWriteEnable: {:X}\n" " primitiveTopology: {}\n" " vertexStrides[0]: {}\n" " vertexStrides[1]: {}\n" " vertexStrides[2]: {}\n" " vertexStrides[3]: {}\n" " renderTargetFormat: {}\n" " depthStencilFormat: {}\n" " sampleCount: {}\n" " enableAlphaToCoverage: {}\n" " enableConditionalSurvey: {}\n" " specConstants: {:X}\n", hash, pipelineState.vertexShader->name, pipelineState.pixelShader != nullptr ? pipelineState.pixelShader->name : "", reinterpret_cast(pipelineState.vertexDeclaration), pipelineState.zEnable, pipelineState.zWriteEnable, pipelineState.stencilEnable, pipelineState.stencilTwoSided, magic_enum::enum_name(pipelineState.srcBlend), magic_enum::enum_name(pipelineState.destBlend), magic_enum::enum_name(pipelineState.cullMode), magic_enum::enum_name(pipelineState.frontFace), magic_enum::enum_name(pipelineState.zFunc), magic_enum::enum_name(pipelineState.stencilFunc), magic_enum::enum_name(pipelineState.stencilFail), magic_enum::enum_name(pipelineState.stencilZFail), magic_enum::enum_name(pipelineState.stencilPass), magic_enum::enum_name(pipelineState.stencilFuncCCW), magic_enum::enum_name(pipelineState.stencilFailCCW), magic_enum::enum_name(pipelineState.stencilZFailCCW), magic_enum::enum_name(pipelineState.stencilPassCCW), pipelineState.stencilMask, pipelineState.stencilWriteMask, pipelineState.stencilRef, pipelineState.alphaBlendEnable, magic_enum::enum_name(pipelineState.blendOp), pipelineState.slopeScaledDepthBias, pipelineState.depthBias, magic_enum::enum_name(pipelineState.srcBlendAlpha), magic_enum::enum_name(pipelineState.destBlendAlpha), magic_enum::enum_name(pipelineState.blendOpAlpha), pipelineState.colorWriteEnable, magic_enum::enum_name(pipelineState.primitiveTopology), pipelineState.vertexStrides[0], pipelineState.vertexStrides[1], pipelineState.vertexStrides[2], pipelineState.vertexStrides[3], magic_enum::enum_name(pipelineState.renderTargetFormat), magic_enum::enum_name(pipelineState.depthStencilFormat), pipelineState.sampleCount, pipelineState.enableAlphaToCoverage, pipelineState.enableConditionalSurvey, pipelineState.specConstants) + g_pipelineDebugText; } #endif #ifdef PSO_CACHING std::lock_guard lock(g_pipelineCacheMutex); g_pipelineStatesToCache.emplace(hash, pipelineState); #endif } return pipeline.get(); } static RenderTextureAddressMode ConvertTextureAddressMode(size_t value) { switch (value) { case D3DTADDRESS_WRAP: return RenderTextureAddressMode::WRAP; case D3DTADDRESS_MIRROR: return RenderTextureAddressMode::MIRROR; case D3DTADDRESS_CLAMP: return RenderTextureAddressMode::CLAMP; case D3DTADDRESS_MIRRORONCE: return RenderTextureAddressMode::MIRROR_ONCE; case D3DTADDRESS_BORDER: return RenderTextureAddressMode::BORDER; default: assert(false && "Unknown texture address mode"); return RenderTextureAddressMode::UNKNOWN; } } static RenderFilter ConvertTextureFilter(uint32_t value) { switch (value) { case D3DTEXF_POINT: case D3DTEXF_NONE: return RenderFilter::NEAREST; case D3DTEXF_LINEAR: return RenderFilter::LINEAR; default: assert(false && "Unknown texture filter"); return RenderFilter::UNKNOWN; } } static RenderBorderColor ConvertBorderColor(uint32_t value) { switch (value) { case 0: return RenderBorderColor::TRANSPARENT_BLACK; case 1: return RenderBorderColor::OPAQUE_WHITE; default: assert(false && "Unknown border color"); return RenderBorderColor::UNKNOWN; } } struct LocalRenderCommandQueue { RenderCommand commands[20]; uint32_t count = 0; RenderCommand& enqueue() { assert(count < std::size(commands)); return commands[count++]; } void submit() { g_renderQueue.enqueue_bulk(commands, count); } }; static void FlushRenderStateForMainThread(GuestDevice* device, LocalRenderCommandQueue& queue) { constexpr size_t BOOL_MASK = 0x2ull; if ((device->dirtyFlags[3].get() & BOOL_MASK) != 0) { auto& cmd = queue.enqueue(); cmd.type = RenderCommandType::SetBooleans; cmd.setBooleans.booleans = (device->vertexShaderBoolConstants[0].get() & 0xFF) | ((device->pixelShaderBoolConstants[0].get() & 0xFF) << 16); device->dirtyFlags[3] = device->dirtyFlags[3].get() & ~BOOL_MASK; } for (uint32_t i = 0; i < 16; i++) { const size_t mask = 0x8000000000000000ull >> (i + 20); if (device->dirtyFlags[2].get() & mask) { auto& cmd = queue.enqueue(); cmd.type = RenderCommandType::SetSamplerState; cmd.setSamplerState.index = i; cmd.setSamplerState.data0 = device->samplerStates[i].data[0]; cmd.setSamplerState.data3 = device->samplerStates[i].data[3]; cmd.setSamplerState.data5 = device->samplerStates[i].data[5]; device->dirtyFlags[2] = device->dirtyFlags[2].get() & ~mask; } } uint64_t dirtyFlags = device->dirtyFlags[0].get(); if (dirtyFlags != 0) { int startRegister = std::countl_zero(dirtyFlags); int endRegister = 64 - std::countr_zero(dirtyFlags); uint32_t index = startRegister * 16; uint32_t size = (endRegister - startRegister) * 64; auto& cmd = queue.enqueue(); cmd.type = RenderCommandType::SetVertexShaderConstants; cmd.setVertexShaderConstants.memory = g_intermediaryUploadAllocator.allocate(&device->vertexShaderFloatConstants[index], size); cmd.setVertexShaderConstants.index = index; cmd.setVertexShaderConstants.size = size; device->dirtyFlags[0] = 0; } dirtyFlags = device->dirtyFlags[1].get(); if (dirtyFlags != 0) { int startRegister = std::countl_zero(dirtyFlags); int endRegister = std::min(56, 64 - std::countr_zero(dirtyFlags)); uint32_t index = startRegister * 16; uint32_t size = (endRegister - startRegister) * 64; auto& cmd = queue.enqueue(); cmd.type = RenderCommandType::SetPixelShaderConstants; cmd.setPixelShaderConstants.memory = g_intermediaryUploadAllocator.allocate(&device->pixelShaderFloatConstants[index], size); cmd.setPixelShaderConstants.index = index; cmd.setPixelShaderConstants.size = size; device->dirtyFlags[1] = 0; } } static void ProcSetBooleans(const RenderCommand& cmd) { SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.booleans, cmd.setBooleans.booleans); } static void ProcSetSamplerState(const RenderCommand& cmd) { const auto& args = cmd.setSamplerState; const auto addressU = ConvertTextureAddressMode((args.data0 >> 10) & 0x7); const auto addressV = ConvertTextureAddressMode((args.data0 >> 13) & 0x7); const auto addressW = ConvertTextureAddressMode((args.data0 >> 16) & 0x7); auto magFilter = ConvertTextureFilter((args.data3 >> 19) & 0x3); auto minFilter = ConvertTextureFilter((args.data3 >> 21) & 0x3); auto mipFilter = ConvertTextureFilter((args.data3 >> 23) & 0x3); const auto borderColor = ConvertBorderColor(args.data5 & 0x3); bool anisotropyEnabled = Config::AnisotropicFiltering > 0 && mipFilter == RenderFilter::LINEAR; if (anisotropyEnabled) { magFilter = RenderFilter::LINEAR; minFilter = RenderFilter::LINEAR; } auto& samplerDesc = g_samplerDescs[args.index]; bool dirty = false; SetDirtyValue(dirty, samplerDesc.addressU, addressU); SetDirtyValue(dirty, samplerDesc.addressV, addressV); SetDirtyValue(dirty, samplerDesc.addressW, addressW); SetDirtyValue(dirty, samplerDesc.minFilter, minFilter); SetDirtyValue(dirty, samplerDesc.magFilter, magFilter); SetDirtyValue(dirty, samplerDesc.mipmapMode, RenderMipmapMode(mipFilter)); SetDirtyValue(dirty, samplerDesc.maxAnisotropy, anisotropyEnabled ? Config::AnisotropicFiltering : 16u); SetDirtyValue(dirty, samplerDesc.anisotropyEnabled, anisotropyEnabled); SetDirtyValue(dirty, samplerDesc.borderColor, borderColor); if (dirty) { auto& [descriptorIndex, sampler] = g_samplerStates[XXH3_64bits(&samplerDesc, sizeof(RenderSamplerDesc))]; if (descriptorIndex == NULL) { descriptorIndex = g_samplerStates.size(); sampler = g_device->createSampler(samplerDesc); g_samplerDescriptorSet->setSampler(descriptorIndex - 1, sampler.get()); } SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.samplerIndices[args.index], descriptorIndex - 1); } } static void ProcSetVertexShaderConstants(const RenderCommand& cmd) { auto& args = cmd.setVertexShaderConstants; assert((args.index * sizeof(uint32_t) + args.size) <= sizeof(g_vertexShaderConstants)); memcpy(&g_vertexShaderConstants[args.index], args.memory, args.size); g_dirtyStates.vertexShaderConstants = true; } static void ProcSetPixelShaderConstants(const RenderCommand& cmd) { auto& args = cmd.setPixelShaderConstants; assert((args.index * sizeof(uint32_t) + args.size) <= sizeof(g_pixelShaderConstants)); memcpy(&g_pixelShaderConstants[args.index], args.memory, args.size); g_dirtyStates.pixelShaderConstants = true; } static void ProcAddPipeline(const RenderCommand& cmd) { auto& args = cmd.addPipeline; auto& pipeline = g_pipelines[args.hash]; if (pipeline == nullptr) { pipeline = std::unique_ptr(args.pipeline); #ifdef ASYNC_PSO_DEBUG ++g_pipelinesCreatedAsynchronously; #endif } else { #ifdef ASYNC_PSO_DEBUG ++g_pipelinesDropped; #endif delete args.pipeline; } } static constexpr int32_t COMMON_DEPTH_BIAS_VALUE = int32_t((1 << 24) * 0.002f); static constexpr float COMMON_SLOPE_SCALED_DEPTH_BIAS_VALUE = 1.0f; static void FlushRenderStateForRenderThread() { auto renderTarget = g_pipelineState.colorWriteEnable ? g_renderTarget : nullptr; auto depthStencil = g_pipelineState.zEnable || g_pipelineState.stencilEnable ? g_depthStencil : nullptr; bool foundAny = PopulateBarriersForStretchRect(renderTarget, depthStencil); for (const auto surface : g_pendingResolves) { bool isDepthStencil = RenderFormatIsDepth(surface->format); foundAny |= PopulateBarriersForStretchRect(isDepthStencil ? nullptr : surface, isDepthStencil ? surface : nullptr); } if (foundAny) { FlushBarriers(); ExecutePendingStretchRectCommands(renderTarget, depthStencil); for (const auto surface : g_pendingResolves) { bool isDepthStencil = RenderFormatIsDepth(surface->format); ExecutePendingStretchRectCommands(isDepthStencil ? nullptr : surface, isDepthStencil ? surface : nullptr); } } if (!g_pendingResolves.empty()) g_pendingResolves.clear(); AddBarrier(renderTarget, RenderTextureLayout::COLOR_WRITE); AddBarrier(depthStencil, RenderTextureLayout::DEPTH_WRITE); FlushBarriers(); SetFramebuffer(renderTarget, depthStencil, false); FlushViewport(); auto& commandList = g_commandLists[g_frame]; // D3D12 resets depth bias values to the pipeline values, even if they are dynamic. // We can reduce unnecessary calls by making common depth bias values part of the pipeline. if (g_capabilities.dynamicDepthBias && g_backend == Backend::D3D12) { bool useDepthBias = (g_depthBias != 0) || (g_slopeScaledDepthBias != 0.0f); int32_t depthBias = useDepthBias ? COMMON_DEPTH_BIAS_VALUE : 0; float slopeScaledDepthBias = useDepthBias ? COMMON_SLOPE_SCALED_DEPTH_BIAS_VALUE : 0.0f; SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.depthBias, depthBias); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.slopeScaledDepthBias, slopeScaledDepthBias); } if (g_dirtyStates.pipelineState) { commandList->setPipeline(CreateGraphicsPipelineInRenderThread(g_pipelineState)); // D3D12 resets the depth bias values. Check if they need to be set again. if (g_capabilities.dynamicDepthBias && g_backend == Backend::D3D12) g_dirtyStates.depthBias = (g_depthBias != g_pipelineState.depthBias) || (g_slopeScaledDepthBias != g_pipelineState.slopeScaledDepthBias); } if (g_dirtyStates.depthBias && g_capabilities.dynamicDepthBias) commandList->setDepthBias(g_depthBias, 0.0f, g_slopeScaledDepthBias); if (g_dirtyStates.vertexShaderConstants) { auto vertexShaderConstants = g_uploadAllocators[g_frame].allocate(g_vertexShaderConstants, sizeof(g_vertexShaderConstants), 0x100); SetRootDescriptor(vertexShaderConstants, 0); } if (g_dirtyStates.pixelShaderConstants) { auto pixelShaderConstants = g_uploadAllocators[g_frame].allocate(g_pixelShaderConstants, sizeof(g_pixelShaderConstants), 0x100); SetRootDescriptor(pixelShaderConstants, 1); } if (g_dirtyStates.sharedConstants) { auto sharedConstants = g_uploadAllocators[g_frame].allocate(&g_sharedConstants, sizeof(g_sharedConstants), 0x100); SetRootDescriptor(sharedConstants, 2); } if (g_dirtyStates.vertexStreamFirst <= g_dirtyStates.vertexStreamLast) { commandList->setVertexBuffers( g_dirtyStates.vertexStreamFirst, g_vertexBufferViews + g_dirtyStates.vertexStreamFirst, g_dirtyStates.vertexStreamLast - g_dirtyStates.vertexStreamFirst + 1, g_inputSlots + g_dirtyStates.vertexStreamFirst); } if (g_dirtyStates.indices && (g_backend == Backend::D3D12 || g_indexBufferView.buffer.ref != nullptr)) commandList->setIndexBuffer(&g_indexBufferView); g_dirtyStates = DirtyStates(false); } static RenderPrimitiveTopology ConvertPrimitiveType(uint32_t primitiveType) { switch (primitiveType) { case D3DPT_POINTLIST: return RenderPrimitiveTopology::POINT_LIST; case D3DPT_LINELIST: return RenderPrimitiveTopology::LINE_LIST; case D3DPT_LINESTRIP: return RenderPrimitiveTopology::LINE_STRIP; case D3DPT_TRIANGLELIST: case D3DPT_QUADLIST: return RenderPrimitiveTopology::TRIANGLE_LIST; case D3DPT_TRIANGLESTRIP: return RenderPrimitiveTopology::TRIANGLE_STRIP; case D3DPT_TRIANGLEFAN: return g_capabilities.triangleFan ? RenderPrimitiveTopology::TRIANGLE_FAN : RenderPrimitiveTopology::TRIANGLE_LIST; default: assert(false && "Unknown primitive type"); return RenderPrimitiveTopology::UNKNOWN; } } static void SetPrimitiveType(uint32_t primitiveType) { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.primitiveTopology, ConvertPrimitiveType(primitiveType)); } static void DrawPrimitive(GuestDevice* device, uint32_t primitiveType, uint32_t startVertex, uint32_t primitiveCount) { LocalRenderCommandQueue queue; FlushRenderStateForMainThread(device, queue); auto& cmd = queue.enqueue(); cmd.type = RenderCommandType::DrawPrimitive; cmd.drawPrimitive.primitiveType = primitiveType; cmd.drawPrimitive.startVertex = startVertex; cmd.drawPrimitive.primitiveCount = primitiveCount; queue.submit(); } static void ProcDrawPrimitive(const RenderCommand& cmd) { const auto& args = cmd.drawPrimitive; SetPrimitiveType(args.primitiveType); FlushRenderStateForRenderThread(); auto& commandList = g_commandLists[g_frame]; commandList->drawInstanced(args.primitiveCount, 1, args.startVertex, 0); } static void DrawIndexedPrimitive(GuestDevice* device, uint32_t primitiveType, int32_t baseVertexIndex, uint32_t startIndex, uint32_t primCount) { LocalRenderCommandQueue queue; FlushRenderStateForMainThread(device, queue); auto& cmd = queue.enqueue(); cmd.type = RenderCommandType::DrawIndexedPrimitive; cmd.drawIndexedPrimitive.primitiveType = primitiveType; cmd.drawIndexedPrimitive.baseVertexIndex = baseVertexIndex; cmd.drawIndexedPrimitive.startIndex = startIndex; cmd.drawIndexedPrimitive.primCount = primCount; queue.submit(); } static void ProcDrawIndexedPrimitive(const RenderCommand& cmd) { const auto& args = cmd.drawIndexedPrimitive; SetPrimitiveType(args.primitiveType); FlushRenderStateForRenderThread(); g_commandLists[g_frame]->drawIndexedInstanced(args.primCount, 1, args.startIndex, args.baseVertexIndex, 0); } static void DrawPrimitiveUP(GuestDevice* device, uint32_t primitiveType, uint32_t primitiveCount, void* vertexStreamZeroData, uint32_t vertexStreamZeroStride) { LocalRenderCommandQueue queue; FlushRenderStateForMainThread(device, queue); auto& cmd = queue.enqueue(); cmd.type = RenderCommandType::DrawPrimitiveUP; cmd.drawPrimitiveUP.primitiveType = primitiveType; cmd.drawPrimitiveUP.primitiveCount = primitiveCount; cmd.drawPrimitiveUP.vertexStreamZeroData = g_intermediaryUploadAllocator.allocate(vertexStreamZeroData, primitiveCount * vertexStreamZeroStride); cmd.drawPrimitiveUP.vertexStreamZeroSize = primitiveCount * vertexStreamZeroStride; cmd.drawPrimitiveUP.vertexStreamZeroStride = vertexStreamZeroStride; cmd.drawPrimitiveUP.csdFilterState = g_csdFilterState; queue.submit(); } static void ProcDrawPrimitiveUP(const RenderCommand& cmd) { const auto& args = cmd.drawPrimitiveUP; SetPrimitiveType(args.primitiveType); SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.vertexStrides[0], uint8_t(args.vertexStreamZeroStride)); auto allocation = g_uploadAllocators[g_frame].allocate(reinterpret_cast(args.vertexStreamZeroData), args.vertexStreamZeroSize, 0x4); auto& vertexBufferView = g_vertexBufferViews[0]; vertexBufferView.size = args.primitiveCount * args.vertexStreamZeroStride; vertexBufferView.buffer = allocation.buffer->at(allocation.offset); g_inputSlots[0].stride = args.vertexStreamZeroStride; g_dirtyStates.vertexStreamFirst = 0; uint32_t indexCount = 0; if (args.primitiveType == D3DPT_QUADLIST) indexCount = g_quadIndexData.prepare(args.primitiveCount); else if (!g_capabilities.triangleFan && args.primitiveType == D3DPT_TRIANGLEFAN) indexCount = g_triangleFanIndexData.prepare(args.primitiveCount); if (args.csdFilterState != CsdFilterState::Unknown && (g_pipelineState.pixelShader == g_csdShader || g_pipelineState.pixelShader == g_csdFilterShader.get())) { SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.pixelShader, args.csdFilterState == CsdFilterState::On ? g_csdFilterShader.get() : g_csdShader); } FlushRenderStateForRenderThread(); if (indexCount != 0) g_commandLists[g_frame]->drawIndexedInstanced(indexCount, 1, 0, 0, 0); else g_commandLists[g_frame]->drawInstanced(args.primitiveCount, 1, 0, 0); } static const char* ConvertDeclUsage(uint32_t usage) { switch (usage) { case D3DDECLUSAGE_POSITION: return "POSITION"; case D3DDECLUSAGE_BLENDWEIGHT: return "BLENDWEIGHT"; case D3DDECLUSAGE_BLENDINDICES: return "BLENDINDICES"; case D3DDECLUSAGE_NORMAL: return "NORMAL"; case D3DDECLUSAGE_PSIZE: return "PSIZE"; case D3DDECLUSAGE_TEXCOORD: return "TEXCOORD"; case D3DDECLUSAGE_TANGENT: return "TANGENT"; case D3DDECLUSAGE_BINORMAL: return "BINORMAL"; case D3DDECLUSAGE_TESSFACTOR: return "TESSFACTOR"; case D3DDECLUSAGE_POSITIONT: return "POSITIONT"; case D3DDECLUSAGE_COLOR: return "COLOR"; case D3DDECLUSAGE_FOG: return "FOG"; case D3DDECLUSAGE_DEPTH: return "DEPTH"; case D3DDECLUSAGE_SAMPLE: return "SAMPLE"; default: assert(false && "Unknown usage"); return "UNKNOWN"; } } static RenderFormat ConvertDeclType(uint32_t type) { switch (type) { case D3DDECLTYPE_FLOAT1: return RenderFormat::R32_FLOAT; case D3DDECLTYPE_FLOAT2: return RenderFormat::R32G32_FLOAT; case D3DDECLTYPE_FLOAT3: return RenderFormat::R32G32B32_FLOAT; case D3DDECLTYPE_FLOAT4: return RenderFormat::R32G32B32A32_FLOAT; case D3DDECLTYPE_D3DCOLOR: return RenderFormat::B8G8R8A8_UNORM; case D3DDECLTYPE_UBYTE4: case D3DDECLTYPE_UBYTE4_2: return RenderFormat::R8G8B8A8_UINT; case D3DDECLTYPE_SHORT2: return RenderFormat::R16G16_SINT; case D3DDECLTYPE_SHORT4: return RenderFormat::R16G16B16A16_SINT; case D3DDECLTYPE_UBYTE4N: case D3DDECLTYPE_UBYTE4N_2: return RenderFormat::R8G8B8A8_UNORM; case D3DDECLTYPE_SHORT2N: return RenderFormat::R16G16_SNORM; case D3DDECLTYPE_SHORT4N: return RenderFormat::R16G16B16A16_SNORM; case D3DDECLTYPE_USHORT2N: return RenderFormat::R16G16_UNORM; case D3DDECLTYPE_USHORT4N: return RenderFormat::R16G16B16A16_UNORM; case D3DDECLTYPE_UINT1: return RenderFormat::R32_UINT; case D3DDECLTYPE_DEC3N_2: case D3DDECLTYPE_DEC3N_3: return RenderFormat::R32_UINT; case D3DDECLTYPE_FLOAT16_2: return RenderFormat::R16G16_FLOAT; case D3DDECLTYPE_FLOAT16_4: return RenderFormat::R16G16B16A16_FLOAT; default: assert(false && "Unknown type"); return RenderFormat::UNKNOWN; } } static GuestVertexDeclaration* CreateVertexDeclarationWithoutAddRef(GuestVertexElement* vertexElements) { size_t vertexElementCount = 0; auto vertexElement = vertexElements; while (vertexElement->stream != 0xFF && vertexElement->type != D3DDECLTYPE_UNUSED) { vertexElement->padding = 0; ++vertexElement; ++vertexElementCount; } vertexElement->padding = 0; // Clear the padding in D3DDECL_END() std::lock_guard lock(g_vertexDeclarationMutex); XXH64_hash_t hash = XXH3_64bits(vertexElements, vertexElementCount * sizeof(GuestVertexElement)); auto& vertexDeclaration = g_vertexDeclarations[hash]; if (vertexDeclaration == nullptr) { vertexDeclaration = g_userHeap.AllocPhysical(ResourceType::VertexDeclaration); vertexDeclaration->hash = hash; static std::vector inputElements; inputElements.clear(); struct Location { uint32_t usage; uint32_t usageIndex; uint32_t location; }; // Should match the locations defined in XenosRecomp. constexpr Location locations[] = { { D3DDECLUSAGE_POSITION, 0, 0 }, { D3DDECLUSAGE_POSITION, 1, 1 }, { D3DDECLUSAGE_POSITION, 2, 2 }, { D3DDECLUSAGE_POSITION, 3, 3 }, { D3DDECLUSAGE_NORMAL, 0, 4 }, { D3DDECLUSAGE_NORMAL, 1, 5 }, { D3DDECLUSAGE_NORMAL, 2, 6 }, { D3DDECLUSAGE_NORMAL, 3, 7 }, { D3DDECLUSAGE_TANGENT, 0, 8 }, { D3DDECLUSAGE_TANGENT, 1, 9 }, { D3DDECLUSAGE_TANGENT, 2, 10 }, { D3DDECLUSAGE_TANGENT, 3, 11 }, { D3DDECLUSAGE_BINORMAL, 0, 12 }, { D3DDECLUSAGE_TEXCOORD, 0, 13 }, { D3DDECLUSAGE_TEXCOORD, 1, 14 }, { D3DDECLUSAGE_TEXCOORD, 2, 15 }, { D3DDECLUSAGE_TEXCOORD, 3, 16 }, { D3DDECLUSAGE_COLOR, 0, 17 }, { D3DDECLUSAGE_BLENDINDICES, 0, 18 }, { D3DDECLUSAGE_BLENDWEIGHT, 0, 19 }, }; vertexElement = vertexElements; while (vertexElement->stream != 0xFF && vertexElement->type != D3DDECLTYPE_UNUSED) { uint32_t resolvedLocation = ~0; for (auto& location : locations) { if (location.usage == vertexElement->usage && location.usageIndex == vertexElement->usageIndex) { resolvedLocation = location.location; break; } } if (resolvedLocation == ~0) { // Bound but not used by any guest shaders. ++vertexElement; continue; } auto& inputElement = inputElements.emplace_back(); inputElement.semanticName = ConvertDeclUsage(vertexElement->usage); inputElement.semanticIndex = vertexElement->usageIndex; inputElement.location = resolvedLocation; inputElement.format = ConvertDeclType(vertexElement->type); inputElement.slotIndex = vertexElement->stream; inputElement.alignedByteOffset = vertexElement->offset; switch (vertexElement->usage) { case D3DDECLUSAGE_NORMAL: switch (vertexElement->type) { case D3DDECLTYPE_SHORT2: case D3DDECLTYPE_SHORT4: case D3DDECLTYPE_SHORT2N: case D3DDECLTYPE_SHORT4N: case D3DDECLTYPE_USHORT2N: case D3DDECLTYPE_USHORT4N: case D3DDECLTYPE_FLOAT16_2: case D3DDECLTYPE_FLOAT16_4: vertexDeclaration->swappedNormals |= 1 << vertexElement->usageIndex; break; } break; case D3DDECLUSAGE_BINORMAL: switch (vertexElement->type) { case D3DDECLTYPE_SHORT2: case D3DDECLTYPE_SHORT4: case D3DDECLTYPE_SHORT2N: case D3DDECLTYPE_SHORT4N: case D3DDECLTYPE_USHORT2N: case D3DDECLTYPE_USHORT4N: case D3DDECLTYPE_FLOAT16_2: case D3DDECLTYPE_FLOAT16_4: vertexDeclaration->swappedBinormals |= 1 << vertexElement->usageIndex; break; } break; case D3DDECLUSAGE_TANGENT: switch (vertexElement->type) { case D3DDECLTYPE_SHORT2: case D3DDECLTYPE_SHORT4: case D3DDECLTYPE_SHORT2N: case D3DDECLTYPE_SHORT4N: case D3DDECLTYPE_USHORT2N: case D3DDECLTYPE_USHORT4N: case D3DDECLTYPE_FLOAT16_2: case D3DDECLTYPE_FLOAT16_4: vertexDeclaration->swappedTangents |= 1 << vertexElement->usageIndex; break; } break; case D3DDECLUSAGE_BLENDWEIGHT: switch (vertexElement->type) { case D3DDECLTYPE_SHORT2: case D3DDECLTYPE_SHORT4: case D3DDECLTYPE_SHORT2N: case D3DDECLTYPE_SHORT4N: case D3DDECLTYPE_USHORT2N: case D3DDECLTYPE_USHORT4N: case D3DDECLTYPE_FLOAT16_2: case D3DDECLTYPE_FLOAT16_4: vertexDeclaration->swappedBlendWeights |= 1 << vertexElement->usageIndex; break; } break; case D3DDECLUSAGE_TEXCOORD: switch (vertexElement->type) { case D3DDECLTYPE_SHORT2: case D3DDECLTYPE_SHORT4: case D3DDECLTYPE_SHORT2N: case D3DDECLTYPE_SHORT4N: case D3DDECLTYPE_USHORT2N: case D3DDECLTYPE_USHORT4N: case D3DDECLTYPE_FLOAT16_2: case D3DDECLTYPE_FLOAT16_4: vertexDeclaration->swappedTexcoords |= 1 << vertexElement->usageIndex; break; } break; } vertexDeclaration->vertexStreams[vertexElement->stream] = true; ++vertexElement; } auto addInputElement = [&](uint32_t usage, uint32_t usageIndex) { uint32_t location = ~0; for (auto& alsoLocation : locations) { if (alsoLocation.usage == usage && alsoLocation.usageIndex == usageIndex) { location = alsoLocation.location; break; } } assert(location != ~0); for (auto& inputElement : inputElements) { if (inputElement.location == location) return; } auto format = RenderFormat::R32_FLOAT; switch (usage) { case D3DDECLUSAGE_NORMAL: case D3DDECLUSAGE_TANGENT: case D3DDECLUSAGE_BINORMAL: case D3DDECLUSAGE_BLENDINDICES: format = RenderFormat::R32G32B32_FLOAT; break; } inputElements.emplace_back(ConvertDeclUsage(usage), usageIndex, location, format, 15, 0); }; // Assign any unbound usages to null buffer slot. for (auto& location : locations) { addInputElement(location.usage, location.usageIndex); } vertexDeclaration->inputElements = std::make_unique(inputElements.size()); std::copy(inputElements.begin(), inputElements.end(), vertexDeclaration->inputElements.get()); vertexDeclaration->vertexElements = std::make_unique(vertexElementCount + 1); std::copy(vertexElements, vertexElements + vertexElementCount + 1, vertexDeclaration->vertexElements.get()); vertexDeclaration->inputElementCount = uint32_t(inputElements.size()); vertexDeclaration->vertexElementCount = vertexElementCount + 1; } vertexDeclaration->AddRef(); return vertexDeclaration; } static GuestVertexDeclaration* CreateVertexDeclaration(GuestVertexElement* vertexElements) { auto vertexDeclaration = CreateVertexDeclarationWithoutAddRef(vertexElements); vertexDeclaration->AddRef(); return vertexDeclaration; } static void SetVertexDeclaration(GuestDevice* device, GuestVertexDeclaration* vertexDeclaration) { RenderCommand cmd; cmd.type = RenderCommandType::SetVertexDeclaration; cmd.setVertexDeclaration.vertexDeclaration = vertexDeclaration; g_renderQueue.enqueue(cmd); device->vertexDeclaration = g_memory.MapVirtual(vertexDeclaration); } static void ProcSetVertexDeclaration(const RenderCommand& cmd) { auto& args = cmd.setVertexDeclaration; if (args.vertexDeclaration != nullptr) { SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.swappedTexcoords, args.vertexDeclaration->swappedTexcoords); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.swappedNormals, args.vertexDeclaration->swappedNormals); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.swappedBinormals, args.vertexDeclaration->swappedBinormals); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.swappedTangents, args.vertexDeclaration->swappedTangents); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.swappedBlendWeights, args.vertexDeclaration->swappedBlendWeights); uint32_t specConstants = g_pipelineState.specConstants; if (args.vertexDeclaration->hasR11G11B10Normal) specConstants |= SPEC_CONSTANT_R11G11B10_NORMAL; else specConstants &= ~SPEC_CONSTANT_R11G11B10_NORMAL; SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.specConstants, specConstants); } SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.vertexDeclaration, args.vertexDeclaration); } static ShaderCacheEntry* FindShaderCacheEntry(XXH64_hash_t hash) { auto end = g_shaderCacheEntries + g_shaderCacheEntryCount; auto findResult = std::lower_bound(g_shaderCacheEntries, end, hash, [](ShaderCacheEntry& lhs, XXH64_hash_t rhs) { return lhs.hash < rhs; }); return findResult != end && findResult->hash == hash ? findResult : nullptr; } static GuestShader* CreateShader(const be* function, ResourceType resourceType) { XXH64_hash_t hash = XXH3_64bits(function, function[1] + function[2]); auto findResult = FindShaderCacheEntry(hash); GuestShader* shader = nullptr; if (findResult == nullptr) { LOGF_WARNING("Shader of function {:x} is not found by value: {:x}", reinterpret_cast(function), hash); LOG_WARNING("Perhaps the path to the required shader will be printed before this error"); __builtin_trap(); } if (findResult != nullptr) { if (findResult->guestShader == nullptr) { shader = g_userHeap.AllocPhysical(resourceType); if (hash == 0x85ED723035ECF535) shader->shader = CREATE_SHADER(blend_color_alpha_ps); else if (hash == 0xB1086A4947A797DE) shader->shader = CREATE_SHADER(csd_no_tex_vs); else if (hash == 0xB4CAFC034A37C8A8) shader->shader = CREATE_SHADER(csd_vs); else shader->shaderCacheEntry = findResult; findResult->guestShader = shader; } else { shader = findResult->guestShader; } } if (shader == nullptr) shader = g_userHeap.AllocPhysical(resourceType); else shader->AddRef(); if (hash == 0x31173204A896098A) g_csdShader = shader; return shader; } static GuestShader* CreateVertexShader(const be* function) { return CreateShader(function, ResourceType::VertexShader); } static void SetVertexShader(GuestDevice* device, GuestShader* shader) { RenderCommand cmd; cmd.type = RenderCommandType::SetVertexShader; cmd.setVertexShader.shader = shader; g_renderQueue.enqueue(cmd); } static void ProcSetVertexShader(const RenderCommand& cmd) { GuestShader* shader = cmd.setVertexShader.shader; if (shader != nullptr && shader->shaderCacheEntry != nullptr) { if (shader->shaderCacheEntry->hash == 0x3687D038CE7D0BEA || shader->shaderCacheEntry->hash == 0xB4DA7A442DBB16CC) { if (Config::RadialBlur == ERadialBlur::Enhanced) shader = g_enhancedBurnoutBlurVSShader.get(); } } SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.vertexShader, shader); } static void SetStreamSource(GuestDevice* device, uint32_t index, GuestBuffer* buffer, uint32_t offset, uint32_t stride) { RenderCommand cmd; cmd.type = RenderCommandType::SetStreamSource; cmd.setStreamSource.index = index; cmd.setStreamSource.buffer = buffer; cmd.setStreamSource.offset = offset; cmd.setStreamSource.stride = stride; g_renderQueue.enqueue(cmd); } static void ProcSetStreamSource(const RenderCommand& cmd) { const auto& args = cmd.setStreamSource; SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.vertexStrides[args.index], uint8_t(args.buffer != nullptr ? args.stride : 0)); bool dirty = false; SetDirtyValue(dirty, g_vertexBufferViews[args.index].buffer, args.buffer != nullptr ? args.buffer->buffer->at(args.offset) : RenderBufferReference{}); SetDirtyValue(dirty, g_vertexBufferViews[args.index].size, args.buffer != nullptr ? (args.buffer->dataSize - args.offset) : 0u); SetDirtyValue(dirty, g_inputSlots[args.index].stride, args.buffer != nullptr ? args.stride : 0u); if (dirty) { g_dirtyStates.vertexStreamFirst = std::min(g_dirtyStates.vertexStreamFirst, args.index); g_dirtyStates.vertexStreamLast = std::max(g_dirtyStates.vertexStreamLast, args.index); } } static void SetIndices(GuestDevice* device, GuestBuffer* buffer) { RenderCommand cmd; cmd.type = RenderCommandType::SetIndices; cmd.setIndices.buffer = buffer; g_renderQueue.enqueue(cmd); } static void ProcSetIndices(const RenderCommand& cmd) { const auto& args = cmd.setIndices; SetDirtyValue(g_dirtyStates.indices, g_indexBufferView.buffer, args.buffer != nullptr ? args.buffer->buffer->at(0) : RenderBufferReference{}); SetDirtyValue(g_dirtyStates.indices, g_indexBufferView.format, args.buffer != nullptr ? args.buffer->format : RenderFormat::R16_UINT); SetDirtyValue(g_dirtyStates.indices, g_indexBufferView.size, args.buffer != nullptr ? args.buffer->dataSize : 0u); } static GuestShader* CreatePixelShader(const be* function) { return CreateShader(function, ResourceType::PixelShader); } static void SetPixelShader(GuestDevice* device, GuestShader* shader) { RenderCommand cmd; cmd.type = RenderCommandType::SetPixelShader; cmd.setPixelShader.shader = shader; g_renderQueue.enqueue(cmd); } static void ProcSetPixelShader(const RenderCommand& cmd) { GuestShader* shader = cmd.setPixelShader.shader; if (shader != nullptr && shader->shaderCacheEntry != nullptr) { if (shader->shaderCacheEntry->hash == 0xDA58F0110A8595D9 || shader->shaderCacheEntry->hash == 0x845A4EF989446C01) { if (Config::RadialBlur == ERadialBlur::Enhanced) shader = g_enhancedBurnoutBlurPSShader.get(); } } SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.pixelShader, shader); } static void BeginConditionalSurvey(GuestDevice* device, uint32_t index) { assert(index < CONDITIONAL_SURVEY_MAX && "Invalid conditional survey index."); RenderCommand cmd; cmd.type = RenderCommandType::SetConditionalSurvey; cmd.setConditionalSurvey.enabled = true; cmd.setConditionalSurvey.index = index; g_renderQueue.enqueue(cmd); } static void EndConditionalSurvey(GuestDevice* device) { RenderCommand cmd; cmd.type = RenderCommandType::SetConditionalSurvey; cmd.setConditionalSurvey.enabled = false; cmd.setConditionalSurvey.index = 0; g_renderQueue.enqueue(cmd); } static void ProcSetConditionalSurvey(const RenderCommand& cmd) { if (cmd.setConditionalSurvey.enabled) { // Clear previous survey result first. auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(sizeof(uint32_t))); memset(uploadBuffer->map(), 0, sizeof(uint32_t)); uploadBuffer->unmap(); auto& commandList = g_commandLists[g_frame]; commandList->barriers(RenderBarrierStage::COPY, RenderBufferBarrier(g_conditionalSurveyBuffer.get(), RenderBufferAccess::WRITE)); commandList->copyBufferRegion(g_conditionalSurveyBuffer->at(cmd.setConditionalSurvey.index * sizeof(uint32_t)), uploadBuffer->at(0), sizeof(uint32_t)); commandList->barriers(RenderBarrierStage::GRAPHICS, RenderBufferBarrier(g_conditionalSurveyBuffer.get(), RenderBufferAccess::READ | RenderBufferAccess::WRITE)); g_tempBuffers[g_frame].emplace_back(std::move(uploadBuffer)); } SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.enableConditionalSurvey, cmd.setConditionalSurvey.enabled); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.conditionalSurveyIndex, cmd.setConditionalSurvey.index); } static void BeginConditionalRendering(GuestDevice* device, uint32_t index) { assert(index < CONDITIONAL_SURVEY_MAX && "Invalid conditional rendering index."); RenderCommand cmd; cmd.type = RenderCommandType::SetConditionalRendering; cmd.setConditionalRendering.enabled = true; cmd.setConditionalRendering.index = index; g_renderQueue.enqueue(cmd); } static void EndConditionalRendering(GuestDevice* device) { RenderCommand cmd; cmd.type = RenderCommandType::SetConditionalRendering; cmd.setConditionalRendering.enabled = false; cmd.setConditionalRendering.index = 0; g_renderQueue.enqueue(cmd); } static void ProcSetConditionalRendering(const RenderCommand& cmd) { uint32_t specConstants = g_pipelineState.specConstants; if (cmd.setConditionalRendering.enabled) specConstants |= SPEC_CONSTANT_CONDITIONAL_RENDERING; else specConstants &= ~SPEC_CONSTANT_CONDITIONAL_RENDERING; SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.specConstants, specConstants); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.conditionalRenderingIndex, cmd.setConditionalRendering.index); } static void SetClipPlane(GuestDevice* device, uint32_t index, const be* plane) { if (index != 0) return; SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.clipPlane[0], plane[0].get()); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.clipPlane[1], plane[1].get()); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.clipPlane[2], plane[2].get()); SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.clipPlane[3], plane[3].get()); } static std::thread g_renderThread([] { #ifdef _WIN32 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); GuestThread::SetThreadName(GetCurrentThreadId(), "Render Thread"); #endif RenderCommand commands[32]; while (true) { size_t count = g_renderQueue.wait_dequeue_bulk(commands, std::size(commands)); for (size_t i = 0; i < count; i++) { auto& cmd = commands[i]; switch (cmd.type) { case RenderCommandType::SetRenderState: ProcSetRenderState(cmd); break; case RenderCommandType::DestructResource: ProcDestructResource(cmd); break; case RenderCommandType::UnlockTextureRect: ProcUnlockTextureRect(cmd); break; case RenderCommandType::UnlockBuffer16: ProcUnlockBuffer16(cmd); break; case RenderCommandType::UnlockBuffer32: ProcUnlockBuffer32(cmd); break; case RenderCommandType::DrawImGui: ProcDrawImGui(cmd); break; case RenderCommandType::ExecuteCommandList: ProcExecuteCommandList(cmd); break; case RenderCommandType::BeginCommandList: ProcBeginCommandList(cmd); break; case RenderCommandType::StretchRect: ProcStretchRect(cmd); break; case RenderCommandType::SetRenderTarget: ProcSetRenderTarget(cmd); break; case RenderCommandType::SetDepthStencilSurface: ProcSetDepthStencilSurface(cmd); break; case RenderCommandType::ExecutePendingStretchRectCommands: ProcExecutePendingStretchRectCommands(cmd); break; case RenderCommandType::Clear: ProcClear(cmd); break; case RenderCommandType::SetViewport: ProcSetViewport(cmd); break; case RenderCommandType::SetTexture: ProcSetTexture(cmd); break; case RenderCommandType::SetScissorRect: ProcSetScissorRect(cmd); break; case RenderCommandType::SetSamplerState: ProcSetSamplerState(cmd); break; case RenderCommandType::SetBooleans: ProcSetBooleans(cmd); break; case RenderCommandType::SetVertexShaderConstants: ProcSetVertexShaderConstants(cmd); break; case RenderCommandType::SetPixelShaderConstants: ProcSetPixelShaderConstants(cmd); break; case RenderCommandType::AddPipeline: ProcAddPipeline(cmd); break; case RenderCommandType::DrawPrimitive: ProcDrawPrimitive(cmd); break; case RenderCommandType::DrawIndexedPrimitive: ProcDrawIndexedPrimitive(cmd); break; case RenderCommandType::DrawPrimitiveUP: ProcDrawPrimitiveUP(cmd); break; case RenderCommandType::SetVertexDeclaration: ProcSetVertexDeclaration(cmd); break; case RenderCommandType::SetVertexShader: ProcSetVertexShader(cmd); break; case RenderCommandType::SetStreamSource: ProcSetStreamSource(cmd); break; case RenderCommandType::SetIndices: ProcSetIndices(cmd); break; case RenderCommandType::SetPixelShader: ProcSetPixelShader(cmd); break; case RenderCommandType::SetConditionalSurvey: ProcSetConditionalSurvey(cmd); break; case RenderCommandType::SetConditionalRendering: ProcSetConditionalRendering(cmd); break; default: assert(false && "Unrecognized render command type."); break; } } } }); struct GuestPictureData { be vtable; uint8_t flags; be name; be texture; be type; }; union Bxty { char _Buf[16]; xpointer _Ptr; }; struct BEString { char alval[1]; Bxty bx; be size; uint32_t res; const char* c_str() { uint32_t len = size.get(); if (len == 0) { return alval; } else if (len < 16) { return bx._Buf; } else { return reinterpret_cast(bx._Ptr.get()); } } }; struct GuestMyTexture { be vtable; // 0x0 be field0x4; // 0x4 be regIndex; // 0x8 BEString str1; // 0xC BEString str2; // 0x28 BEString str3; // 0x44 be byte60; // 0x60 be texture; // 0x64 be Surface[6]; // 0x64 be width; // 0x80 be height; // 0x84 be graphicsDevice; // 0x88 be guestDevice; // 0x8C }; static RenderTextureDimension ConvertTextureDimension(ddspp::TextureType type) { switch (type) { case ddspp::Texture1D: return RenderTextureDimension::TEXTURE_1D; case ddspp::Texture2D: case ddspp::Cubemap: return RenderTextureDimension::TEXTURE_2D; case ddspp::Texture3D: return RenderTextureDimension::TEXTURE_3D; default: assert(false && "Unknown texture type from DDS."); return RenderTextureDimension::UNKNOWN; } } static RenderTextureViewDimension ConvertTextureViewDimension(ddspp::TextureType type) { switch (type) { case ddspp::Texture1D: return RenderTextureViewDimension::TEXTURE_1D; case ddspp::Texture2D: return RenderTextureViewDimension::TEXTURE_2D; case ddspp::Texture3D: return RenderTextureViewDimension::TEXTURE_3D; case ddspp::Cubemap: return RenderTextureViewDimension::TEXTURE_CUBE; default: assert(false && "Unknown texture type from DDS."); return RenderTextureViewDimension::UNKNOWN; } } static RenderFormat ConvertDXGIFormat(ddspp::DXGIFormat format) { switch (format) { case ddspp::R32G32B32A32_TYPELESS: return RenderFormat::R32G32B32A32_TYPELESS; case ddspp::R32G32B32A32_FLOAT: return RenderFormat::R32G32B32A32_FLOAT; case ddspp::R32G32B32A32_UINT: return RenderFormat::R32G32B32A32_UINT; case ddspp::R32G32B32A32_SINT: return RenderFormat::R32G32B32A32_SINT; case ddspp::R32G32B32_TYPELESS: return RenderFormat::R32G32B32_TYPELESS; case ddspp::R32G32B32_FLOAT: return RenderFormat::R32G32B32_FLOAT; case ddspp::R32G32B32_UINT: return RenderFormat::R32G32B32_UINT; case ddspp::R32G32B32_SINT: return RenderFormat::R32G32B32_SINT; case ddspp::R16G16B16A16_TYPELESS: return RenderFormat::R16G16B16A16_TYPELESS; case ddspp::R16G16B16A16_FLOAT: return RenderFormat::R16G16B16A16_FLOAT; case ddspp::R16G16B16A16_UNORM: return RenderFormat::R16G16B16A16_UNORM; case ddspp::R16G16B16A16_UINT: return RenderFormat::R16G16B16A16_UINT; case ddspp::R16G16B16A16_SNORM: return RenderFormat::R16G16B16A16_SNORM; case ddspp::R16G16B16A16_SINT: return RenderFormat::R16G16B16A16_SINT; case ddspp::R32G32_TYPELESS: return RenderFormat::R32G32_TYPELESS; case ddspp::R32G32_FLOAT: return RenderFormat::R32G32_FLOAT; case ddspp::R32G32_UINT: return RenderFormat::R32G32_UINT; case ddspp::R32G32_SINT: return RenderFormat::R32G32_SINT; case ddspp::R8G8B8A8_TYPELESS: return RenderFormat::R8G8B8A8_TYPELESS; case ddspp::R8G8B8A8_UNORM: return RenderFormat::R8G8B8A8_UNORM; case ddspp::R8G8B8A8_UINT: return RenderFormat::R8G8B8A8_UINT; case ddspp::R8G8B8A8_SNORM: return RenderFormat::R8G8B8A8_SNORM; case ddspp::R8G8B8A8_SINT: return RenderFormat::R8G8B8A8_SINT; case ddspp::B8G8R8A8_UNORM: return RenderFormat::B8G8R8A8_UNORM; case ddspp::B8G8R8X8_UNORM: return RenderFormat::B8G8R8A8_UNORM; case ddspp::R16G16_TYPELESS: return RenderFormat::R16G16_TYPELESS; case ddspp::R16G16_FLOAT: return RenderFormat::R16G16_FLOAT; case ddspp::R16G16_UNORM: return RenderFormat::R16G16_UNORM; case ddspp::R16G16_UINT: return RenderFormat::R16G16_UINT; case ddspp::R16G16_SNORM: return RenderFormat::R16G16_SNORM; case ddspp::R16G16_SINT: return RenderFormat::R16G16_SINT; case ddspp::R32_TYPELESS: return RenderFormat::R32_TYPELESS; case ddspp::D32_FLOAT: return RenderFormat::D32_FLOAT; case ddspp::R32_FLOAT: return RenderFormat::R32_FLOAT; case ddspp::R32_UINT: return RenderFormat::R32_UINT; case ddspp::R32_SINT: return RenderFormat::R32_SINT; case ddspp::R8G8_TYPELESS: return RenderFormat::R8G8_TYPELESS; case ddspp::R8G8_UNORM: return RenderFormat::R8G8_UNORM; case ddspp::R8G8_UINT: return RenderFormat::R8G8_UINT; case ddspp::R8G8_SNORM: return RenderFormat::R8G8_SNORM; case ddspp::R8G8_SINT: return RenderFormat::R8G8_SINT; case ddspp::R16_TYPELESS: return RenderFormat::R16_TYPELESS; case ddspp::R16_FLOAT: return RenderFormat::R16_FLOAT; case ddspp::D16_UNORM: return RenderFormat::D16_UNORM; case ddspp::R16_UNORM: return RenderFormat::R16_UNORM; case ddspp::R16_UINT: return RenderFormat::R16_UINT; case ddspp::R16_SNORM: return RenderFormat::R16_SNORM; case ddspp::R16_SINT: return RenderFormat::R16_SINT; case ddspp::R8_TYPELESS: return RenderFormat::R8_TYPELESS; case ddspp::R8_UNORM: case ddspp::A8_UNORM: return RenderFormat::R8_UNORM; case ddspp::R8_UINT: return RenderFormat::R8_UINT; case ddspp::R8_SNORM: return RenderFormat::R8_SNORM; case ddspp::R8_SINT: return RenderFormat::R8_SINT; case ddspp::BC1_TYPELESS: return RenderFormat::BC1_TYPELESS; case ddspp::BC1_UNORM: return RenderFormat::BC1_UNORM; case ddspp::BC1_UNORM_SRGB: return RenderFormat::BC1_UNORM_SRGB; case ddspp::BC2_TYPELESS: return RenderFormat::BC2_TYPELESS; case ddspp::BC2_UNORM: return RenderFormat::BC2_UNORM; case ddspp::BC2_UNORM_SRGB: return RenderFormat::BC2_UNORM_SRGB; case ddspp::BC3_TYPELESS: return RenderFormat::BC3_TYPELESS; case ddspp::BC3_UNORM: return RenderFormat::BC3_UNORM; case ddspp::BC3_UNORM_SRGB: return RenderFormat::BC3_UNORM_SRGB; case ddspp::BC4_TYPELESS: return RenderFormat::BC4_TYPELESS; case ddspp::BC4_UNORM: return RenderFormat::BC4_UNORM; case ddspp::BC4_SNORM: return RenderFormat::BC4_SNORM; case ddspp::BC5_TYPELESS: return RenderFormat::BC5_TYPELESS; case ddspp::BC5_UNORM: return RenderFormat::BC5_UNORM; case ddspp::BC5_SNORM: return RenderFormat::BC5_SNORM; case ddspp::BC6H_TYPELESS: return RenderFormat::BC6H_TYPELESS; case ddspp::BC6H_UF16: return RenderFormat::BC6H_UF16; case ddspp::BC6H_SF16: return RenderFormat::BC6H_SF16; case ddspp::BC7_TYPELESS: return RenderFormat::BC7_TYPELESS; case ddspp::BC7_UNORM: return RenderFormat::BC7_UNORM; case ddspp::BC7_UNORM_SRGB: return RenderFormat::BC7_UNORM_SRGB; default: printf("format: %x\n", format); assert(false && "Unsupported format from DDS."); return RenderFormat::UNKNOWN; } } static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping) { ddspp::Descriptor ddsDesc; if (ddspp::decode_header((unsigned char *)(data), ddsDesc) != ddspp::Error) { RenderTextureDesc desc; desc.dimension = ConvertTextureDimension(ddsDesc.type); desc.width = ddsDesc.width; desc.height = ddsDesc.height; desc.depth = ddsDesc.depth; desc.mipLevels = ddsDesc.numMips; desc.arraySize = ddsDesc.type == ddspp::TextureType::Cubemap ? ddsDesc.arraySize * 6 : ddsDesc.arraySize; desc.format = ConvertDXGIFormat(ddsDesc.format); desc.flags = ddsDesc.type == ddspp::TextureType::Cubemap ? RenderTextureFlag::CUBE : RenderTextureFlag::NONE; texture.textureHolder = g_device->createTexture(desc); texture.texture = texture.textureHolder.get(); texture.layout = RenderTextureLayout::COPY_DEST; RenderTextureViewDesc viewDesc; viewDesc.format = desc.format; viewDesc.dimension = ConvertTextureViewDimension(ddsDesc.type); viewDesc.mipLevels = ddsDesc.numMips; if (ddsDesc.format == ddspp::A8_UNORM) { // Map A8_UNORM to R8_UNORM for compatability componentMapping = RenderComponentMapping(RenderSwizzle::ZERO, RenderSwizzle::ZERO, RenderSwizzle::ZERO, RenderSwizzle::R); } viewDesc.componentMapping = componentMapping; texture.textureView = texture.texture->createTextureView(viewDesc); texture.descriptorIndex = g_textureDescriptorAllocator.allocate(); g_textureDescriptorSet->setTexture(texture.descriptorIndex, texture.texture, RenderTextureLayout::SHADER_READ, texture.textureView.get()); texture.width = ddsDesc.width; texture.height = ddsDesc.height; texture.mipLevels = viewDesc.mipLevels; texture.viewDimension = viewDesc.dimension; struct Slice { uint32_t width; uint32_t height; uint32_t depth; uint32_t srcOffset; uint32_t dstOffset; uint32_t srcRowPitch; uint32_t dstRowPitch; uint32_t rowCount; }; std::vector slices; uint32_t curSrcOffset = 0; uint32_t curDstOffset = 0; for (uint32_t arraySlice = 0; arraySlice < desc.arraySize; arraySlice++) { for (uint32_t mipSlice = 0; mipSlice < ddsDesc.numMips; mipSlice++) { auto& slice = slices.emplace_back(); slice.width = std::max(1u, ddsDesc.width >> mipSlice); slice.height = std::max(1u, ddsDesc.height >> mipSlice); slice.depth = std::max(1u, ddsDesc.depth >> mipSlice); slice.srcOffset = curSrcOffset; slice.dstOffset = curDstOffset; uint32_t rowPitch = ((slice.width + ddsDesc.blockWidth - 1) / ddsDesc.blockWidth) * ddsDesc.bitsPerPixelOrBlock; slice.srcRowPitch = (rowPitch + 7) / 8; slice.dstRowPitch = (slice.srcRowPitch + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); slice.rowCount = (slice.height + ddsDesc.blockHeight - 1) / ddsDesc.blockHeight; curSrcOffset += slice.srcRowPitch * slice.rowCount * slice.depth; curDstOffset += (slice.dstRowPitch * slice.rowCount * slice.depth + PLACEMENT_ALIGNMENT - 1) & ~(PLACEMENT_ALIGNMENT - 1); } } auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(curDstOffset)); uint8_t* mappedMemory = reinterpret_cast(uploadBuffer->map()); for (auto& slice : slices) { const uint8_t* srcData = data + ddsDesc.headerSize + slice.srcOffset; uint8_t* dstData = mappedMemory + slice.dstOffset; if (slice.srcRowPitch == slice.dstRowPitch) { memcpy(dstData, srcData, slice.srcRowPitch * slice.rowCount * slice.depth); } else { for (size_t i = 0; i < slice.rowCount * slice.depth; i++) { memcpy(dstData, srcData, slice.srcRowPitch); srcData += slice.srcRowPitch; dstData += slice.dstRowPitch; } } } uploadBuffer->unmap(); ExecuteCopyCommandList([&] { g_copyCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(texture.texture, RenderTextureLayout::COPY_DEST)); for (size_t i = 0; i < slices.size(); i++) { auto& slice = slices[i]; g_copyCommandList->copyTextureRegion( RenderTextureCopyLocation::Subresource(texture.texture, i % desc.mipLevels, i / desc.mipLevels), RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), desc.format, slice.width, slice.height, slice.depth, (slice.dstRowPitch * 8) / ddsDesc.bitsPerPixelOrBlock * ddsDesc.blockWidth, slice.dstOffset)); } }); return true; } else { int width, height; void* stbImage = stbi_load_from_memory(data, dataSize, &width, &height, nullptr, 4); if (stbImage != nullptr) { texture.textureHolder = g_device->createTexture(RenderTextureDesc::Texture2D(width, height, 1, RenderFormat::R8G8B8A8_UNORM)); texture.texture = texture.textureHolder.get(); texture.viewDimension = RenderTextureViewDimension::TEXTURE_2D; texture.layout = RenderTextureLayout::COPY_DEST; texture.descriptorIndex = g_textureDescriptorAllocator.allocate(); g_textureDescriptorSet->setTexture(texture.descriptorIndex, texture.texture, RenderTextureLayout::SHADER_READ); uint32_t rowPitch = (width * 4 + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); uint32_t slicePitch = rowPitch * height; auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(slicePitch)); uint8_t* mappedMemory = reinterpret_cast(uploadBuffer->map()); if (rowPitch == (width * 4)) { memcpy(mappedMemory, stbImage, slicePitch); } else { auto data = reinterpret_cast(stbImage); for (size_t i = 0; i < height; i++) { memcpy(mappedMemory, data, width * 4); data += width * 4; mappedMemory += rowPitch; } } uploadBuffer->unmap(); stbi_image_free(stbImage); ExecuteCopyCommandList([&] { g_copyCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(texture.texture, RenderTextureLayout::COPY_DEST)); g_copyCommandList->copyTextureRegion( RenderTextureCopyLocation::Subresource(texture.texture, 0), RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); }); return true; } } return false; } std::unique_ptr LoadTexture(const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping) { GuestTexture texture(ResourceType::Texture); if (LoadTexture(texture, data, dataSize, componentMapping)) return std::make_unique(std::move(texture)); return nullptr; } static void DiffPatchTexture(GuestTexture& texture, uint8_t* data, uint32_t dataSize) { auto header = reinterpret_cast(g_buttonBcDiff.get()); auto entries = reinterpret_cast(g_buttonBcDiff.get() + header->entriesOffset); auto end = entries + header->entryCount; auto hash = XXH3_64bits(data, dataSize); auto findResult = std::lower_bound(entries, end, hash, [](BlockCompressionDiffPatchEntry& lhs, XXH64_hash_t rhs) { return lhs.hash < rhs; }); if (findResult != end && findResult->hash == hash) { auto patch = reinterpret_cast(g_buttonBcDiff.get() + findResult->patchesOffset); for (size_t i = 0; i < findResult->patchCount; i++) { assert(patch->destinationOffset + patch->patchBytesSize <= dataSize); memcpy(data + patch->destinationOffset, g_buttonBcDiff.get() + patch->patchBytesOffset, patch->patchBytesSize); ++patch; } GuestTexture patchedTexture(ResourceType::Texture); if (LoadTexture(patchedTexture, data, dataSize, {})) texture.patchedTexture = std::make_unique(std::move(patchedTexture)); } } static void MakePictureData(GuestMyTexture* pictureData, uint8_t* data, uint32_t dataSize) { if (data != nullptr) { GuestTexture texture(ResourceType::Texture); if (LoadTexture(texture, data, dataSize, {})) { #ifdef _DEBUG if (pictureData->str1.size.get() > 0) texture.texture->setName(fmt::format("Texture {}", pictureData->str1.c_str())); #endif DiffPatchTexture(texture, data, dataSize); pictureData->texture = g_memory.MapVirtual(g_userHeap.AllocPhysical(std::move(texture))); pictureData->width = texture.width; pictureData->height = texture.height; } } } void IndexBufferLengthMidAsmHook(PPCRegister& r3) { r3.u64 *= 2; } void SetShadowResolutionMidAsmHook(PPCRegister& r11) { auto res = (int32_t)Config::ShadowResolution.Value; if (res > 0) r11.u64 = res; } static void SetResolution(be* device) { Video::ComputeViewportDimensions(); uint32_t width = uint32_t(round(Video::s_viewportWidth * Config::ResolutionScale)); uint32_t height = uint32_t(round(Video::s_viewportHeight * Config::ResolutionScale)); device[46] = width == 0 ? 880 : width; device[47] = height == 0 ? 720 : height; } static GuestShader* g_movieVertexShader; static GuestShader* g_moviePixelShaderHD; static GuestShader* g_moviePixelShaderSD; static int ScreenShaderInit(be* a1) { if (g_movieVertexShader == nullptr) { g_movieVertexShader = g_userHeap.AllocPhysical(ResourceType::VertexShader); } if (g_moviePixelShaderHD == nullptr) { g_moviePixelShaderHD = g_userHeap.AllocPhysical(ResourceType::PixelShader); } if (g_moviePixelShaderSD == nullptr) { g_moviePixelShaderSD = g_userHeap.AllocPhysical(ResourceType::PixelShader); } g_moviePixelShaderHD->AddRef(); g_moviePixelShaderSD->AddRef(); g_movieVertexShader->AddRef(); a1[0x12] = g_memory.MapVirtual(g_movieVertexShader); a1[0x51] = g_memory.MapVirtual(g_moviePixelShaderHD); a1[0x52] = g_memory.MapVirtual(g_moviePixelShaderSD); return 0; } // Needed for correct clearing of index buffer static bool IsSet() { return true; } void MovieRendererMidAsmHook(PPCRegister& r3) { auto device = reinterpret_cast(g_memory.Translate(r3.u32)); // Force linear filtering & clamp addressing for (size_t i = 0; i < 3; i++) { device->samplerStates[i].data[0] = (device->samplerStates[i].data[0].get() & ~0x7fc00) | 0x24800; device->samplerStates[i].data[3] = (device->samplerStates[i].data[3].get() & ~0x1f80000) | 0x1280000; } device->dirtyFlags[3] = device->dirtyFlags[3].get() | 0xe0000000ull; } // Normally, we could delay setting IsMadeOne, but the game relies on that flag // being present to handle load priority. To work around that, we can prevent // IsMadeAll from being set until the compilation is finished. Time for a custom flag! enum { eDatabaseDataFlags_CompilingPipelines = 0x80 }; // This is passed to pipeline compilation threads to keep the loading screen busy until // all of them are finished. A shared pointer makes sure the destructor is called only once. //struct PipelineTaskToken //{ // PipelineTaskType type{}; // boost::shared_ptr databaseData; // // PipelineTaskToken() : databaseData() // { // } // // PipelineTaskToken(const PipelineTaskToken&) = delete; // // PipelineTaskToken(PipelineTaskToken&& other) // : type(std::exchange(other.type, PipelineTaskType::Null)) // , databaseData(std::exchange(other.databaseData, nullptr)) // { // } // // ~PipelineTaskToken() // { // if (type != PipelineTaskType::Null) // { // if (databaseData.get() != nullptr) // databaseData->m_Flags &= ~eDatabaseDataFlags_CompilingPipelines; // // if ((--g_compilingPipelineTaskCount) == 0) // g_compilingPipelineTaskCount.notify_one(); // } // } //}; //struct PipelineStateQueueItem //{ // XXH64_hash_t pipelineHash; // PipelineState pipelineState; // std::shared_ptr token; //#ifdef ASYNC_PSO_DEBUG // std::string pipelineName; //#endif //}; //static moodycamel::BlockingConcurrentQueue g_pipelineStateQueue; static void CompilePipeline(XXH64_hash_t pipelineHash, const PipelineState& pipelineState #ifdef ASYNC_PSO_DEBUG , const std::string& pipelineName #endif ) { auto pipeline = CreateGraphicsPipeline(pipelineState); #ifdef ASYNC_PSO_DEBUG pipeline->setName(pipelineName); #endif // Will get dropped in render thread if a different thread already managed to compile this. RenderCommand cmd; cmd.type = RenderCommandType::AddPipeline; cmd.addPipeline.hash = pipelineHash; cmd.addPipeline.pipeline = pipeline.release(); g_renderQueue.enqueue(cmd); } static void PipelineCompilerThread() { #ifdef _WIN32 int threadPriority = THREAD_PRIORITY_LOWEST; SetThreadPriority(GetCurrentThread(), threadPriority); GuestThread::SetThreadName(GetCurrentThreadId(), "Pipeline Compiler Thread"); #endif std::unique_ptr ctx; // while (true) // { // PipelineStateQueueItem queueItem; // g_pipelineStateQueue.wait_dequeue(queueItem); // // if (ctx == nullptr) // ctx = std::make_unique(0); // //#ifdef _WIN32 // int newThreadPriority = threadPriority; // // bool loading = *SWA::SGlobals::ms_IsLoading; // if (loading) // newThreadPriority = THREAD_PRIORITY_HIGHEST; // else // newThreadPriority = THREAD_PRIORITY_LOWEST; // // if (newThreadPriority != threadPriority) // { // SetThreadPriority(GetCurrentThread(), newThreadPriority); // threadPriority = newThreadPriority; // } //#endif // // CompilePipeline(queueItem.pipelineHash, queueItem.pipelineState //#ifdef ASYNC_PSO_DEBUG // , queueItem.pipelineName.c_str() //#endif // ); // // std::this_thread::yield(); // } } static std::vector> g_pipelineCompilerThreads = []() { size_t threadCount = std::max(2u, (std::thread::hardware_concurrency() * 2) / 3); std::vector> threads(threadCount); for (auto& thread : threads) thread = std::make_unique(PipelineCompilerThread); return threads; }(); static constexpr uint32_t MODEL_DATA_VFTABLE = 0x82073A44; static constexpr uint32_t TERRAIN_MODEL_DATA_VFTABLE = 0x8211D25C; static constexpr uint32_t PARTICLE_MATERIAL_VFTABLE = 0x8211F198; // Allocate the shared pointer only when new compilations are happening. // If nothing was compiled, the local "token" variable will get destructed with RAII instead. struct PipelineTaskTokenPair { // PipelineTaskToken token; // std::shared_ptr sharedToken; }; // Having this separate, because I don't want to lock a mutex in the render thread before // every single draw. Might be worth profiling to see if it actually has an impact and merge them. static xxHashMap g_asyncPipelineStates; //static void EnqueueGraphicsPipelineCompilation( // const PipelineState& pipelineState, // PipelineTaskTokenPair& tokenPair, // const char* name, // bool isPrecompiledPipeline = false) //{ // XXH64_hash_t hash = XXH3_64bits(&pipelineState, sizeof(pipelineState)); // bool shouldCompile = g_asyncPipelineStates.emplace(hash, pipelineState).second; // // if (shouldCompile) // { // bool loading = *SWA::SGlobals::ms_IsLoading; // if (!loading && isPrecompiledPipeline) // { // // We can just compile here during the logos. // CompilePipeline(hash, pipelineState //#ifdef ASYNC_PSO_DEBUG // , fmt::format("CACHE {} {:X}", name, hash) //#endif // ); // } // else // { // if (tokenPair.sharedToken == nullptr && tokenPair.token.type != PipelineTaskType::Null) // tokenPair.sharedToken = std::make_shared(std::move(tokenPair.token)); // // PipelineStateQueueItem queueItem; // queueItem.pipelineHash = hash; // queueItem.pipelineState = pipelineState; // queueItem.token = tokenPair.sharedToken; //#ifdef ASYNC_PSO_DEBUG // queueItem.pipelineName = fmt::format("ASYNC {} {:X}", name, hash); //#endif // g_pipelineStateQueue.enqueue(queueItem); // } // } // //#ifdef PSO_CACHING_CLEANUP // if (shouldCompile && isPrecompiledPipeline) // { // std::lock_guard lock(g_pipelineCacheMutex); // g_pipelineStatesToCache.emplace(hash, pipelineState); // } //#endif // //#ifdef PSO_CACHING // if (!isPrecompiledPipeline) // { // std::lock_guard lock(g_pipelineCacheMutex); // g_pipelineStatesToCache.erase(hash); // } //#endif //} struct CompilationArgs { PipelineTaskTokenPair tokenPair; bool noGI{}; bool hasMoreThanOneBone{}; bool velocityMapQuickStep{}; bool objectIcon{}; }; enum class MeshLayer { Opaque, Transparent, PunchThrough, Special }; struct Mesh { uint32_t vertexSize{}; uint32_t morphTargetVertexSize{}; GuestVertexDeclaration* vertexDeclaration{}; // Hedgehog::Mirage::CMaterialData* material{}; MeshLayer layer{}; bool morphModel{}; }; //static void CompileMeshPipeline(const Mesh& mesh, CompilationArgs& args) //{ // if (mesh.material == nullptr || mesh.material->m_spShaderListData.get() == nullptr) // return; // // auto& shaderList = mesh.material->m_spShaderListData; // // bool isFur = !mesh.morphModel && !args.instancing && // strstr(shaderList->m_TypeAndName.c_str(), "Fur") != nullptr; // // bool isSky = !mesh.morphModel && !args.instancing && // strstr(shaderList->m_TypeAndName.c_str(), "Sky") != nullptr; // // bool isSonicMouth = !mesh.morphModel && !args.instancing && // strcmp(mesh.material->m_TypeAndName.c_str() + 2, "sonic_gm_mouth_duble") == 0 && // strcmp(shaderList->m_TypeAndName.c_str() + 3, "SonicSkin_dspf[b]") == 0; // // bool compiledOutsideMainFramebuffer = !args.instancing && !isFur && !isSky; // // bool constTexCoord; // if (args.instancing) // { // constTexCoord = false; // } // else // { // constTexCoord = true; // if (mesh.material->m_spTexsetData.get() != nullptr) // { // for (size_t i = 1; i < mesh.material->m_spTexsetData->m_TextureList.size(); i++) // { // if (mesh.material->m_spTexsetData->m_TextureList[i]->m_TexcoordIndex != // mesh.material->m_spTexsetData->m_TextureList[0]->m_TexcoordIndex) // { // constTexCoord = false; // break; // } // } // } // } // // // Shadow pipeline. // // if (compiledOutsideMainFramebuffer && (mesh.layer == MeshLayer::Opaque || mesh.layer == MeshLayer::PunchThrough)) // // { // // PipelineState pipelineState{}; // // // if (mesh.layer == MeshLayer::PunchThrough) // // { // // pipelineState.vertexShader = FindShaderCacheEntry(0xDD4FA7BB53876300)->guestShader; // // pipelineState.pixelShader = FindShaderCacheEntry(0xE2ECA594590DDE8B)->guestShader; // // } // // else // // { // // pipelineState.vertexShader = FindShaderCacheEntry(0x8E4BB23465BD909E)->guestShader; // // } // // // pipelineState.vertexDeclaration = mesh.vertexDeclaration; // // pipelineState.cullMode = mesh.material->m_DoubleSided ? RenderCullMode::NONE : RenderCullMode::BACK; // // pipelineState.zFunc = RenderComparisonFunction::LESS_EQUAL; // // // if (g_capabilities.dynamicDepthBias) // // { // // // Put common depth bias values for reducing unnecessary calls. // // if (g_backend == Backend::D3D12) // // { // // pipelineState.depthBias = COMMON_DEPTH_BIAS_VALUE; // // pipelineState.slopeScaledDepthBias = COMMON_SLOPE_SCALED_DEPTH_BIAS_VALUE; // // } // // } // // else // // { // // pipelineState.depthBias = (1 << 24) * (*reinterpret_cast*>(g_memory.Translate(0x83302760))); // // pipelineState.slopeScaledDepthBias = *reinterpret_cast*>(g_memory.Translate(0x83302764)); // // } // // // pipelineState.colorWriteEnable = 0; // // pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; // // pipelineState.vertexStrides[0] = mesh.vertexSize; // // pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT; // // // if (mesh.layer == MeshLayer::PunchThrough) // // pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; // // // const char* name = (mesh.layer == MeshLayer::PunchThrough ? "MakeShadowMapTransparent" : "MakeShadowMap"); // // SanitizePipelineState(pipelineState); // // EnqueueGraphicsPipelineCompilation(pipelineState, args.tokenPair, name); // // // // Morph models have 4 targets where unused targets default to the first vertex stream. // // if (mesh.morphModel) // // { // // for (size_t i = 0; i < 5; i++) // // { // // for (size_t j = 0; j < 4; j++) // // pipelineState.vertexStrides[j + 1] = i > j ? mesh.morphTargetVertexSize : mesh.vertexSize; // // // SanitizePipelineState(pipelineState); // // EnqueueGraphicsPipelineCompilation(pipelineState, args.tokenPair, name); // // } // // } // // } // // // // Motion blur pipeline. We could normally do the player here only, but apparently Werehog enemies also have object blur. // // // TODO: Do punch through meshes get rendered? // // if (!mesh.morphModel && compiledOutsideMainFramebuffer && args.hasMoreThanOneBone && mesh.layer == MeshLayer::Opaque) // // { // // PipelineState pipelineState{}; // // pipelineState.vertexShader = FindShaderCacheEntry(0x4620B236DC38100C)->guestShader; // // pipelineState.pixelShader = FindShaderCacheEntry(0xBBDB735BEACC8F41)->guestShader; // // pipelineState.vertexDeclaration = mesh.vertexDeclaration; // // pipelineState.cullMode = RenderCullMode::NONE; // // pipelineState.zFunc = RenderComparisonFunction::GREATER_EQUAL; // // pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; // // pipelineState.vertexStrides[0] = mesh.vertexSize; // // pipelineState.renderTargetFormat = RenderFormat::R8G8B8A8_UNORM; // // pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT; // // pipelineState.specConstants = SPEC_CONSTANT_REVERSE_Z; // // // SanitizePipelineState(pipelineState); // // EnqueueGraphicsPipelineCompilation(pipelineState, args.tokenPair, "FxVelocityMap"); // // // if (args.velocityMapQuickStep) // // { // // pipelineState.vertexShader = FindShaderCacheEntry(0x99DC3F27E402700D)->guestShader; // // SanitizePipelineState(pipelineState); // // EnqueueGraphicsPipelineCompilation(pipelineState, args.tokenPair, "FxVelocityMapQuickStep"); // // } // // } // // uint32_t defaultStr = args.instancing ? 0x820C8734 : 0x8202DDBC; // "instancing" for instancing, "default" for regular // guest_stack_var defaultSymbol(reinterpret_cast(g_memory.Translate(defaultStr))); // auto defaultFindResult = shaderList->m_PixelShaderPermutations.find(*defaultSymbol); // if (defaultFindResult == shaderList->m_PixelShaderPermutations.end()) // return; // // uint32_t pixelShaderSubPermutationsToCompile = 0; // if (constTexCoord) pixelShaderSubPermutationsToCompile |= 0x1; // if (args.noGI) pixelShaderSubPermutationsToCompile |= 0x2; // // if ((defaultFindResult->second.m_SubPermutations.get() & (1 << pixelShaderSubPermutationsToCompile)) == 0) pixelShaderSubPermutationsToCompile &= ~0x1; // if ((defaultFindResult->second.m_SubPermutations.get() & (1 << pixelShaderSubPermutationsToCompile)) == 0) pixelShaderSubPermutationsToCompile &= ~0x2; // // uint32_t noneStr = mesh.morphModel ? 0x820D72F0 : 0x8200D938; // "p" for morph, "none" for regular // guest_stack_var noneSymbol(reinterpret_cast(g_memory.Translate(noneStr))); // auto noneFindResult = defaultFindResult->second.m_VertexShaderPermutations.find(*noneSymbol); // if (noneFindResult == defaultFindResult->second.m_VertexShaderPermutations.end()) // return; // // uint32_t vertexShaderSubPermutationsToCompile = 0; // if (constTexCoord) vertexShaderSubPermutationsToCompile |= 0x1; // // if ((noneFindResult->second->m_SubPermutations.get() & (1 << vertexShaderSubPermutationsToCompile)) == 0) // vertexShaderSubPermutationsToCompile &= ~0x1; // // auto vertexDeclaration = mesh.vertexDeclaration; // bool instancing = args.instancing || isFur; // // if (instancing) // { // GuestVertexElement vertexElements[64]; // memcpy(vertexElements, mesh.vertexDeclaration->vertexElements.get(), (mesh.vertexDeclaration->vertexElementCount - 1) * sizeof(GuestVertexElement)); // // if (args.instancing) // { // vertexElements[mesh.vertexDeclaration->vertexElementCount - 1] = { 1, 0, 0x2A23B9, 0, 5, 4 }; // vertexElements[mesh.vertexDeclaration->vertexElementCount] = { 1, 12, 0x2C2159, 0, 5, 5 }; // vertexElements[mesh.vertexDeclaration->vertexElementCount + 1] = { 1, 16, 0x2C2159, 0, 5, 6 }; // vertexElements[mesh.vertexDeclaration->vertexElementCount + 2] = { 1, 20, 0x182886, 0, 10, 1 }; // vertexElements[mesh.vertexDeclaration->vertexElementCount + 3] = { 2, 0, 0x2C82A1, 0, 0, 1 }; // vertexElements[mesh.vertexDeclaration->vertexElementCount + 4] = D3DDECL_END(); // } // else if (isFur) // { // vertexElements[mesh.vertexDeclaration->vertexElementCount - 1] = { 1, 0, 0x2C82A1, 0, 0, 1 }; // vertexElements[mesh.vertexDeclaration->vertexElementCount] = { 2, 0, 0x2C83A4, 0, 0, 2 }; // vertexElements[mesh.vertexDeclaration->vertexElementCount + 1] = D3DDECL_END(); // } // // vertexDeclaration = CreateVertexDeclarationWithoutAddRef(vertexElements); // } // // for (auto& [pixelShaderSubPermutations, pixelShader] : defaultFindResult->second.m_PixelShaders) // { // if (pixelShader.get() == nullptr || (pixelShaderSubPermutations & 0x3) != pixelShaderSubPermutationsToCompile) // continue; // // for (auto& [vertexShaderSubPermutations, vertexShader] : noneFindResult->second->m_VertexShaders) // { // if (vertexShader.get() == nullptr || (vertexShaderSubPermutations & 0x1) != vertexShaderSubPermutationsToCompile) // continue; // // PipelineState pipelineState{}; // pipelineState.vertexShader = reinterpret_cast(vertexShader->m_spCode->m_pD3DVertexShader.get()); // pipelineState.pixelShader = reinterpret_cast(pixelShader->m_spCode->m_pD3DPixelShader.get()); // pipelineState.vertexDeclaration = vertexDeclaration; // pipelineState.instancing = instancing; // pipelineState.zWriteEnable = !isSky && mesh.layer != MeshLayer::Transparent; // pipelineState.srcBlend = RenderBlend::SRC_ALPHA; // pipelineState.destBlend = mesh.material->m_Additive ? RenderBlend::ONE : RenderBlend::INV_SRC_ALPHA; // pipelineState.cullMode = mesh.material->m_DoubleSided ? RenderCullMode::NONE : RenderCullMode::BACK; // pipelineState.zFunc = RenderComparisonFunction::GREATER_EQUAL; // Reverse Z // pipelineState.alphaBlendEnable = mesh.layer == MeshLayer::Transparent || mesh.layer == MeshLayer::Special; // pipelineState.srcBlendAlpha = RenderBlend::SRC_ALPHA; // pipelineState.destBlendAlpha = RenderBlend::INV_SRC_ALPHA; // pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; // pipelineState.vertexStrides[0] = mesh.vertexSize; // // if (args.instancing) // { // pipelineState.vertexStrides[1] = 24; // pipelineState.vertexStrides[2] = 4; // } // else if (isFur) // { // pipelineState.vertexStrides[1] = 4; // pipelineState.vertexStrides[2] = 4; // } // // pipelineState.renderTargetFormat = RenderFormat::R16G16B16A16_FLOAT; // pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT_S8_UINT; // pipelineState.sampleCount = Config::AntiAliasing != EAntiAliasing::None ? int32_t(Config::AntiAliasing.Value) : 1; // // if (pipelineState.vertexDeclaration->hasR11G11B10Normal) // pipelineState.specConstants |= SPEC_CONSTANT_R11G11B10_NORMAL; // // if (Config::GITextureFiltering == EGITextureFiltering::Bicubic) // pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER; // // if (mesh.layer == MeshLayer::PunchThrough) // { // if (Config::AntiAliasing != EAntiAliasing::None && Config::TransparencyAntiAliasing) // { // pipelineState.enableAlphaToCoverage = true; // pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TO_COVERAGE; // } // else // { // pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; // } // } // // if (!isSky) // pipelineState.specConstants |= SPEC_CONSTANT_REVERSE_Z; // // auto createGraphicsPipeline = [&](PipelineState& pipelineStateToCreate) // { // SanitizePipelineState(pipelineStateToCreate); // EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, args.tokenPair, shaderList->m_TypeAndName.c_str() + 3); // // // Morph models have 4 targets where unused targets default to the first vertex stream. // if (mesh.morphModel) // { // for (size_t i = 0; i < 5; i++) // { // for (size_t j = 0; j < 4; j++) // pipelineStateToCreate.vertexStrides[j + 1] = i > j ? mesh.morphTargetVertexSize : mesh.vertexSize; // // SanitizePipelineState(pipelineStateToCreate); // EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, args.tokenPair, shaderList->m_TypeAndName.c_str() + 3); // } // } // }; // // createGraphicsPipeline(pipelineState); // // // We cannot rely on this being accurate during loading as SceneEffect.prm.xml gets loaded a bit later. // bool planarReflectionEnabled = *reinterpret_cast(g_memory.Translate(0x832FA0D8)); // bool loading = *SWA::SGlobals::ms_IsLoading; // bool compileNoMsaaPipeline = pipelineState.sampleCount != 1 && (loading || planarReflectionEnabled); // // auto noMsaaPipeline = pipelineState; // noMsaaPipeline.sampleCount = 1; // noMsaaPipeline.enableAlphaToCoverage = false; // // if ((noMsaaPipeline.specConstants & SPEC_CONSTANT_ALPHA_TO_COVERAGE) != 0) // { // noMsaaPipeline.specConstants &= ~SPEC_CONSTANT_ALPHA_TO_COVERAGE; // noMsaaPipeline.specConstants |= SPEC_CONSTANT_ALPHA_TEST; // } // // if (compileNoMsaaPipeline) // { // // Planar reflections don't use MSAA. // createGraphicsPipeline(noMsaaPipeline); // } // // if (args.objectIcon) // { // // Object icons get rendered to a SDR buffer without MSAA. // auto iconPipelineState = noMsaaPipeline; // iconPipelineState.renderTargetFormat = RenderFormat::R8G8B8A8_UNORM; // createGraphicsPipeline(iconPipelineState); // } // // if (isSonicMouth) // { // // Sonic's mouth switches between "SonicSkin_dspf[b]" or "SonicSkinNodeInvX_dspf[b]" depending on the view angle. // auto mouthPipelineState = pipelineState; // mouthPipelineState.vertexShader = FindShaderCacheEntry(0x689AA3140AB9EBAA)->guestShader; // createGraphicsPipeline(mouthPipelineState); // // if (compileNoMsaaPipeline) // { // auto noMsaaMouthPipelineState = noMsaaPipeline; // noMsaaMouthPipelineState.vertexShader = mouthPipelineState.vertexShader; // createGraphicsPipeline(noMsaaMouthPipelineState); // } // } // } // } //} //static void CompileMeshPipeline(Hedgehog::Mirage::CMeshData* mesh, MeshLayer layer, CompilationArgs& args) //{ // CompileMeshPipeline(Mesh // { // mesh->m_VertexSize, // 0, // reinterpret_cast(mesh->m_VertexDeclarationPtr.m_pD3DVertexDeclaration.get()), // mesh->m_spMaterial.get(), // layer, // false // }, args); //} //static void CompileMeshPipeline(Hedgehog::Mirage::CMorphModelData* morphModel, Hedgehog::Mirage::CMeshIndexData* mesh, MeshLayer layer, CompilationArgs& args) //{ // CompileMeshPipeline(Mesh // { // morphModel->m_VertexSize, // morphModel->m_MorphTargetVertexSize, // reinterpret_cast(morphModel->m_VertexDeclarationPtr.m_pD3DVertexDeclaration.get()), // mesh->m_spMaterial.get(), // layer, // true // }, args); //} //template //static void CompileMeshPipelines(const T& modelData, CompilationArgs& args) //{ // for (auto& meshGroup : modelData.m_NodeGroupModels) // { // for (auto& mesh : meshGroup->m_OpaqueMeshes) // { // CompileMeshPipeline(mesh.get(), MeshLayer::Opaque, args); // // if (args.noGI) // For models that can be shown transparent (eg. medals) // CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); // } // // for (auto& mesh : meshGroup->m_TransparentMeshes) // CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); // // for (auto& mesh : meshGroup->m_PunchThroughMeshes) // CompileMeshPipeline(mesh.get(), MeshLayer::PunchThrough, args); // // for (auto& specialMeshGroup : meshGroup->m_SpecialMeshGroups) // { // for (auto& mesh : specialMeshGroup) // CompileMeshPipeline(mesh.get(), MeshLayer::Special, args); // TODO: Are there layer types other than water in this game?? // } // } // // for (auto& mesh : modelData.m_OpaqueMeshes) // { // CompileMeshPipeline(mesh.get(), MeshLayer::Opaque, args); // // if (args.noGI) // CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); // } // // for (auto& mesh : modelData.m_TransparentMeshes) // CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); // // for (auto& mesh : modelData.m_PunchThroughMeshes) // CompileMeshPipeline(mesh.get(), MeshLayer::PunchThrough, args); // // if constexpr (std::is_same_v) // { // for (auto& morphModel : modelData.m_MorphModels) // { // for (auto& mesh : morphModel->m_OpaqueMeshList) // CompileMeshPipeline(morphModel.get(), mesh.get(), MeshLayer::Opaque, args); // // for (auto& mesh : morphModel->m_TransparentMeshList) // CompileMeshPipeline(morphModel.get(), mesh.get(), MeshLayer::Transparent, args); // // for (auto& mesh : morphModel->m_PunchThroughMeshList) // CompileMeshPipeline(morphModel.get(), mesh.get(), MeshLayer::PunchThrough, args); // } // } //} //static void CompileParticleMaterialPipeline(const Hedgehog::Sparkle::CParticleMaterial& material, PipelineTaskTokenPair& tokenPair) //{ // auto& shaderList = material.m_spShaderListData; // if (shaderList.get() == nullptr) // return; // // guest_stack_var defaultSymbol(reinterpret_cast(g_memory.Translate(0x8202DDBC))); // auto defaultFindResult = shaderList->m_PixelShaderPermutations.find(*defaultSymbol); // if (defaultFindResult == shaderList->m_PixelShaderPermutations.end()) // return; // // guest_stack_var noneSymbol(reinterpret_cast(g_memory.Translate(0x8200D938))); // auto noneFindResult = defaultFindResult->second.m_VertexShaderPermutations.find(*noneSymbol); // if (noneFindResult == defaultFindResult->second.m_VertexShaderPermutations.end()) // return; // // // All the particle models in the game come with the unoptimized format, so we can assume it. // uint8_t unoptimizedVertexElements[144] = // { // 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x0C, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x03, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x18, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x06, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x24, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x07, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x30, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x38, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x01, 0x00, // 0x00, 0x00, 0x00, 0x40, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x02, 0x00, // 0x00, 0x00, 0x00, 0x48, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x03, 0x00, // 0x00, 0x00, 0x00, 0x50, 0x00, 0x1A, 0x23, 0xA6, 0x00, 0x0A, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x60, 0x00, 0x1A, 0x23, 0x86, 0x00, 0x02, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x64, 0x00, 0x1A, 0x20, 0x86, 0x00, 0x01, 0x00, 0x00, // 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // }; // // auto unoptimizedVertexDeclaration = CreateVertexDeclarationWithoutAddRef(reinterpret_cast(unoptimizedVertexElements)); // auto sparkleVertexDeclaration = CreateVertexDeclarationWithoutAddRef(reinterpret_cast(g_memory.Translate(0x8211F540))); // // bool isMeshShader = strstr(shaderList->m_TypeAndName.c_str(), "Mesh") != nullptr; // // PipelineState pipelineState{}; // pipelineState.vertexShader = reinterpret_cast(noneFindResult->second->m_VertexShaders.begin()->second->m_spCode->m_pD3DVertexShader.get()); // pipelineState.pixelShader = reinterpret_cast(defaultFindResult->second.m_PixelShaders.begin()->second->m_spCode->m_pD3DPixelShader.get()); // pipelineState.vertexDeclaration = isMeshShader ? unoptimizedVertexDeclaration : sparkleVertexDeclaration; // pipelineState.zWriteEnable = false; // pipelineState.zFunc = RenderComparisonFunction::GREATER_EQUAL; // pipelineState.alphaBlendEnable = true; // pipelineState.srcBlendAlpha = RenderBlend::SRC_ALPHA; // pipelineState.destBlendAlpha = RenderBlend::INV_SRC_ALPHA; // pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; // pipelineState.vertexStrides[0] = isMeshShader ? 104 : 28; // pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT_S8_UINT; // pipelineState.specConstants = SPEC_CONSTANT_REVERSE_Z; // // if (pipelineState.vertexDeclaration->hasR11G11B10Normal) // pipelineState.specConstants |= SPEC_CONSTANT_R11G11B10_NORMAL; // // switch (material.m_BlendMode.get()) // { // case Hedgehog::Sparkle::CParticleMaterial::eBlendMode_Zero: // pipelineState.srcBlend = RenderBlend::ZERO; // pipelineState.destBlend = RenderBlend::ZERO; // break; // case Hedgehog::Sparkle::CParticleMaterial::eBlendMode_Typical: // pipelineState.srcBlend = RenderBlend::SRC_ALPHA; // pipelineState.destBlend = RenderBlend::INV_SRC_ALPHA; // break; // case Hedgehog::Sparkle::CParticleMaterial::eBlendMode_Add: // pipelineState.srcBlend = RenderBlend::SRC_ALPHA; // pipelineState.destBlend = RenderBlend::ONE; // break; // default: // pipelineState.srcBlend = RenderBlend::ONE; // pipelineState.destBlend = RenderBlend::ONE; // break; // } // // auto createGraphicsPipeline = [&](PipelineState& pipelineStateToCreate) // { // SanitizePipelineState(pipelineStateToCreate); // EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, tokenPair, shaderList->m_TypeAndName.c_str() + 3); // }; // // // Mesh particles can use both cull modes. Quad particles are only NONE. // RenderCullMode cullModes[] = { RenderCullMode::NONE, RenderCullMode::BACK }; // uint32_t cullModeCount = isMeshShader ? std::size(cullModes) : 1; // RenderFormat renderTargetFormats[] = { RenderFormat::R16G16B16A16_FLOAT, RenderFormat::R8G8B8A8_UNORM }; // // for (size_t i = 0; i < cullModeCount; i++) // { // pipelineState.cullMode = cullModes[i]; // // for (auto renderTargetFormat : renderTargetFormats) // { // pipelineState.renderTargetFormat = renderTargetFormat; // // if (renderTargetFormat == RenderFormat::R16G16B16A16_FLOAT) // pipelineState.sampleCount = Config::AntiAliasing != EAntiAliasing::None ? int32_t(Config::AntiAliasing.Value) : 1; // else // pipelineState.sampleCount = 1; // // createGraphicsPipeline(pipelineState); // // // Always compile no MSAA variant for particles, as the planar // // reflection variable isn't reliable at this time of compilation. // bool compileNoMsaaPipeline = pipelineState.sampleCount != 1; // // auto noMsaaPipelineState = pipelineState; // noMsaaPipelineState.sampleCount = 1; // // if (compileNoMsaaPipeline) // createGraphicsPipeline(noMsaaPipelineState); // // if (!isMeshShader) // { // // Previous compilation was for locus particles. This one will be for quads. // auto quadPipelineState = pipelineState; // quadPipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST; // createGraphicsPipeline(quadPipelineState); // // if (compileNoMsaaPipeline) // { // auto noMsaaQuadPipelineState = noMsaaPipelineState; // noMsaaQuadPipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST; // createGraphicsPipeline(noMsaaQuadPipelineState); // } // } // } // } //} static std::thread::id g_mainThreadId = std::this_thread::get_id(); // SWA::CGameModeStage::ExitLoading // PPC_FUNC_IMPL(__imp__sub_825369A0); // PPC_FUNC(sub_825369A0) // { // assert(std::this_thread::get_id() == g_mainThreadId); // // // Wait for pipeline compilations to finish. // uint32_t value; // while ((value = g_compilingPipelineTaskCount.load()) != 0) // { // // Pump SDL events to prevent the OS // // from thinking the process is unresponsive. // SDL_PumpEvents(); // SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); // // g_compilingPipelineTaskCount.wait(value); // } // // __imp__sub_825369A0(ctx, base); // } // CModelData::CheckMadeAll // PPC_FUNC_IMPL(__imp__sub_82E2EFB0); // PPC_FUNC(sub_82E2EFB0) // { // if (reinterpret_cast(base + ctx.r3.u32)->m_Flags & eDatabaseDataFlags_CompilingPipelines) // { // ctx.r3.u64 = 0; // } // else // { // __imp__sub_82E2EFB0(ctx, base); // } // } // CTerrainModelData::CheckMadeAll // PPC_FUNC_IMPL(__imp__sub_82E243D8); // PPC_FUNC(sub_82E243D8) // { // if (reinterpret_cast(base + ctx.r3.u32)->m_Flags & eDatabaseDataFlags_CompilingPipelines) // { // ctx.r3.u64 = 0; // } // else // { // __imp__sub_82E243D8(ctx, base); // } // } // CParticleMaterial::CheckMadeAll // PPC_FUNC_IMPL(__imp__sub_82E87598); // PPC_FUNC(sub_82E87598) // { // if (reinterpret_cast(base + ctx.r3.u32)->m_Flags & eDatabaseDataFlags_CompilingPipelines) // { // ctx.r3.u64 = 0; // } // else // { // __imp__sub_82E87598(ctx, base); // } // } //void GetDatabaseDataMidAsmHook(PPCRegister& r1, PPCRegister& r4) //{ // auto& databaseData = *reinterpret_cast*>( // g_memory.Translate(r1.u32 + 0x58)); // // if (!databaseData->IsMadeOne() && r4.u32 != NULL) // { // if (databaseData->m_pVftable.ptr == MODEL_DATA_VFTABLE) // { // // Ignore particle models, the materials they point at don't actually // // get used and give the threads unnecessary work. // bool isParticleModel = *reinterpret_cast*>(g_memory.Translate(r4.u32 + 4)) != 5 && // strncmp(databaseData->m_TypeAndName.c_str() + 2, "eff_", 4) == 0; // // if (isParticleModel) // return; // // // Adabat water is broken in original game, which they tried to fix by partially including the files in the update, // // which then finally fixed for real in the DLC. This confuses the async PSO compiler and causes a hang if the DLC is missing. // // We'll just ignore it. // bool isAdabatWater = strcmp(databaseData->m_TypeAndName.c_str() + 2, "evl_sea_obj_st_waterCircle") == 0; // if (isAdabatWater) // return; // } // // databaseData->m_Flags |= eDatabaseDataFlags_CompilingPipelines; // EnqueuePipelineTask(PipelineTaskType::DatabaseData, databaseData); // } //} //static bool CheckMadeAll(Hedgehog::Mirage::CMeshData* meshData) //{ // if (!meshData->IsMadeOne()) // return false; // // if (meshData->m_spMaterial.get() != nullptr) // { // if (!meshData->m_spMaterial->IsMadeOne()) // return false; // // if (meshData->m_spMaterial->m_spTexsetData.get() != nullptr) // { // if (!meshData->m_spMaterial->m_spTexsetData->IsMadeOne()) // return false; // // for (auto& texture : meshData->m_spMaterial->m_spTexsetData->m_TextureList) // { // if (!texture->IsMadeOne()) // return false; // } // } // } // // return true; //} template static bool CheckMadeAll(const T& modelData) { if (!modelData.IsMadeOne()) return false; for (auto& meshGroup : modelData.m_NodeGroupModels) { for (auto& mesh : meshGroup->m_OpaqueMeshes) { if (!CheckMadeAll(mesh.get())) return false; } for (auto& mesh : meshGroup->m_TransparentMeshes) { if (!CheckMadeAll(mesh.get())) return false; } for (auto& mesh : meshGroup->m_PunchThroughMeshes) { if (!CheckMadeAll(mesh.get())) return false; } for (auto& specialMeshGroup : meshGroup->m_SpecialMeshGroups) { for (auto& mesh : specialMeshGroup) { if (!CheckMadeAll(mesh.get())) return false; } } } for (auto& mesh : modelData.m_OpaqueMeshes) { if (!CheckMadeAll(mesh.get())) return false; } for (auto& mesh : modelData.m_TransparentMeshes) { if (!CheckMadeAll(mesh.get())) return false; } for (auto& mesh : modelData.m_PunchThroughMeshes) { if (!CheckMadeAll(mesh.get())) return false; } return true; } //static void PipelineTaskConsumerThread() //{ //#ifdef _WIN32 // SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); // GuestThread::SetThreadName(GetCurrentThreadId(), "Pipeline Task Consumer Thread"); //#endif // // std::vector localPipelineTaskQueue; // std::unique_ptr ctx; // // while (true) // { // // Wait for tasks to arrive. // uint32_t pendingPipelineTaskCount; // while ((pendingPipelineTaskCount = g_pendingPipelineTaskCount.load()) == 0) // g_pendingPipelineTaskCount.wait(pendingPipelineTaskCount); // // if (ctx == nullptr) // ctx = std::make_unique(0); // // { // std::lock_guard lock(g_pipelineTaskMutex); // localPipelineTaskQueue.insert(localPipelineTaskQueue.end(), g_pipelineTaskQueue.begin(), g_pipelineTaskQueue.end()); // g_pipelineTaskQueue.clear(); // } // // bool allHandled = true; // // for (auto& [type, databaseData] : localPipelineTaskQueue) // { // switch (type) // { // case PipelineTaskType::DatabaseData: // { // bool ready = false; // // if (databaseData->m_pVftable.ptr == MODEL_DATA_VFTABLE) // ready = CheckMadeAll(*reinterpret_cast(databaseData.get())); // else // ready = databaseData->IsMadeOne(); // // if (ready || databaseData.unique()) // { // if (databaseData->m_pVftable.ptr == TERRAIN_MODEL_DATA_VFTABLE) // { // CompilationArgs args{}; // args.tokenPair.token.type = type; // args.tokenPair.token.databaseData = databaseData; // args.instancing = strncmp(databaseData->m_TypeAndName.c_str() + 3, "ins", 3) == 0; // CompileMeshPipelines(*reinterpret_cast(databaseData.get()), args); // } // else if (databaseData->m_pVftable.ptr == PARTICLE_MATERIAL_VFTABLE) // { // PipelineTaskTokenPair tokenPair; // tokenPair.token.type = type; // tokenPair.token.databaseData = databaseData; // CompileParticleMaterialPipeline(*reinterpret_cast(databaseData.get()), tokenPair); // } // else // { // assert(databaseData->m_pVftable.ptr == MODEL_DATA_VFTABLE); // // auto modelData = reinterpret_cast(databaseData.get()); // // CompilationArgs args{}; // args.tokenPair.token.type = type; // args.tokenPair.token.databaseData = databaseData; // args.noGI = true; // args.hasMoreThanOneBone = modelData->m_NodeNum > 1; // args.velocityMapQuickStep = strcmp(databaseData->m_TypeAndName.c_str() + 2, "SonicRoot") == 0; // // // Check for the on screen items, eg. rings going to HUD. // auto items = reinterpret_cast*>(g_memory.Translate(0x832A8DD0)); // for (size_t i = 0; i < 50; i++) // { // if (strcmp(databaseData->m_TypeAndName.c_str() + 2, (*items).get()) == 0) // { // args.objectIcon = true; // break; // } // items += 7; // } // // CompileMeshPipelines(*modelData, args); // } // // type = PipelineTaskType::Null; // databaseData = nullptr; // // --g_pendingPipelineTaskCount; // } // else // { // allHandled = false; // } // // break; // } // // case PipelineTaskType::PrecompilePipelines: // { // // Deliberately leaving the type null to account for the enqueue // // call not incrementing the compiling pipeline task counter. // PipelineTaskTokenPair tokenPair; // // for (auto vertexElements : g_vertexDeclarationCache) // CreateVertexDeclarationWithoutAddRef(reinterpret_cast(vertexElements)); // // for (auto pipelineState : g_pipelineStateCache) // { // // The hashes were reinterpret casted to pointers in the cache. // pipelineState.vertexShader = FindShaderCacheEntry(reinterpret_cast(pipelineState.vertexShader))->guestShader; // // if (pipelineState.pixelShader != nullptr) // pipelineState.pixelShader = FindShaderCacheEntry(reinterpret_cast(pipelineState.pixelShader))->guestShader; // // { // std::lock_guard lock(g_vertexDeclarationMutex); // pipelineState.vertexDeclaration = g_vertexDeclarations[reinterpret_cast(pipelineState.vertexDeclaration)]; // } // // if (!g_capabilities.triangleFan && pipelineState.primitiveTopology == RenderPrimitiveTopology::TRIANGLE_FAN) // pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST; // // // Zero out depth bias for Vulkan, we only store common values for D3D12. // if (g_capabilities.dynamicDepthBias && g_backend != Backend::D3D12) // { // pipelineState.depthBias = 0; // pipelineState.slopeScaledDepthBias = 0.0f; // } // // if (Config::GITextureFiltering == EGITextureFiltering::Bicubic) // pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER; // // auto createGraphicsPipeline = [&](PipelineState& pipelineStateToCreate, const char* name) // { // SanitizePipelineState(pipelineStateToCreate); // EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, tokenPair, name, true); // }; // // // Compile both MSAA and non MSAA variants to work with reflection maps. The render formats are an assumption but it should hold true. // if (Config::AntiAliasing != EAntiAliasing::None && // pipelineState.renderTargetFormat == RenderFormat::R16G16B16A16_FLOAT && // pipelineState.depthStencilFormat == RenderFormat::D32_FLOAT_S8_UINT) // { // auto msaaPipelineState = pipelineState; // msaaPipelineState.sampleCount = int32_t(Config::AntiAliasing.Value); // // if (Config::TransparencyAntiAliasing && (msaaPipelineState.specConstants & SPEC_CONSTANT_ALPHA_TEST) != 0) // { // msaaPipelineState.enableAlphaToCoverage = true; // msaaPipelineState.specConstants &= ~SPEC_CONSTANT_ALPHA_TEST; // msaaPipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TO_COVERAGE; // } // // createGraphicsPipeline(msaaPipelineState, "Precompiled Pipeline MSAA"); // } // // if (pipelineState.pixelShader != nullptr && // pipelineState.pixelShader->shaderCacheEntry != nullptr) // { // XXH64_hash_t hash = pipelineState.pixelShader->shaderCacheEntry->hash; // // // Compile the custom gaussian blur shaders that we pass to the game. // if (hash == 0x4294510C775F4EE8) // { // for (auto& shader : g_gaussianBlurShaders) // { // auto newPipelineState = pipelineState; // newPipelineState.pixelShader = shader.get(); // createGraphicsPipeline(newPipelineState, "Precompiled Gaussian Blur Pipeline"); // } // } // // Compile enhanced motion blur shader. // else if (hash == 0x6B9732B4CD7E7740) // { // auto newPipelineState = pipelineState; // newPipelineState.pixelShader = g_enhancedMotionBlurShader.get(); // createGraphicsPipeline(newPipelineState, "Precompiled Enhanced Motion Blur Pipeline"); // } // } // // createGraphicsPipeline(pipelineState, "Precompiled Pipeline"); // // // Compile the CSD filter shader that we pass to the game when point filtering is used. // if (pipelineState.pixelShader == g_csdShader) // { // pipelineState.pixelShader = g_csdFilterShader.get(); // createGraphicsPipeline(pipelineState, "Precompiled CSD Filter Pipeline"); // } // } // // type = PipelineTaskType::Null; // --g_pendingPipelineTaskCount; // // break; // } // // case PipelineTaskType::RecompilePipelines: // { // PipelineTaskTokenPair tokenPair; // tokenPair.token.type = type; // // auto asyncPipelines = g_asyncPipelineStates.values(); // // for (auto& [hash, pipelineState] : asyncPipelines) // { // bool alphaTest = (pipelineState.specConstants & (SPEC_CONSTANT_ALPHA_TEST | SPEC_CONSTANT_ALPHA_TO_COVERAGE)) != 0; // bool msaa = pipelineState.sampleCount != 1 || (pipelineState.renderTargetFormat == RenderFormat::R16G16B16A16_FLOAT && pipelineState.depthStencilFormat == RenderFormat::D32_FLOAT_S8_UINT); // // pipelineState.sampleCount = 1; // pipelineState.enableAlphaToCoverage = false; // pipelineState.specConstants &= ~(SPEC_CONSTANT_BICUBIC_GI_FILTER | SPEC_CONSTANT_ALPHA_TEST | SPEC_CONSTANT_ALPHA_TO_COVERAGE); // // if (msaa && Config::AntiAliasing != EAntiAliasing::None) // { // pipelineState.sampleCount = int32_t(Config::AntiAliasing.Value); // // if (alphaTest) // { // if (Config::TransparencyAntiAliasing) // { // pipelineState.enableAlphaToCoverage = true; // pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TO_COVERAGE; // } // else // { // pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; // } // } // } // else if (alphaTest) // { // pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; // } // // if (Config::GITextureFiltering == EGITextureFiltering::Bicubic) // pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER; // // SanitizePipelineState(pipelineState); // EnqueueGraphicsPipelineCompilation(pipelineState, tokenPair, "Recompiled Pipeline State"); // } // // type = PipelineTaskType::Null; // --g_pendingPipelineTaskCount; // // break; // } // } // } // // if (allHandled) // localPipelineTaskQueue.clear(); // // std::this_thread::yield(); // } //} //static std::thread g_pipelineTaskConsumerThread(PipelineTaskConsumerThread); #ifdef ASYNC_PSO_DEBUG // PPC_FUNC_IMPL(__imp__sub_82E33330); // PPC_FUNC(sub_82E33330) // { // auto vertexShaderCode = reinterpret_cast(g_memory.Translate(ctx.r4.u32)); // __imp__sub_82E33330(ctx, base); // reinterpret_cast(vertexShaderCode->m_pD3DVertexShader.get())->name = vertexShaderCode->m_TypeAndName.c_str() + 3; // } // PPC_FUNC_IMPL(__imp__sub_82E328D8); // PPC_FUNC(sub_82E328D8) // { // auto pixelShaderCode = reinterpret_cast(g_memory.Translate(ctx.r4.u32)); // __imp__sub_82E328D8(ctx, base); // reinterpret_cast(pixelShaderCode->m_pD3DPixelShader.get())->name = pixelShaderCode->m_TypeAndName.c_str() + 2; // } #endif #ifdef PSO_CACHING class SDLEventListenerForPSOCaching : public SDLEventListener { public: bool OnSDLEvent(SDL_Event* event) override { if (event->type != SDL_QUIT) return false; std::lock_guard lock(g_pipelineCacheMutex); if (g_pipelineStatesToCache.empty()) return false; FILE* f = fopen("send_this_file_to_skyth.txt", "ab"); if (f != nullptr) { ankerl::unordered_dense::set vertexDeclarations; xxHashMap pipelineStatesToCache; for (auto& [hash, pipelineState] : g_pipelineStatesToCache) { if (pipelineState.vertexShader->shaderCacheEntry == nullptr || (pipelineState.pixelShader != nullptr && pipelineState.pixelShader->shaderCacheEntry == nullptr)) { continue; } vertexDeclarations.emplace(pipelineState.vertexDeclaration); // Mask out the config options. pipelineState.sampleCount = 1; pipelineState.enableAlphaToCoverage = false; if ((pipelineState.specConstants & SPEC_CONSTANT_ALPHA_TO_COVERAGE) != 0) { pipelineState.specConstants &= ~SPEC_CONSTANT_ALPHA_TO_COVERAGE; pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; } pipelineStatesToCache.emplace(XXH3_64bits(&pipelineState, sizeof(pipelineState)), pipelineState); } for (auto vertexDeclaration : vertexDeclarations) { fmt::print(f, "static uint8_t g_vertexElements_{:016X}[] = {{", vertexDeclaration->hash); auto bytes = reinterpret_cast(vertexDeclaration->vertexElements.get()); for (size_t i = 0; i < vertexDeclaration->vertexElementCount * sizeof(GuestVertexElement); i++) fmt::print(f, "0x{:X},", bytes[i]); fmt::println(f, "}};"); } for (auto& [pipelineHash, pipelineState] : pipelineStatesToCache) { fmt::println(f, "{{ " "reinterpret_cast(0x{:X})," "reinterpret_cast(0x{:X})," "reinterpret_cast(0x{:X})," "{}," "{}," "{}," "{}," "RenderBlend::{}," "RenderBlend::{}," "RenderCullMode::{}," "RenderFrontFace::{}," "RenderComparisonFunction::{}," "RenderComparisonFunction::{}," "RenderStencilOp::{}," "RenderStencilOp::{}," "RenderStencilOp::{}," "RenderComparisonFunction::{}," "RenderStencilOp::{}," "RenderStencilOp::{}," "RenderStencilOp::{}," "{}," "{}," "{}," "{}," "RenderBlendOperation::{}," "{}," "{}," "RenderBlend::{}," "RenderBlend::{}," "RenderBlendOperation::{}," "0x{:X}," "RenderPrimitiveTopology::{}," "{{ {},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{} }}," "RenderFormat::{}," "RenderFormat::{}," "{}," "{}," "{}," "0x{:X} }},", pipelineState.vertexShader->shaderCacheEntry->hash, pipelineState.pixelShader != nullptr ? pipelineState.pixelShader->shaderCacheEntry->hash : 0, pipelineState.vertexDeclaration->hash, pipelineState.zEnable, pipelineState.zWriteEnable, pipelineState.stencilEnable, pipelineState.stencilTwoSided, magic_enum::enum_name(pipelineState.srcBlend), magic_enum::enum_name(pipelineState.destBlend), magic_enum::enum_name(pipelineState.cullMode), magic_enum::enum_name(pipelineState.frontFace), magic_enum::enum_name(pipelineState.zFunc), magic_enum::enum_name(pipelineState.stencilFunc), magic_enum::enum_name(pipelineState.stencilFail), magic_enum::enum_name(pipelineState.stencilZFail), magic_enum::enum_name(pipelineState.stencilPass), magic_enum::enum_name(pipelineState.stencilFuncCCW), magic_enum::enum_name(pipelineState.stencilFailCCW), magic_enum::enum_name(pipelineState.stencilZFailCCW), magic_enum::enum_name(pipelineState.stencilPassCCW), pipelineState.stencilMask, pipelineState.stencilWriteMask, pipelineState.stencilRef, pipelineState.alphaBlendEnable, magic_enum::enum_name(pipelineState.blendOp), pipelineState.slopeScaledDepthBias, pipelineState.depthBias, magic_enum::enum_name(pipelineState.srcBlendAlpha), magic_enum::enum_name(pipelineState.destBlendAlpha), magic_enum::enum_name(pipelineState.blendOpAlpha), pipelineState.colorWriteEnable, magic_enum::enum_name(pipelineState.primitiveTopology), pipelineState.vertexStrides[0], pipelineState.vertexStrides[1], pipelineState.vertexStrides[2], pipelineState.vertexStrides[3], pipelineState.vertexStrides[4], pipelineState.vertexStrides[5], pipelineState.vertexStrides[6], pipelineState.vertexStrides[7], pipelineState.vertexStrides[8], pipelineState.vertexStrides[9], pipelineState.vertexStrides[10], pipelineState.vertexStrides[11], pipelineState.vertexStrides[12], pipelineState.vertexStrides[13], pipelineState.vertexStrides[14], pipelineState.vertexStrides[15], magic_enum::enum_name(pipelineState.renderTargetFormat), magic_enum::enum_name(pipelineState.depthStencilFormat), pipelineState.sampleCount, pipelineState.enableAlphaToCoverage, pipelineState.enableConditionalSurvey, pipelineState.specConstants); } fclose(f); } return false; } }; SDLEventListenerForPSOCaching g_sdlEventListenerForPSOCaching; #endif void VideoConfigValueChangedCallback(IConfigDef* config) { // Config options that require internal resolution resize g_needsResize |= config == &Config::AspectRatio || config == &Config::ResolutionScale || config == &Config::AntiAliasing || config == &Config::ShadowResolution; if (g_needsResize) Video::ComputeViewportDimensions(); // Config options that require pipeline recompilation bool shouldRecompile = config == &Config::AntiAliasing || config == &Config::TransparencyAntiAliasing; // if (shouldRecompile) // EnqueuePipelineTask(PipelineTaskType::RecompilePipelines, {}); } // There is a bug on AMD where restart indices cause incorrect culling and prevent some triangles from being rendered. // This seems to happen on both Windows AMD drivers and Mesa. Converting restart indices to degenerate triangles fixes it. static void ConvertToDegenerateTriangles(uint16_t* indices, uint32_t indexCount, uint16_t*& newIndices, uint32_t& newIndexCount) { newIndices = reinterpret_cast(g_userHeap.Alloc(indexCount * sizeof(uint16_t) * 3)); newIndexCount = 0; bool stripStart = true; uint32_t stripSize = 0; uint16_t lastIndex = 0; for (uint32_t i = 0; i < indexCount; i++) { uint16_t index = indices[i]; if (index == 0xFFFF) { if ((stripSize % 2) != 0) newIndices[newIndexCount++] = lastIndex; stripStart = true; stripSize = 0; } else { if (stripStart && newIndexCount != 0) { newIndices[newIndexCount++] = lastIndex; newIndices[newIndexCount++] = index; } newIndices[newIndexCount++] = index; stripStart = false; ++stripSize; lastIndex = index; } } } struct MeshResource { MARATHON_INSERT_PADDING(0x4); be indexCount; be indices; }; static std::vector g_newIndicesToFree; // Hedgehog::Mirage::CMeshData::Make // PPC_FUNC_IMPL(__imp__sub_82E44AF8); // PPC_FUNC(sub_82E44AF8) // { // uint16_t* newIndicesToFree = nullptr; // auto databaseData = reinterpret_cast(base + ctx.r3.u32); // if (g_triangleStripWorkaround && !databaseData->IsMadeOne()) // { // auto meshResource = reinterpret_cast(base + ctx.r4.u32); // if (meshResource->indexCount != 0) // { // uint16_t* newIndices; // uint32_t newIndexCount; // ConvertToDegenerateTriangles( // reinterpret_cast(base + meshResource->indices), // meshResource->indexCount, // newIndices, // newIndexCount); // meshResource->indexCount = newIndexCount; // meshResource->indices = static_cast(reinterpret_cast(newIndices) - base); // if (PPC_LOAD_U32(0x83396E98) != NULL) // { // // If index buffers are getting merged, new indices need to survive until the merge happens. // g_newIndicesToFree.push_back(newIndices); // } // else // { // // Otherwise, we can free it immediately. // newIndicesToFree = newIndices; // } // } // } // __imp__sub_82E44AF8(ctx, base); // if (newIndicesToFree != nullptr) // g_userHeap.Free(newIndicesToFree); // } // Hedgehog::Mirage::CShareVertexBuffer::Reset // PPC_FUNC_IMPL(__imp__sub_82E250D0); // PPC_FUNC(sub_82E250D0) // { // __imp__sub_82E250D0(ctx, base); // for (auto newIndicesToFree : g_newIndicesToFree) // g_userHeap.Free(newIndicesToFree); // g_newIndicesToFree.clear(); // } struct LightAndIndexBufferResourceV1 { MARATHON_INSERT_PADDING(0x4); be indexCount; be indices; }; // Hedgehog::Mirage::CLightAndIndexBufferData::MakeV1 // PPC_FUNC_IMPL(__imp__sub_82E3AFC8); // PPC_FUNC(sub_82E3AFC8) // { // uint16_t* newIndices = nullptr; // auto databaseData = reinterpret_cast(base + ctx.r3.u32); // if (g_triangleStripWorkaround && !databaseData->IsMadeOne()) // { // auto lightAndIndexBufferResource = reinterpret_cast(base + ctx.r4.u32); // if (lightAndIndexBufferResource->indexCount != 0) // { // uint32_t newIndexCount; // ConvertToDegenerateTriangles( // reinterpret_cast(base + lightAndIndexBufferResource->indices), // lightAndIndexBufferResource->indexCount, // newIndices, // newIndexCount); // lightAndIndexBufferResource->indexCount = newIndexCount; // lightAndIndexBufferResource->indices = static_cast(reinterpret_cast(newIndices) - base); // } // } // __imp__sub_82E3AFC8(ctx, base); // if (newIndices != nullptr) // g_userHeap.Free(newIndices); // } struct LightAndIndexBufferResourceV5 { MARATHON_INSERT_PADDING(0x8); be indexCount; be indices; }; // Hedgehog::Mirage::CLightAndIndexBufferData::MakeV5 // PPC_FUNC_IMPL(__imp__sub_82E3B1C0); // PPC_FUNC(sub_82E3B1C0) // { // uint16_t* newIndices = nullptr; // auto databaseData = reinterpret_cast(base + ctx.r3.u32); // if (g_triangleStripWorkaround && !databaseData->IsMadeOne()) // { // auto lightAndIndexBufferResource = reinterpret_cast(base + ctx.r4.u32); // if (lightAndIndexBufferResource->indexCount != 0) // { // uint32_t newIndexCount; // ConvertToDegenerateTriangles( // reinterpret_cast(base + lightAndIndexBufferResource->indices), // lightAndIndexBufferResource->indexCount, // newIndices, // newIndexCount); // lightAndIndexBufferResource->indexCount = newIndexCount; // lightAndIndexBufferResource->indices = static_cast(reinterpret_cast(newIndices) - base); // } // } // __imp__sub_82E3B1C0(ctx, base); // if (newIndices != nullptr) // g_userHeap.Free(newIndices); // } GUEST_FUNCTION_HOOK(sub_8253EC98, CreateDevice); GUEST_FUNCTION_HOOK(sub_8253AE98, DestructResource); GUEST_FUNCTION_HOOK(sub_8253A740, LockTextureRect); GUEST_FUNCTION_HOOK(sub_82538D30, UnlockTextureRect); GUEST_FUNCTION_HOOK(sub_8253B5D0, LockVertexBuffer); GUEST_FUNCTION_HOOK(sub_8253B630, UnlockVertexBuffer); // GUEST_FUNCTION_HOOK(sub_82BE61D0, GetVertexBufferDesc); GUEST_FUNCTION_HOOK(sub_8253B6F0, LockIndexBuffer); GUEST_FUNCTION_HOOK(sub_8253B750, UnlockIndexBuffer); // GUEST_FUNCTION_HOOK(sub_82BE6200, GetIndexBufferDesc); GUEST_FUNCTION_HOOK(sub_8253AB20, GetSurfaceDesc); GUEST_FUNCTION_HOOK(sub_825471F8, GetVertexDeclaration); // GUEST_FUNCTION_HOOK(sub_82BE0530, HashVertexDeclaration); GUEST_FUNCTION_HOOK(sub_825586B0, Video::Present); GUEST_FUNCTION_HOOK(sub_82543B58, GetBackBuffer); GUEST_FUNCTION_HOOK(sub_82543BA0, GetDepthStencil); GUEST_FUNCTION_HOOK(sub_8253A8D8, CreateTexture); GUEST_FUNCTION_HOOK(sub_8253B508, CreateVertexBuffer); GUEST_FUNCTION_HOOK(sub_8253B640, CreateIndexBuffer); GUEST_FUNCTION_HOOK(sub_8253A9F8, CreateSurface); GUEST_FUNCTION_HOOK(sub_825575B8, StretchRect); GUEST_FUNCTION_HOOK(sub_82543EE0, SetRenderTarget); GUEST_FUNCTION_HOOK(sub_825444F0, SetRenderTarget); GUEST_FUNCTION_HOOK(sub_82544210, SetDepthStencilSurface); GUEST_FUNCTION_HOOK(sub_82555B30, Clear); GUEST_FUNCTION_HOOK(sub_825436F0, SetViewport); GUEST_FUNCTION_HOOK(sub_8253AC40, SetTexture); GUEST_FUNCTION_HOOK(sub_82543628, SetScissorRect); GUEST_FUNCTION_HOOK(sub_826FEC28, DrawPrimitive); GUEST_FUNCTION_HOOK(sub_826FF030, DrawIndexedPrimitive); GUEST_FUNCTION_HOOK(sub_826FE5C0, DrawPrimitiveUP); GUEST_FUNCTION_HOOK(sub_82547118, CreateVertexDeclaration); GUEST_FUNCTION_HOOK(sub_825470F8, SetVertexDeclaration); GUEST_FUNCTION_HOOK(sub_82548700, CreateVertexShader); GUEST_FUNCTION_HOOK(sub_82546EE0, SetVertexShader); GUEST_FUNCTION_HOOK(sub_82543918, SetStreamSource); GUEST_FUNCTION_HOOK(sub_82543AC8, SetIndices); GUEST_FUNCTION_HOOK(sub_82548608, CreatePixelShader); GUEST_FUNCTION_HOOK(sub_82546BD8, SetPixelShader); GUEST_FUNCTION_HOOK(sub_82636BF8, BeginConditionalSurvey); GUEST_FUNCTION_HOOK(sub_82636C08, EndConditionalSurvey); GUEST_FUNCTION_HOOK(sub_82636C10, BeginConditionalRendering); GUEST_FUNCTION_HOOK(sub_82636C18, EndConditionalRendering); GUEST_FUNCTION_HOOK(sub_8253B760, IsSet); GUEST_FUNCTION_HOOK(sub_82543CF0, SetClipPlane); GUEST_FUNCTION_HOOK(sub_82541A78, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541AC0, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541460, SetRenderState); GUEST_FUNCTION_HOOK(sub_825415C0, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541650, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541400, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541AF0, SetRenderState); GUEST_FUNCTION_HOOK(sub_825418C8, SetRenderState); GUEST_FUNCTION_HOOK(sub_825414A0, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541530, SetRenderState); GUEST_FUNCTION_HOOK(sub_82543ED0, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541E90, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541F58, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541750, SetRenderState); GUEST_FUNCTION_HOOK(sub_825417C0, SetRenderState); GUEST_FUNCTION_HOOK(sub_825416E0, SetRenderState); GUEST_FUNCTION_HOOK(sub_82542050, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541B30, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541B78, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541BE8, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541C28, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541C68, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541BB8, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541D78, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541D98, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541DB8, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541CC8, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541D08, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541D48, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541C98, SetRenderState); GUEST_FUNCTION_HOOK(sub_82541E38, SetRenderState); int GetType(GuestResource* resource) { if (resource->type == ResourceType::Texture) return 3; if (resource->type == ResourceType::VolumeTexture) return 17; if (resource->type == ResourceType::ArrayTexture) return 19; LOGF_WARNING("unknown resource type {:d}!", (int32_t)resource->type); __builtin_trap(); return 0; } GUEST_FUNCTION_HOOK(sub_8253AE08, GetType); // Game asks about the size of surface to check if it needs to be tiled. // Because EDRAM has only 10MB, if size is more than 1024, then it enables tiling. // We return 0 to always disable tiling. int SurfaceSize(uint32_t width, uint32_t height, uint32_t format, uint32_t multisampleLevel) { return 0; } GUEST_FUNCTION_HOOK(sub_82538D60, SurfaceSize); GUEST_FUNCTION_HOOK(sub_82656B68, MakePictureData); GUEST_FUNCTION_HOOK(sub_82656DB8, MakePictureData); // GUEST_FUNCTION_HOOK(sub_82E9EE38, SetResolution); GUEST_FUNCTION_HOOK(sub_82736178, ScreenShaderInit); GUEST_FUNCTION_STUB(sub_8253EB38); GUEST_FUNCTION_STUB(sub_8253EB78); GUEST_FUNCTION_STUB(sub_82543BE0); // SetGammaRamp GUEST_FUNCTION_STUB(sub_82543C68); // SetGammaRamp GUEST_FUNCTION_STUB(sub_82547278); // Set shader allocation GUEST_FUNCTION_STUB(sub_8272FAD0); GUEST_FUNCTION_STUB(sub_82558E00); GUEST_FUNCTION_STUB(sub_82559928); GUEST_FUNCTION_STUB(sub_82559C18); GUEST_FUNCTION_STUB(sub_82700C18); // D3DXFilterTexture GUEST_FUNCTION_STUB(sub_8253EAE0); GUEST_FUNCTION_STUB(sub_8254D598); // BeginConditional GUEST_FUNCTION_STUB(sub_8254D7B0); // BeginConditional GUEST_FUNCTION_STUB(sub_8254D9D0); // BeginConditional GUEST_FUNCTION_STUB(sub_8254DB90); // BeginConditional GUEST_FUNCTION_STUB(sub_8254DD40); // SetScreenExtentQueryMode struct Rect { be x1; be y1; be x2; be y2; }; struct RESOLVE_PARAMS { be format; be unk; be format2; }; int D3DDevice_BeginTiling(GuestDevice* device, uint32_t flags, uint32_t count, Rect* pTileRects, be* pClearColor, float clearZ, uint32_t clearStencil) { Clear(device, 0x3F, 0, pClearColor, clearZ, clearStencil); return 0; } GUEST_FUNCTION_HOOK(sub_82558F88, D3DDevice_BeginTiling); int D3DDevice_EndTiling(GuestDevice* device, uint32_t flags, Rect* pResolveRects, GuestTexture* pDestTexture, be* pClearColor, float clearZ, uint32_t clearStencil, RESOLVE_PARAMS* resolveParams) { if (pDestTexture) { StretchRect(device, flags, 0, pDestTexture, 0, 0, 0); } return 0; } GUEST_FUNCTION_HOOK(sub_82559480, D3DDevice_EndTiling); int D3DDevice_BeginShaderConstantF4(GuestDevice* device, uint32_t isPixelShader, uint32_t startRegister, be* cachedConstantData, be* writeCombinedConstantData, uint32_t vectorCount) { uint32_t* constants; be* dirtyFlags; if (isPixelShader) { constants = &device->pixelShaderFloatConstants[startRegister * 4]; dirtyFlags = &device->dirtyFlags[1]; } else { constants = &device->vertexShaderFloatConstants[startRegister * 4]; dirtyFlags = &device->dirtyFlags[0]; } const uint32_t addr = g_memory.MapVirtual(constants); *cachedConstantData = addr; *writeCombinedConstantData = addr; const uint32_t startBit = startRegister >> 2; const uint32_t endBit = (startRegister + vectorCount - 1) >> 2; const uint64_t dirtyFlag = ~0ull << startBit >> startBit >> (63 - endBit) << (63 - endBit); *dirtyFlags = dirtyFlags->get() | dirtyFlag; return 0; } GUEST_FUNCTION_HOOK(sub_825466E8, D3DDevice_BeginShaderConstantF4); ================================================ FILE: MarathonRecomp/gpu/video.h ================================================ #pragma once //#define ASYNC_PSO_DEBUG /////////////////////////////////////////////////////////////////////#define PSO_CACHING //#define PSO_CACHING_CLEANUP #include #include #include #define D3DCLEAR_TARGET 0x1 #define D3DCLEAR_ZBUFFER 0x10 #define D3DCLEAR_STENCIL 0x20 // TODO: remove #define SPEC_CONSTANT_ALPHA_TO_COVERAGE (1 << 3) #define SPEC_CONSTANT_REVERSE_Z (1 << 4) #define LOAD_ZSTD_TEXTURE(name) LoadTexture(decompressZstd(name, name##_uncompressed_size).get(), name##_uncompressed_size) using namespace plume; struct Video { static inline uint32_t s_viewportWidth; static inline uint32_t s_viewportHeight; static bool CreateHostDevice(const char *sdlVideoDriver, bool graphicsApiRetry); static void WaitOnSwapChain(); static void Present(); static void StartPipelinePrecompilation(); static void WaitForGPU(); static void ComputeViewportDimensions(); }; enum class Backend { VULKAN, D3D12, METAL }; struct GuestSamplerState { be data[6]; }; struct GuestDevice { be dirtyFlags[7]; // 0x0 + 0x38 be setRenderStateFunctions[0x61]; // 0x38 + 0x184 uint32_t setSamplerStateFunctions[0x14]; // 0x1BC + 0x50 uint8_t padding20C[0x1F4]; // 0x20C + 0x1F4 GuestSamplerState samplerStates[0x20]; // 0x400 + 0x300 uint32_t vertexShaderFloatConstants[0x400]; // 0x700 + 0x1000 uint32_t pixelShaderFloatConstants[0x400]; // 0x1700 + 0x1000 be vertexShaderBoolConstants[0x4]; // 0x2700 + 0x10 be pixelShaderBoolConstants[0x4]; // 0x2710 + 0x10 uint8_t padding2720[0x5F0]; // 0x2720 + 0x5F0 be vertexDeclaration; // 0x2D10 + 0x4 uint8_t padding2D14[0x344]; // 0x2D14 + 0x344 struct { be x; be y; be width; be height; be minZ; be maxZ; } viewport; // 0x3058 + 0x18 uint8_t padding3070[0x1F90]; // 0x3070 + 0x1F90 }; static_assert(sizeof(GuestDevice) == 0x5000); enum class ResourceType { Texture, VolumeTexture, ArrayTexture, VertexBuffer, IndexBuffer, RenderTarget, DepthStencil, VertexDeclaration, VertexShader, PixelShader }; struct GuestResource { uint32_t unused = 0; be refCount = 1; ResourceType type; GuestResource(ResourceType type) : type(type) { } void AddRef() { std::atomic_ref atomicRef(refCount.value); uint32_t originalValue, incrementedValue; do { originalValue = refCount.value; incrementedValue = ByteSwap(ByteSwap(originalValue) + 1); } while (!atomicRef.compare_exchange_weak(originalValue, incrementedValue)); } void Release() { std::atomic_ref atomicRef(refCount.value); uint32_t originalValue, decrementedValue; do { originalValue = refCount.value; decrementedValue = ByteSwap(ByteSwap(originalValue) - 1); } while (!atomicRef.compare_exchange_weak(originalValue, decrementedValue)); // Normally we are supposed to release here, so only use this // function when you know you won't be the one destructing it. } }; enum GuestFormat { D3DFMT_A16B16G16R16F = 0x1A22AB60, D3DFMT_A16B16G16R16F_2 = 0x1A2201BF, D3DFMT_A16B16G16R16F_EXPAND = 0x1A22AB5D, D3DFMT_DXT1 = 0x1A200152, D3DFMT_DXT4 = 0x1A200154, D3DFMT_A8B8G8R8 = 0x1A200186, D3DFMT_A8R8G8B8 = 0x18280186, D3DFMT_LIN_A8R8G8B8 = 0x18280086, D3DFMT_D24FS8 = 0x1A220197, D3DFMT_D24S8 = 0x2D200196, D3DFMT_R32F = 0x2DA2ABA4, D3DFMT_G16R16F = 0x2D22AB9F, D3DFMT_G16R16F_2 = 0x2D20AB8D, D3DFMT_INDEX16 = 1, D3DFMT_INDEX32 = 6, D3DFMT_A8 = 0x4900102, D3DFMT_L8 = 0x28000102, D3DFMT_L8_2 = 0x28000002, D3DFMT_X8R8G8B8 = 0x28280086, D3DFMT_LE_X8R8G8B8 = 0x28280106, D3DFMT_UNKNOWN = 0xFFFFFFFF }; struct GuestBaseTexture : GuestResource { std::unique_ptr textureHolder; RenderTexture* texture = nullptr; std::unique_ptr textureView; uint32_t width = 0; uint32_t height = 0; RenderFormat format = RenderFormat::UNKNOWN; uint32_t descriptorIndex = 0; RenderTextureLayout layout = RenderTextureLayout::UNKNOWN; GuestBaseTexture(ResourceType type) : GuestResource(type) { } }; // Texture/VolumeTexture struct GuestTexture : GuestBaseTexture { uint32_t depth = 0; uint32_t mipLevels = 1; RenderTextureViewDimension viewDimension = RenderTextureViewDimension::UNKNOWN; void* mappedMemory = nullptr; ankerl::unordered_dense::map> framebuffers; std::vector> framebufferViews; std::unique_ptr patchedTexture; struct GuestSurface* sourceSurface = nullptr; }; struct GuestLockedRect { be pitch; be bits; }; struct GuestBufferDesc { be format; be type; be usage; be pool; be size; be fvf; }; // VertexBuffer/IndexBuffer struct GuestBuffer : GuestResource { std::unique_ptr buffer; void* mappedMemory = nullptr; uint32_t dataSize = 0; RenderFormat format = RenderFormat::UNKNOWN; uint32_t guestFormat = 0; bool lockedReadOnly = false; }; struct GuestSurfaceDesc { be format; be type; be usage; be pool; be multiSampleType; be multiSampleQuality; be width; be height; }; struct GuestSurfaceCreateParams { be base; be hzBase; be colorExpBias; }; // RenderTarget/DepthStencil struct GuestSurface : GuestBaseTexture { uint32_t guestFormat = 0; ankerl::unordered_dense::map> framebuffers; RenderSampleCounts sampleCount = RenderSampleCount::COUNT_1; ankerl::unordered_dense::map destinationTextures; bool wasCached = false; }; enum GuestDeclType { D3DDECLTYPE_FLOAT1 = 0x2C83A4, D3DDECLTYPE_FLOAT2 = 0x2C23A5, D3DDECLTYPE_FLOAT3 = 0x2A23B9, D3DDECLTYPE_FLOAT4 = 0x1A23A6, D3DDECLTYPE_D3DCOLOR = 0x182886, D3DDECLTYPE_UBYTE4 = 0x1A2286, D3DDECLTYPE_UBYTE4_2 = 0x1A2386, D3DDECLTYPE_SHORT2 = 0x2C2359, D3DDECLTYPE_SHORT4 = 0x1A235A, D3DDECLTYPE_UBYTE4N = 0x1A2086, D3DDECLTYPE_UBYTE4N_2 = 0x1A2186, D3DDECLTYPE_SHORT2N = 0x2C2159, D3DDECLTYPE_SHORT4N = 0x1A215A, D3DDECLTYPE_USHORT2N = 0x2C2059, D3DDECLTYPE_USHORT4N = 0x1A205A, D3DDECLTYPE_UINT1 = 0x2C82A1, D3DDECLTYPE_UDEC3 = 0x2A2287, D3DDECLTYPE_DEC3N = 0x2A2187, D3DDECLTYPE_DEC3N_2 = 0x2A2190, D3DDECLTYPE_DEC3N_3 = 0x2A2390, D3DDECLTYPE_FLOAT16_2 = 0x2C235F, D3DDECLTYPE_FLOAT16_4 = 0x1A2360, D3DDECLTYPE_UNUSED = 0xFFFFFFFF }; enum GuestDeclUsage { D3DDECLUSAGE_POSITION = 0, D3DDECLUSAGE_BLENDWEIGHT = 1, D3DDECLUSAGE_BLENDINDICES = 2, D3DDECLUSAGE_NORMAL = 3, D3DDECLUSAGE_PSIZE = 4, D3DDECLUSAGE_TEXCOORD = 5, D3DDECLUSAGE_TANGENT = 6, D3DDECLUSAGE_BINORMAL = 7, D3DDECLUSAGE_TESSFACTOR = 8, D3DDECLUSAGE_POSITIONT = 9, D3DDECLUSAGE_COLOR = 10, D3DDECLUSAGE_FOG = 11, D3DDECLUSAGE_DEPTH = 12, D3DDECLUSAGE_SAMPLE = 13 }; struct GuestVertexElement { be stream; be offset; be type; uint8_t method; uint8_t usage; uint8_t usageIndex; uint8_t padding; }; #define D3DDECL_END() { 255, 0, 0xFFFFFFFF, 0, 0, 0 } struct GuestVertexDeclaration : GuestResource { XXH64_hash_t hash = 0; std::unique_ptr inputElements; std::unique_ptr vertexElements; uint32_t inputElementCount = 0; uint32_t vertexElementCount = 0; uint32_t swappedTexcoords = 0; uint32_t swappedNormals = 0; uint32_t swappedBinormals = 0; uint32_t swappedTangents = 0; uint32_t swappedBlendWeights = 0; bool hasR11G11B10Normal = false; bool vertexStreams[16]{}; uint32_t indexVertexStream = 0; }; // VertexShader/PixelShader struct GuestShader : GuestResource { Mutex mutex; std::unique_ptr shader; struct ShaderCacheEntry* shaderCacheEntry = nullptr; ankerl::unordered_dense::map> linkedShaders; #ifdef MARATHON_RECOMP_D3D12 std::vector> shaderBlobs; ComPtr libraryBlob; #endif #ifdef ASYNC_PSO_DEBUG const char* name = ""; #endif }; struct GuestViewport { be x; be y; be width; be height; be minZ; be maxZ; }; struct GuestRect { be left; be top; be right; be bottom; }; enum GuestRenderState { D3DRS_ZENABLE = 40, D3DRS_ZFUNC = 44, D3DRS_ZWRITEENABLE = 48, D3DRS_CULLMODE = 56, D3DRS_ALPHABLENDENABLE = 60, D3DRS_SRCBLEND = 72, D3DRS_DESTBLEND = 76, D3DRS_BLENDOP = 80, D3DRS_SRCBLENDALPHA = 84, D3DRS_DESTBLENDALPHA = 88, D3DRS_BLENDOPALPHA = 92, D3DRS_ALPHATESTENABLE = 96, D3DRS_ALPHAREF = 100, D3DRS_STENCILENABLE = 108, D3DRS_TWOSIDEDSTENCILMODE = 112, D3DRS_STENCILFAIL = 116, D3DRS_STENCILZFAIL = 120, D3DRS_STENCILPASS = 124, D3DRS_STENCILFUNC = 128, D3DRS_STENCILREF = 132, D3DRS_STENCILMASK = 136, D3DRS_STENCILWRITEMASK = 140, D3DRS_CCW_STENCILFAIL = 144, D3DRS_CCW_STENCILZFAIL = 148, D3DRS_CCW_STENCILPASS = 152, D3DRS_CCW_STENCILFUNC = 156, D3DRS_CLIPPLANEENABLE = 172, D3DRS_SCISSORTESTENABLE = 200, D3DRS_SLOPESCALEDEPTHBIAS = 204, D3DRS_DEPTHBIAS = 208, D3DRS_COLORWRITEENABLE = 212 }; enum GuestCullMode { D3DCULL_NONE_CCW = 0, D3DCULL_FRONT_CCW = 1, D3DCULL_BACK_CCW = 2, D3DCULL_NONE_CW = 4, D3DCULL_FRONT_CW = 5, D3DCULL_BACK_CW = 6 }; enum GuestBlendMode { D3DBLEND_ZERO = 0, D3DBLEND_ONE = 1, D3DBLEND_SRCCOLOR = 4, D3DBLEND_INVSRCCOLOR = 5, D3DBLEND_SRCALPHA = 6, D3DBLEND_INVSRCALPHA = 7, D3DBLEND_DESTCOLOR = 8, D3DBLEND_INVDESTCOLOR = 9, D3DBLEND_DESTALPHA = 10, D3DBLEND_INVDESTALPHA = 11 }; enum GuestBlendOp { D3DBLENDOP_ADD = 0, D3DBLENDOP_SUBTRACT = 1, D3DBLENDOP_MIN = 2, D3DBLENDOP_MAX = 3, D3DBLENDOP_REVSUBTRACT = 4 }; enum GuestCmpFunc { D3DCMP_NEVER = 0, D3DCMP_LESS = 1, D3DCMP_EQUAL = 2, D3DCMP_LESSEQUAL = 3, D3DCMP_GREATER = 4, D3DCMP_NOTEQUAL = 5, D3DCMP_GREATEREQUAL = 6, D3DCMP_ALWAYS = 7 }; enum GuestStencilOp { D3DSTENCILOP_KEEP = 0, D3DSTENCILOP_ZERO = 1, D3DSTENCILOP_REPLACE = 2, D3DSTENCILOP_INCRSAT = 3, D3DSTENCILOP_DECRSAT = 4, D3DSTENCILOP_INVERT = 5, D3DSTENCILOP_INCR = 6, D3DSTENCILOP_DECR = 7 }; enum GuestPrimitiveType { D3DPT_POINTLIST = 1, D3DPT_LINELIST = 2, D3DPT_LINESTRIP = 3, D3DPT_TRIANGLELIST = 4, D3DPT_TRIANGLEFAN = 5, D3DPT_TRIANGLESTRIP = 6, D3DPT_QUADLIST = 13 }; enum GuestTextureFilterType { D3DTEXF_POINT = 0, D3DTEXF_LINEAR = 1, D3DTEXF_NONE = 2 }; enum GuestTextureAddress { D3DTADDRESS_WRAP = 0, D3DTADDRESS_MIRROR = 1, D3DTADDRESS_CLAMP = 2, D3DTADDRESS_MIRRORONCE = 3, D3DTADDRESS_BORDER = 6 }; inline bool g_needsResize; extern std::unique_ptr LoadTexture(const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping = RenderComponentMapping()); extern void VideoConfigValueChangedCallback(class IConfigDef* config); ================================================ FILE: MarathonRecomp/hid/driver/sdl_hid.cpp ================================================ #include #include #include #include #include #include #include #include #define TRANSLATE_INPUT(S, X) SDL_GameControllerGetButton(controller, S) << FirstBitLow(X) #define VIBRATION_TIMEOUT_MS 5000 class Controller { public: SDL_GameController* controller{}; SDL_Joystick* joystick{}; SDL_JoystickID id{ -1 }; XAMINPUT_GAMEPAD state{}; XAMINPUT_VIBRATION vibration{ 0, 0 }; int index{}; Controller() = default; explicit Controller(int index) : Controller(SDL_GameControllerOpen(index)) { this->index = index; } Controller(SDL_GameController* controller) : controller(controller) { if (!controller) return; joystick = SDL_GameControllerGetJoystick(controller); id = SDL_JoystickInstanceID(joystick); } SDL_GameControllerType GetControllerType() const { return SDL_GameControllerGetType(controller); } hid::EInputDevice GetInputDevice() const { switch (GetControllerType()) { case SDL_CONTROLLER_TYPE_PS3: case SDL_CONTROLLER_TYPE_PS4: case SDL_CONTROLLER_TYPE_PS5: return hid::EInputDevice::PlayStation; case SDL_CONTROLLER_TYPE_XBOX360: case SDL_CONTROLLER_TYPE_XBOXONE: return hid::EInputDevice::Xbox; default: return hid::EInputDevice::Unknown; } } const char* GetControllerName() const { auto result = SDL_GameControllerName(controller); if (!result) return "Unknown Device"; return result; } void Close() { if (!controller) return; SDL_GameControllerClose(controller); controller = nullptr; joystick = nullptr; id = -1; } bool CanPoll() { return controller; } void PollAxis() { if (!CanPoll()) return; auto& pad = state; pad.sThumbLX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); pad.sThumbLY = ~SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); pad.sThumbRX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); pad.sThumbRY = ~SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); pad.bLeftTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) >> 7; pad.bRightTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) >> 7; } void Poll() { if (!CanPoll()) return; auto& pad = state; pad.wButtons = 0; pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_UP, XAMINPUT_GAMEPAD_DPAD_UP); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_DOWN, XAMINPUT_GAMEPAD_DPAD_DOWN); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_LEFT, XAMINPUT_GAMEPAD_DPAD_LEFT); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, XAMINPUT_GAMEPAD_DPAD_RIGHT); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_START, XAMINPUT_GAMEPAD_START); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_BACK, XAMINPUT_GAMEPAD_BACK); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_TOUCHPAD, XAMINPUT_GAMEPAD_BACK); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_LEFTSTICK, XAMINPUT_GAMEPAD_LEFT_THUMB); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_RIGHTSTICK, XAMINPUT_GAMEPAD_RIGHT_THUMB); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, XAMINPUT_GAMEPAD_LEFT_SHOULDER); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, XAMINPUT_GAMEPAD_RIGHT_SHOULDER); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_A, XAMINPUT_GAMEPAD_A); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_B, XAMINPUT_GAMEPAD_B); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_X, XAMINPUT_GAMEPAD_X); pad.wButtons |= TRANSLATE_INPUT(SDL_CONTROLLER_BUTTON_Y, XAMINPUT_GAMEPAD_Y); } void SetVibration(const XAMINPUT_VIBRATION& vibration) { if (!CanPoll()) return; this->vibration = vibration; SDL_GameControllerRumble(controller, vibration.wLeftMotorSpeed * 256, vibration.wRightMotorSpeed * 256, VIBRATION_TIMEOUT_MS); } void SetLED(const uint8_t r, const uint8_t g, const uint8_t b) const { SDL_GameControllerSetLED(controller, r, g, b); } }; std::array g_controllers; Controller* g_activeController; inline Controller* EnsureController(uint32_t dwUserIndex) { if (!g_controllers[dwUserIndex].controller) return nullptr; return &g_controllers[dwUserIndex]; } inline size_t FindFreeController() { for (size_t i = 0; i < g_controllers.size(); i++) { if (!g_controllers[i].controller) return i; } return -1; } inline Controller* FindController(int which) { for (auto& controller : g_controllers) { if (controller.id == which) return &controller; } return nullptr; } static void SetControllerInputDevice(Controller* controller) { g_activeController = controller; if (App::s_isLoading) return; hid::g_inputDevice = controller->GetInputDevice(); hid::g_inputDeviceController = hid::g_inputDevice; auto controllerType = (hid::EInputDeviceExplicit)controller->GetControllerType(); auto controllerName = controller->GetControllerName(); // Only proceed if the controller type changes. if (hid::g_inputDeviceExplicit != controllerType) { hid::g_inputDeviceExplicit = controllerType; if (controllerType == hid::EInputDeviceExplicit::Unknown) { LOGFN("Detected controller: {} (Unknown Controller Type)", controllerName); } else { LOGFN("Detected controller: {}", controllerName); } } } static void SetControllerTimeOfDayLED(Controller& controller, EPlayerCharacter player) { uint8_t r, g, b; // TODO: Per-character colors switch (player) { case EPlayerCharacter::Sonic: break; case EPlayerCharacter::Shadow: break; case EPlayerCharacter::Silver: break; case EPlayerCharacter::Blaze: break; case EPlayerCharacter::Amy: break; case EPlayerCharacter::Tails: break; case EPlayerCharacter::Rouge: break; case EPlayerCharacter::Knuckles: break; } r = 0; g = 37; b = 184; controller.SetLED(r, g, b); } int HID_OnSDLEvent(void*, SDL_Event* event) { switch (event->type) { case SDL_CONTROLLERDEVICEADDED: { const auto freeIndex = FindFreeController(); if (freeIndex != -1) { auto controller = Controller(event->cdevice.which); g_controllers[freeIndex] = controller; SetControllerTimeOfDayLED(controller, App::s_playerCharacter); } break; } case SDL_CONTROLLERDEVICEREMOVED: { auto* controller = FindController(event->cdevice.which); if (controller) controller->Close(); break; } case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERAXISMOTION: case SDL_CONTROLLERTOUCHPADDOWN: { auto* controller = FindController(event->cdevice.which); if (!controller) break; if (event->type == SDL_CONTROLLERAXISMOTION) { if (abs(event->caxis.value) > 8000) { SDL_ShowCursor(SDL_DISABLE); SetControllerInputDevice(controller); } controller->PollAxis(); } else { SDL_ShowCursor(SDL_DISABLE); SetControllerInputDevice(controller); controller->Poll(); } break; } case SDL_KEYDOWN: case SDL_KEYUP: hid::g_inputDevice = hid::EInputDevice::Keyboard; break; case SDL_MOUSEMOTION: case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { if (!GameWindow::IsFullscreen() || GameWindow::s_isFullscreenCursorVisible) SDL_ShowCursor(SDL_ENABLE); hid::g_inputDevice = hid::EInputDevice::Mouse; break; } case SDL_WINDOWEVENT: { if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) { // Stop vibrating controllers on focus lost. for (auto& controller : g_controllers) controller.SetVibration({ 0, 0 }); } break; } case SDL_USER_PLAYER_CHAR: { for (auto& controller : g_controllers) SetControllerTimeOfDayLED(controller, static_cast(event->user.code)); break; } } return 0; } void hid::Init() { SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS3, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAMDECK, "1"); SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1"); SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); // Uses Button Labels. This hint is disabled for Nintendo Controllers. SDL_InitSubSystem(SDL_INIT_EVENTS); SDL_AddEventWatch(HID_OnSDLEvent, nullptr); SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); // Load controller mappings from SDL_GameControllerDB if (int mappings = SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); mappings > 0) { LOGFN("Loaded {} controller mapping(s) from SDL_GameControllerDB ({})", mappings, "gamecontrollerdb.txt"); } } uint32_t hid::GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState) { static uint32_t packet; if (!pState) return ERROR_BAD_ARGUMENTS; memset(pState, 0, sizeof(*pState)); pState->dwPacketNumber = packet++; if (!g_activeController) return ERROR_DEVICE_NOT_CONNECTED; pState->Gamepad = g_activeController->state; return ERROR_SUCCESS; } uint32_t hid::SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration) { if (!pVibration) return ERROR_BAD_ARGUMENTS; if (!g_activeController) return ERROR_DEVICE_NOT_CONNECTED; g_activeController->SetVibration(*pVibration); return ERROR_SUCCESS; } uint32_t hid::GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps) { if (!pCaps) return ERROR_BAD_ARGUMENTS; if (!g_activeController) return ERROR_DEVICE_NOT_CONNECTED; memset(pCaps, 0, sizeof(*pCaps)); pCaps->Type = XAMINPUT_DEVTYPE_GAMEPAD; pCaps->SubType = XAMINPUT_DEVSUBTYPE_GAMEPAD; // TODO: other types? pCaps->Flags = 0; pCaps->Gamepad = g_activeController->state; pCaps->Vibration = g_activeController->vibration; return ERROR_SUCCESS; } ================================================ FILE: MarathonRecomp/hid/hid.cpp ================================================ #include "hid.h" #include #include hid::EInputDevice hid::g_inputDevice; hid::EInputDevice hid::g_inputDeviceController; hid::EInputDeviceExplicit hid::g_inputDeviceExplicit; uint16_t hid::g_prohibitedButtons; bool hid::g_isLeftStickProhibited; bool hid::g_isRightStickProhibited; void hid::SetProhibitedInputs(uint16_t wButtons, bool leftStick, bool rightStick) { hid::g_prohibitedButtons = wButtons; hid::g_isLeftStickProhibited = leftStick; hid::g_isRightStickProhibited = rightStick; } bool hid::IsInputAllowed() { return GameWindow::s_isFocused || Config::AllowBackgroundInput; } bool hid::IsInputDeviceController() { return hid::g_inputDevice != hid::EInputDevice::Keyboard && hid::g_inputDevice != hid::EInputDevice::Mouse; } ================================================ FILE: MarathonRecomp/hid/hid.h ================================================ #pragma once namespace hid { enum class EInputDevice { Unknown, Keyboard, Mouse, Xbox, PlayStation }; enum class EInputDeviceExplicit { Unknown, Xbox360, XboxOne, DualShock3, DualShock4, SwitchPro, Virtual, DualSense, Luna, Stadia, NvShield, SwitchJCLeft, SwitchJCRight, SwitchJCPair }; extern EInputDevice g_inputDevice; extern EInputDevice g_inputDeviceController; extern EInputDeviceExplicit g_inputDeviceExplicit; extern uint16_t g_prohibitedButtons; extern bool g_isLeftStickProhibited; extern bool g_isRightStickProhibited; void Init(); uint32_t GetState(uint32_t dwUserIndex, XAMINPUT_STATE* pState); uint32_t SetState(uint32_t dwUserIndex, XAMINPUT_VIBRATION* pVibration); uint32_t GetCapabilities(uint32_t dwUserIndex, XAMINPUT_CAPABILITIES* pCaps); void SetProhibitedInputs(uint16_t wButtons = 0, bool leftStick = false, bool rightStick = false); bool IsInputAllowed(); bool IsInputDeviceController(); } ================================================ FILE: MarathonRecomp/install/directory_file_system.h ================================================ #pragma once #include #include "virtual_file_system.h" struct DirectoryFileSystem : VirtualFileSystem { std::filesystem::path directoryPath; std::string name; DirectoryFileSystem(const std::filesystem::path &directoryPath) { this->directoryPath = directoryPath; name = (const char *)(directoryPath.filename().u8string().data()); } bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override { std::ifstream fileStream(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str()))), std::ios::binary); if (fileStream.is_open()) { fileStream.read((char *)(fileData), fileDataMaxByteCount); return !fileStream.bad(); } else { return false; } } size_t getSize(const std::string &path) const override { std::error_code ec; size_t fileSize = std::filesystem::file_size(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str()))), ec); if (!ec) { return fileSize; } else { return 0; } } bool exists(const std::string &path) const override { if (path.empty()) { return false; } return std::filesystem::exists(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str())))); } const std::string &getName() const override { return name; } static std::unique_ptr create(const std::filesystem::path &directoryPath) { if (std::filesystem::exists(directoryPath)) { return std::make_unique(directoryPath); } else { return nullptr; } } }; ================================================ FILE: MarathonRecomp/install/hashes/episode_amigo.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t EpisodeAmigoHashes[]; extern const std::pair EpisodeAmigoFiles[]; extern const size_t EpisodeAmigoFilesSize; const uint64_t EpisodeAmigoHashes[] = { 8983387072646869383ULL, }; const std::pair EpisodeAmigoFiles[] = { { "download.arc", 1 }, }; const size_t EpisodeAmigoFilesSize = std::size(EpisodeAmigoFiles); ================================================ FILE: MarathonRecomp/install/hashes/episode_amigo.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t EpisodeAmigoHashes[]; extern const std::pair EpisodeAmigoFiles[]; extern const size_t EpisodeAmigoFilesSize; ================================================ FILE: MarathonRecomp/install/hashes/episode_shadow.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t EpisodeShadowHashes[]; extern const std::pair EpisodeShadowFiles[]; extern const size_t EpisodeShadowFilesSize; const uint64_t EpisodeShadowHashes[] = { 10539656742164611611ULL, }; const std::pair EpisodeShadowFiles[] = { { "download.arc", 1 }, }; const size_t EpisodeShadowFilesSize = std::size(EpisodeShadowFiles); ================================================ FILE: MarathonRecomp/install/hashes/episode_shadow.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t EpisodeShadowHashes[]; extern const std::pair EpisodeShadowFiles[]; extern const size_t EpisodeShadowFilesSize; ================================================ FILE: MarathonRecomp/install/hashes/episode_silver.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t EpisodeSilverHashes[]; extern const std::pair EpisodeSilverFiles[]; extern const size_t EpisodeSilverFilesSize; const uint64_t EpisodeSilverHashes[] = { 12996835580564401141ULL, }; const std::pair EpisodeSilverFiles[] = { { "download.arc", 1 }, }; const size_t EpisodeSilverFilesSize = std::size(EpisodeSilverFiles); ================================================ FILE: MarathonRecomp/install/hashes/episode_silver.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t EpisodeSilverHashes[]; extern const std::pair EpisodeSilverFiles[]; extern const size_t EpisodeSilverFilesSize; ================================================ FILE: MarathonRecomp/install/hashes/episode_sonic.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t EpisodeSonicHashes[]; extern const std::pair EpisodeSonicFiles[]; extern const size_t EpisodeSonicFilesSize; const uint64_t EpisodeSonicHashes[] = { 15073381587123252911ULL, }; const std::pair EpisodeSonicFiles[] = { { "download.arc", 1 }, }; const size_t EpisodeSonicFilesSize = std::size(EpisodeSonicFiles); ================================================ FILE: MarathonRecomp/install/hashes/episode_sonic.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t EpisodeSonicHashes[]; extern const std::pair EpisodeSonicFiles[]; extern const size_t EpisodeSonicFilesSize; ================================================ FILE: MarathonRecomp/install/hashes/game.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t GameHashes[]; extern const std::pair GameFiles[]; extern const size_t GameFilesSize; const uint64_t GameHashes[] = { 8351993017922532997ULL, 10387650078945694205ULL, 15918252288161138853ULL, 5452730958083469041ULL, 14864687957236683217ULL, 4588398277622789161ULL, 1122549444656674671ULL, 5885697728922631499ULL, 15635110676280851876ULL, 3502215997315919051ULL, 3287508961237706537ULL, 2383765786859750683ULL, 18013096267158586365ULL, 5378233217385144162ULL, 8984567973277414284ULL, 4068825697758009312ULL, 8912993163194524353ULL, 6766364709494587624ULL, 5165203281591868776ULL, 3293546646423584708ULL, 510127123707132584ULL, 613421661350354339ULL, 8615725139300220084ULL, 418985277142969910ULL, 4065681052882002442ULL, 12554136121954960480ULL, 5703744011735507919ULL, 3934176164980133346ULL, 15390830978020201124ULL, 12325455430664520125ULL, 7189412297617331727ULL, 12946235816270579118ULL, 9106277541578886813ULL, 2649699473170536930ULL, 3207664458238798524ULL, 6052968602963894858ULL, 7644143475127806468ULL, 15966464809890326996ULL, 1776567654481657012ULL, 11700237696799569734ULL, 1552082824368854486ULL, 9409374462197609982ULL, 10329846675422344494ULL, 11345124900216766569ULL, 9789117714488434491ULL, 1130609446972947655ULL, 10189235726002196119ULL, 14884654433672021891ULL, 3078946837915525363ULL, 5960606366973278978ULL, 8970327760245634221ULL, 1838668412725390170ULL, 6542730983527968814ULL, 8754778863202595052ULL, 3193047768790022953ULL, 3750338081570554493ULL, 6524494994971351633ULL, 3432804745839785609ULL, 12024435344793110587ULL, 5607066874173525902ULL, 7261493453087927145ULL, 16852230790612353056ULL, 13610669732030524206ULL, 13255324417898391615ULL, 5595794218955118561ULL, 6483332957420836746ULL, 16456289603706621603ULL, 2047510546540242729ULL, 6079071265208189465ULL, 7320675267255988329ULL, 15396952465157254266ULL, 3264076318155180729ULL, 7382681621572307165ULL, 772079045529068376ULL, 14011921409579744145ULL, 12607615649685981548ULL, 4733996493025490240ULL, 10777260466079228293ULL, 14501850336834917032ULL, 17458338278051450418ULL, 12107062460763034809ULL, 17878170400214505618ULL, 804424115290410973ULL, 6020937142647651946ULL, 16436339236514980274ULL, 511862674129498780ULL, 16741000990776207374ULL, 928481540120459528ULL, 7370069239886430319ULL, 8747513997169024192ULL, 11034232791427722203ULL, 10021372195749921053ULL, 14697917391429243980ULL, 3966379819362750901ULL, 2536061930605766565ULL, 9725840255691789095ULL, 5390405305020820684ULL, 370342763391282173ULL, 2982922091176469641ULL, 5906229564509266432ULL, 3092996426725391072ULL, 4333936066623409741ULL, 2646727979847321182ULL, 195392020737512422ULL, 7523222828581704438ULL, 12895271494233636735ULL, 13352948425944629144ULL, 15394456931489523284ULL, 4701887762849620066ULL, 7627468992402108220ULL, 16634461619475414265ULL, 2171484057264913788ULL, 13610880881124644837ULL, 5922225356576124855ULL, 3305237611592946770ULL, 8305257945785296319ULL, 1141897396282710972ULL, 7259082901179380511ULL, 7909907131794486222ULL, 14594395054266605131ULL, 8911994021944730625ULL, 10902447751954560358ULL, 16985144179030585540ULL, 17696116671783294156ULL, 14454353297407543564ULL, 13355133586748309097ULL, 14777399127630626024ULL, 7930321383281062356ULL, 15610375919952488152ULL, 7797133254038529740ULL, 6265295992650116858ULL, 14579554764499604523ULL, 16328904786231236960ULL, 6311998956955426050ULL, 752940130727856220ULL, 10123616274779016716ULL, 15201238633782710748ULL, 14195309807539611921ULL, 7053636052550479527ULL, 971854982639197781ULL, 8053554397147962679ULL, 17961415202203662445ULL, 8761047933840997261ULL, 2555362181692540322ULL, 10052140421786278741ULL, 8324018035539291015ULL, 11732492012294063386ULL, 1060395877769130600ULL, 7490164146285607869ULL, 17347035194327999920ULL, 10970444186857735374ULL, 10713098883821240959ULL, 6060469365520758136ULL, 1007996608267344047ULL, 17786062467752834965ULL, 8308211700061525638ULL, 330833553673396567ULL, 14263372326684114630ULL, 1595882167377053882ULL, 860509703444667222ULL, 15350623473935087737ULL, 6003127439812853013ULL, 900736905047689077ULL, 11981871784046923216ULL, 343920880078991689ULL, 5400819071441313977ULL, 10425183265000600948ULL, 11892774542645108826ULL, 10929080142559128578ULL, 16941577763568258322ULL, 5001906873105165075ULL, 17333809535728451083ULL, 10630796073354098680ULL, 13998846740819899403ULL, 6352941851781657631ULL, 13998846740819899403ULL, 6835637137171575367ULL, 4714033806257021998ULL, 5447243784357251367ULL, 527685082572578420ULL, 16501737254297182334ULL, 3504869225508502420ULL, 18440098858438145080ULL, 15319453356909134059ULL, 834479186828377545ULL, 6129625056462738972ULL, 6385369917591838684ULL, 14150304350342962897ULL, 11842610431187615496ULL, 2800616125411869998ULL, 15511071891176753974ULL, 5059832060453959225ULL, 7886907665265515499ULL, 1712053747295426785ULL, 214536915690750489ULL, 771267855322846142ULL, 6922242629611452842ULL, 16369817751108526943ULL, 9679152833804734962ULL, 9417058359022793302ULL, 7366452427950478661ULL, 11880277920847728254ULL, 10797292878867928790ULL, 8734347318430424871ULL, 15897617231465261294ULL, 4431970558376936770ULL, 17285867978297205014ULL, 10175833537042922619ULL, 14792054015873360099ULL, 6486687770728031052ULL, 7769161069809673679ULL, 17265231622476163706ULL, 8236739112385361578ULL, 4749911919430749691ULL, 16148046852539936283ULL, 8337502132484112407ULL, 6971864254374112343ULL, 8907485454094133691ULL, 15262059634092425648ULL, 12570881619523088366ULL, 13994637630861496093ULL, 16722836027407925222ULL, 9830020327156033816ULL, 5000351185284949845ULL, 17961330976997968352ULL, 1538249057805925204ULL, 12570359742632133486ULL, 5685453696808446751ULL, 9061023056464070067ULL, 14120929683148910405ULL, 3309345275404648166ULL, 9557892360948858987ULL, 1623775785912254333ULL, 6944543130363118091ULL, 4659127017762284819ULL, 6022545066232005794ULL, 15087159717305837093ULL, 3374467977359598711ULL, 14693241743572139806ULL, 17308043008373190806ULL, 15195500724930505205ULL, 12926040652589884774ULL, 2115384034026814755ULL, 3842191154210892955ULL, 17018225194563796498ULL, 746287388239867709ULL, 6027860221594996801ULL, 17253954969921499058ULL, 12880262150502530819ULL, 1058521587021607252ULL, 4062725745426720610ULL, 3187865372728508958ULL, 16454363834208213770ULL, 9632090572553419359ULL, 7971169413093629601ULL, 5725558575239945301ULL, 11877976377918257900ULL, 2574645338640871653ULL, 3423294849739679677ULL, 13556435884917767732ULL, 9966181774430414379ULL, 207089284894156761ULL, 12875290231482699433ULL, 899740447794318627ULL, 15952018783761653845ULL, 7667824642640720291ULL, 7427886174990410944ULL, 9584074679644931198ULL, 18399567836127700676ULL, 1070491601910802140ULL, 2936342388114571501ULL, 9316632470217813398ULL, 10029658582354536011ULL, 5748174535284053019ULL, 17999598461353987592ULL, 8745706138394892546ULL, 17615441877518324607ULL, 7403692096756338831ULL, 5165279385045870017ULL, 7909554987934377177ULL, 6336194158006475280ULL, 14111472374585243957ULL, 7348584054841674494ULL, 12006092364105360437ULL, 9249372970899913977ULL, 7104849328589122117ULL, 18369221892485385040ULL, 9571432625200090895ULL, 10535965759761355206ULL, 9665537130075426856ULL, 638897479820092399ULL, 12983706327448916459ULL, 14778716265260637324ULL, 11956873822209916526ULL, 13065206822551641159ULL, 5491159551080240894ULL, 13997616488141505830ULL, 16702756346708898139ULL, 1867885498002502378ULL, 13370540394532730625ULL, 11937737989641567504ULL, 3526631081139064372ULL, 10819826954173354016ULL, 5853110784898773696ULL, 2516611224077149082ULL, 114795980703283674ULL, 12005404529790652727ULL, 4171348542926422925ULL, 7703916159985659066ULL, 16823633804314484449ULL, 11086174287032770643ULL, 3793883246011577017ULL, 4819515149424157896ULL, 11928898683278395009ULL, 1568936456297772289ULL, 4735887039752312668ULL, 928744353812847374ULL, 7344657760999837484ULL, 9654120138010048629ULL, 13757300835340009887ULL, 11089494060458697445ULL, 12553853654406470896ULL, 15635891577654601472ULL, 8832028658029497452ULL, 8205588355149874384ULL, 13342439941530128638ULL, 12187280490095261703ULL, 8067383714071535925ULL, 16912593934876692659ULL, 12422644831702678416ULL, 18166397544234961082ULL, 2369882245955732194ULL, 11796726989164444146ULL, 17378717075346097540ULL, 3525883291478512256ULL, 848367955390211596ULL, 2006459348208063059ULL, 16343958618294069305ULL, 2863823323437208997ULL, 18251696668108646564ULL, 7337996130014577039ULL, 11892240709914127526ULL, 7495221918696414431ULL, 303113381492728210ULL, 15233237385859756314ULL, 12685319337324069157ULL, 12493636589782781733ULL, 1775301817975206056ULL, 13508307245017014997ULL, 3485527330376610512ULL, 4162379715913570674ULL, 16017879055605648063ULL, 13172744848535059982ULL, 15602355459393925084ULL, 15012633456961780659ULL, 10376254439602112165ULL, 15385597517216944899ULL, 2814637485477153397ULL, 9198964485540049496ULL, 6676333337772334082ULL, 5297159505961780773ULL, 17530653359255725303ULL, 8695324981480619758ULL, 13587411812706250597ULL, 10261506773264614466ULL, 14050039474732441752ULL, 8056669683552925742ULL, 1168931705088860849ULL, 10993595581236313885ULL, 3065620036691951867ULL, 10128519537204251112ULL, 1838506088752454580ULL, 4835256023845164616ULL, 709651759689231731ULL, 1525065980259613298ULL, 79959291357791363ULL, 9208054580783979299ULL, 13902083415557906632ULL, 3473093760207127230ULL, 16203495575669269215ULL, 12779298991881188818ULL, 7272541615641733654ULL, 15345744604780547172ULL, 13095756032997687824ULL, 2204833361668625529ULL, 10724173582475816957ULL, 15545048778036038900ULL, 9978794013812654518ULL, 856818290265354444ULL, 2889506655662089425ULL, 14115296302232803296ULL, 7563196760068723263ULL, 8485385394068311144ULL, 12049485316948913650ULL, 5663655475981455388ULL, 16537944248429051871ULL, 17781892274573073152ULL, 13494987947427383778ULL, 5716962659307098881ULL, 4275442129747448757ULL, 18135442194652572907ULL, 8900801549027438691ULL, 11963114502409307570ULL, 10062142025419647432ULL, 5895652008077875549ULL, 4631335624881002097ULL, 4662542499424321865ULL, 3358757962956692588ULL, 15114372007040201893ULL, 4440706939232180849ULL, 17995021598399256656ULL, 8127498309758732261ULL, 521151505841430304ULL, 1396322723480197038ULL, 11035329645669405883ULL, 1757815837875818655ULL, 8873490651245396144ULL, 10483150650916546522ULL, 4274864912807753567ULL, 5869008965405724563ULL, 7792416540993860030ULL, 14931034717213510044ULL, 9484948581386755200ULL, 5761228058690313011ULL, 9284504778112732092ULL, 6633585134868883126ULL, 12292738352984717287ULL, 10902622584257963897ULL, 628681741957641146ULL, 12079448067121759617ULL, 10760656019193445306ULL, 3672778528153768138ULL, 3390670181688366098ULL, 13267165257124597242ULL, 10270373981357709347ULL, 7301519417989144198ULL, 13989241316996978224ULL, 13426931212772572842ULL, 6444751773467579067ULL, 14940068127089162330ULL, 13319217303702988621ULL, 3363734170122156520ULL, 2580685914354674873ULL, 12722851617482606977ULL, 4906193330347601496ULL, 13111870407175695021ULL, 13743530607964923338ULL, 18123043232206904816ULL, 12225280854555501044ULL, 17007990560047949591ULL, 2698592928603177737ULL, 12191182564275140072ULL, 12208454231373372199ULL, 9830245398415199299ULL, 5424160629629644789ULL, 13968078082672584669ULL, 5863825222949994539ULL, 8648308022916788253ULL, 7240357069941765424ULL, 5946605689652378135ULL, 17017535000047952169ULL, 5171870438038426178ULL, 1244876970046738876ULL, 8465660519601025778ULL, 5504548963550724854ULL, 7771407730898950664ULL, 16368771188259720784ULL, 4311239670696750755ULL, 4968452184205841963ULL, 697097814947072955ULL, 11013488083031902958ULL, 14199829919664509577ULL, 7743310017525777353ULL, 854956437112527516ULL, 15784006904119809858ULL, 4798882063829361007ULL, 5617703021199534281ULL, 1197252000346614381ULL, 15060016762042652340ULL, 2081609199332392174ULL, 1675129038765752358ULL, 12332199727301977815ULL, 6125661215039136734ULL, 15672317145370182419ULL, 18221381779501877352ULL, 7228787152931836228ULL, 12735568401120880983ULL, 9233387875830017939ULL, 9143219540714851293ULL, 16714296060812624674ULL, 18201253524629860419ULL, 5446984105259166320ULL, 7937473934999406053ULL, 5910376975385683194ULL, 13154576458139876148ULL, 5420762873134539363ULL, 10365906532460919927ULL, 1673017889298233417ULL, 11552802984945530037ULL, 14814446083309710768ULL, 12779307538890327570ULL, 15079267257140745086ULL, 12203830292074701041ULL, 16933848102201662751ULL, 2045483273445943957ULL, 5159650813021108915ULL, 2856716663410507935ULL, 11073016866987985452ULL, 8915479933644108909ULL, 10012507498473792534ULL, 14998696283275266105ULL, 5112106394122086351ULL, 9214155745948408280ULL, 15412931815166359063ULL, 10222794371068498411ULL, 9821622687184294283ULL, 3431937847552504935ULL, 5662639919797214153ULL, 17885504976659472267ULL, 6591673573599737900ULL, 2168332678473414023ULL, 5214514320274186652ULL, 15038547246044350722ULL, 13345985432557011543ULL, 7209320308531573075ULL, 18220333491451921408ULL, 5678897429212405700ULL, 16064600085814907793ULL, 3472546797620339007ULL, 15340983014796521250ULL, 17186424670820756799ULL, 8026695432293249880ULL, 15245922278866504420ULL, 2942701409123021830ULL, 61685656663478174ULL, 17744502409497832192ULL, 597218275677198665ULL, 12070732252857882104ULL, 12577607755993540347ULL, 14577851801366563546ULL, 569424962551762664ULL, 4819743411046739820ULL, 18033373234276007025ULL, 3900231270563096763ULL, 18237347144771987933ULL, 1189635854127777866ULL, 11445790009393598516ULL, 13213457517821880611ULL, 8158543456630727814ULL, 14227771364659835707ULL, 6175861229769239237ULL, 2821859239710330492ULL, 18165223348748145981ULL, 7879500160558649018ULL, 9669144783373179736ULL, 9653381490544416935ULL, 1790491185694037868ULL, 13948351776143190799ULL, 5059832060453959225ULL, 5641623213857577086ULL, 8531873957825661008ULL, 8449674017136423031ULL, 16010761086236204726ULL, 8223340351077020328ULL, 2502506861199528149ULL, 12140564918112154349ULL, 12530299546340185083ULL, 9887057189834774931ULL, 9887057189834774931ULL, 8576898839131461099ULL, 14245745346864146954ULL, 16425537936422063455ULL, 6784902161520198595ULL, 16642584420979243102ULL, 10780548309269621176ULL, 2605618666653483974ULL, 14460164355407180547ULL, 14098427466058439125ULL, 5994098872828070339ULL, 14466607442696957193ULL, 14072559801773610090ULL, 5099310622143272229ULL, 4060612237789893594ULL, 12507027238332520124ULL, 15761310942686801560ULL, 866436462325504289ULL, 3869200021415839950ULL, 10640631636436308825ULL, 10432852934073152401ULL, 15657221014909940938ULL, 11036282581185334579ULL, 14746471652276158792ULL, 3184838388564580679ULL, 5693069587398182779ULL, 341485645424976849ULL, 11145543909036989457ULL, 15269501220065861110ULL, 15995184087178920289ULL, 10716423334959482557ULL, 2377248888797091206ULL, 4115822570748708776ULL, 11388933796231570976ULL, 14497192719562039543ULL, 11586395822997028015ULL, 4645773340435018756ULL, 118476643439542059ULL, 11169704403807182154ULL, 2219774214832864320ULL, 6089363088788742567ULL, 12348810704711838465ULL, 179945416471397200ULL, 18072226658460580274ULL, 8641145848019948113ULL, 17075842764203528331ULL, 16724898342327851092ULL, 8684178675525421804ULL, 3391624620423236573ULL, 6975778023245020533ULL, 15744887874383809519ULL, 9127441870184072352ULL, 13732241678392867004ULL, 15874528746776236542ULL, 11308136038699409267ULL, 10090462849616997114ULL, 2319851642455852322ULL, 14980878792580915251ULL, 7021852729868595658ULL, 11829638469203602971ULL, 5961000722500142009ULL, 2010988334373851604ULL, 8397343067170144572ULL, 7329340908130540942ULL, 11469793625926110448ULL, 8341214052999351393ULL, 11941850039160720038ULL, 2739517693202722669ULL, 454960022759158248ULL, 1869584801156740282ULL, 10520713985714859337ULL, 3374911733708483236ULL, 9946506611103202700ULL, 11223993919054829192ULL, 18309285555084955502ULL, 16890327522252192792ULL, 4310405874941698297ULL, 8033116960703072316ULL, 16071981091907285075ULL, 13597417902808862481ULL, 5671918273738509734ULL, 1231379187865589624ULL, 5743819681686072484ULL, 17303585594429125845ULL, 643586888697107538ULL, 10623850330738770572ULL, 8350128881660137785ULL, 7879471671503197828ULL, 14965300806909434771ULL, 9825797398655493928ULL, 15829502716039044256ULL, 6141126275953976273ULL, 13913899515411053537ULL, 2998965223158835912ULL, 867297293931461982ULL, 7345116506437505409ULL, 17338603876067779656ULL, 9545367699877783189ULL, 1834692154111193299ULL, 14830627386028111128ULL, 11672860550361259278ULL, 10414143939848848503ULL, 11326523905978997836ULL, 12308150692193025732ULL, 3846027965814650051ULL, 17669158754023571203ULL, 12188325058436471821ULL, 893038791899433117ULL, 7528986555014441259ULL, 9355881475747350793ULL, 9506589357362851429ULL, 8473635810382738528ULL, 16756411262318116093ULL, 2570665089862783418ULL, 6453035060108754733ULL, 12257615611857859109ULL, 8323164577417832502ULL, 7175958230147046152ULL, 4430523301164828322ULL, 9251000329672196383ULL, 4990427710551625836ULL, 7468899768688433647ULL, 9726348972748312352ULL, 11409693337251608260ULL, 1615050364109362318ULL, 16829685849726650205ULL, 10380730648414344921ULL, 7107188244499802138ULL, 11266727273456089960ULL, 9439342345611589587ULL, 9918092791983423099ULL, 4581276738904665928ULL, 8025116480227746022ULL, 11543416585745112172ULL, 17407131100665684792ULL, 1086450915778578322ULL, 1831083372562693809ULL, 6374231527496226540ULL, 8015367347074421025ULL, 11318676341435951810ULL, 1327912270717056111ULL, 3427703668299119184ULL, 11717680167065794140ULL, 13048788781534374626ULL, 12128317082618557090ULL, 17898894568269986959ULL, 6274020245741659168ULL, 14552047339087075988ULL, 3383676660014058912ULL, 818734912428938329ULL, 8438786199576916766ULL, 773719639023551534ULL, 12772172244193724329ULL, 5034732825400079180ULL, 7941179100324463905ULL, 13811086193082914122ULL, 6720398906925219186ULL, 10699697447945300176ULL, 11624306108012394100ULL, 17853630840698813664ULL, 4082018903322704577ULL, 1962091557509014189ULL, 17785331436284546685ULL, 4667598548862178845ULL, 6618069832304396ULL, 15510494483861912311ULL, 16655903700684371984ULL, 16219971186266781498ULL, 9648992599622936289ULL, 7877848085047343816ULL, 15934551949561042273ULL, 18439495745783061843ULL, 3286952060071349871ULL, 11729165847586466942ULL, 14927044058593075086ULL, 17972591775837562688ULL, 14050561518456598138ULL, 13704317380322751929ULL, 4205798040017274091ULL, 18015393910947568342ULL, 3413282044258557643ULL, 10507383618343192046ULL, 7148418804566291123ULL, 973175939423853560ULL, 16222653260529178807ULL, 15307964309993596677ULL, 11079658243751797796ULL, 996882297813599117ULL, 2848960757027274475ULL, 6927455324226944384ULL, 1426971738285605416ULL, 7055293552933584992ULL, 5433510150290408553ULL, 9497394168076074527ULL, 6586281728537107526ULL, 3866553109934735247ULL, 8691717521835605621ULL, 11075655361701695647ULL, 15566952123269095969ULL, 10636486022052414762ULL, 1228972866609697089ULL, 6720131548633544224ULL, 8277451931616866779ULL, 17465358754620213187ULL, 7511308886832264185ULL, 14871329507968214110ULL, 12792949510202019135ULL, 11280118962957614679ULL, 3086653447632689460ULL, 5142889603054261352ULL, 4776991110931092089ULL, 5017470542735072645ULL, 3524090706009551786ULL, 11169773346657361017ULL, 4585024966166591874ULL, 17962443748965456612ULL, 10427788546305418937ULL, 1178635746666671612ULL, 3340443756665183388ULL, 11250915178328546085ULL, 13904949387679567044ULL, 6214744891586462264ULL, 12471880659613319971ULL, 9530346073013573025ULL, 14969509193593753581ULL, 4273715943787407087ULL, 12243618151725562062ULL, 11708578121778514118ULL, 8678695290742706139ULL, 652416975719013268ULL, 17464592324648489788ULL, 11249897501343429325ULL, 633410116845312035ULL, 7469588151720230518ULL, 9577894317781215728ULL, 9828762314990147916ULL, 13618454942183172740ULL, 13880060555209828659ULL, 658605211171047744ULL, 12272027473061616277ULL, 4004438423025373610ULL, 2583940694094995381ULL, 13301601068322738776ULL, 4101279113912975284ULL, 5858235705055605228ULL, 5810307123235729470ULL, 13981560279594353615ULL, 1658473343027293991ULL, 13575145523497625729ULL, 12739106425376991277ULL, 2520404150970719859ULL, 9389038075179138300ULL, 6100334389452305792ULL, 9493222785580177571ULL, 14033966692292086166ULL, 15480900219633823043ULL, 10765629729262362021ULL, 13527312071547332153ULL, 12378956226374948863ULL, 17370734672278564863ULL, 2430127020953458880ULL, 12257903534775569134ULL, 1715111782699741031ULL, 6546772316297694437ULL, 6401732411202784160ULL, 4409252929806640757ULL, 9316007178466860786ULL, 3870405006267222384ULL, 14001717390532491989ULL, 10546997836312665781ULL, 14494491557005554488ULL, 11201832427405724532ULL, 14531847842993913464ULL, 15366873004491100313ULL, 2464180448062060661ULL, 544931618946096598ULL, 12111356697619274336ULL, 5818526526817004026ULL, 7956701155956054264ULL, 14130168630807296324ULL, 13692834942098558903ULL, 12146844769294605287ULL, 7990922245830817023ULL, 14774283556796669089ULL, 112272453166719332ULL, 12546566050809307190ULL, 10951599714615334764ULL, 12704995156168448459ULL, 10654816214017551205ULL, 536014619925651164ULL, 3354469534696244734ULL, 15668977646198108744ULL, 8618485860573818446ULL, 5082995861232064529ULL, 6145756778443652902ULL, 1483844006457656179ULL, 7791341198193559159ULL, 11407397596035416986ULL, 4890249398170314754ULL, 331970667432018520ULL, 17266470247160558799ULL, 9621539626217656319ULL, 9792494282348105232ULL, 16394701671512250680ULL, 2145903491145660241ULL, 14955368525306955393ULL, 8491874958854443255ULL, 9995604794334887028ULL, 11255835401816773528ULL, 17749905697160318183ULL, 336063646905865685ULL, 6186476254259830693ULL, 11443143299919207815ULL, 18202722262437729896ULL, 4552367301005432295ULL, 5625666157252064939ULL, 14204681731479024893ULL, 14819700369052386237ULL, 7605991604504873406ULL, 13319014080937881793ULL, 4228424950655384741ULL, 3136715284561859667ULL, 3797177139495693505ULL, 10706279149092517688ULL, 11413529812019457957ULL, 4006691196168425106ULL, 7418465187222617222ULL, 14704293106269088426ULL, 17595497157843007650ULL, 6491545582682523999ULL, 15862971336239284141ULL, 5686701677206427640ULL, 16451400838894059983ULL, 4358443752841640468ULL, 18423702616091319800ULL, 2111043077325460789ULL, 1981023287553424020ULL, 8254310130471253356ULL, 17200366928353365217ULL, 4572372321949679931ULL, 18288040824798672686ULL, 10240149940309224065ULL, 8610113896360102336ULL, 18206306752130904718ULL, 16051245626762498885ULL, 14420133578374234637ULL, 17630103502996531786ULL, 1485320106593048371ULL, 7110289158842245424ULL, 3427283450482136586ULL, 12464613933882266170ULL, 4094424101916028755ULL, 11284847456387790116ULL, 11368649573580391609ULL, 16524469873627385466ULL, 4525522258127830444ULL, 10692687498919892110ULL, 11186257673053233851ULL, 4404994858146333309ULL, 18044346897038469332ULL, 9998997686460801540ULL, 3282340509227031838ULL, 14190018384083470688ULL, 18067753429254816290ULL, 550661846429631190ULL, 4282408549975338757ULL, 3614900342801985344ULL, 17695327435529983701ULL, 10893533162120630793ULL, 13934767290623498830ULL, 18032453925890744399ULL, 671649577702987885ULL, 5695524958235453738ULL, 17290853643570518204ULL, 10147804298505476892ULL, 10865166598773235811ULL, 1407818763313326082ULL, 5936544796019507132ULL, 11858981454634938040ULL, 6835826908768355906ULL, 14567325364718250102ULL, 13374905634998037034ULL, 15820991821047927904ULL, 10119657062856748091ULL, 896189263449679402ULL, 2607620399263787626ULL, 12728675172521683130ULL, 9660451360752769902ULL, 10345320277567462847ULL, 8704997179690474560ULL, 15964890310631413592ULL, 6351819485349166065ULL, 10724537413501995097ULL, 1371673613305652334ULL, 3736585133788975247ULL, 10554538713530489561ULL, 15312037039014010129ULL, 6956413110099818193ULL, 18425424462606621780ULL, 13088758916340939792ULL, 5414086710836912196ULL, 2997154599580423181ULL, 11696455433479581695ULL, 6511721942536088768ULL, 3950125865196258054ULL, 4545658813820002532ULL, 509576450901606649ULL, 16927170973212935628ULL, 16578936878150284598ULL, 17195465731838580837ULL, 14418010845088262072ULL, 5684073397697328286ULL, 8143806543198754783ULL, 9361980519305459491ULL, 4324358879721591088ULL, 3377732966265486593ULL, 1603577653080741006ULL, 7778408070497092058ULL, 6795129235518226470ULL, 8198674595486293592ULL, 17308605419676257118ULL, 2516532493498065940ULL, 7515435695903893835ULL, 9362532201570799100ULL, 17000540944471452772ULL, 10686709376088632546ULL, 13962167458447908589ULL, 10534208516011028508ULL, 7527043027894172126ULL, 6205935423926046869ULL, 16221877186684330173ULL, 15600326551271913607ULL, 14585053613104089414ULL, 7981421870017751373ULL, 11753160284862637027ULL, 466145054588804647ULL, 5958942503622532154ULL, 8277364052508189754ULL, 10640400737142300570ULL, 1128788486330077589ULL, 12008462896355002969ULL, 3526415014835764579ULL, 14343257517130816531ULL, 12450101410341107044ULL, 843936302021964092ULL, 2826974608677871878ULL, 5119464066529504593ULL, 12501148658143736328ULL, 9159630037941308309ULL, 8292567273955695489ULL, 1920179511660231237ULL, 13055377622122336743ULL, 10424628371875203728ULL, 15800917410170716754ULL, 3803447690489063226ULL, 10733324093560261769ULL, 18343518969143062959ULL, 14778851814209208712ULL, 5606702454821688119ULL, 4433512416242729291ULL, 8226138258465920396ULL, 17351692808279922968ULL, 15790056605615294463ULL, 5058549875635716964ULL, 3409317061396743231ULL, 18174253588988753504ULL, 15672784223066734574ULL, 4474100889019566559ULL, 3438883945466823858ULL, 3219216156442985827ULL, 15260357126503000657ULL, 10622153912646372469ULL, 1909262261092377153ULL, 7730381237907164144ULL, 8784065456410604338ULL, 14530832482079541373ULL, 6173109868976300355ULL, 9930398822244399216ULL, 1773677737031781992ULL, 16980276403124952020ULL, 18170375288487624349ULL, 4569683471986031824ULL, 7541141564505614892ULL, 1895576511575229993ULL, 7755118074503646575ULL, 7091481092936535777ULL, 17596520681879371953ULL, 6262048760649076480ULL, 1838600894017113801ULL, 16814836061718227666ULL, 11412485070438171164ULL, 17241713446250323161ULL, 5162678431797942829ULL, 14737152370914675182ULL, 825278782297693329ULL, 17241369539689250764ULL, 15395157532314233105ULL, 13693183289866598504ULL, 3865750470216266483ULL, 7846858360249643730ULL, 3569552894303832286ULL, 14028222716931337068ULL, 10650279896189401070ULL, 10559074586139607784ULL, 17974736064430080336ULL, 4131226028475820878ULL, 8947717113384599004ULL, 16448156465905474500ULL, 13704678662563690113ULL, 12644623095651391718ULL, 13288724719926100172ULL, 17317427275883751814ULL, 9340347374406538135ULL, 3636560584197667677ULL, 6441255017632990032ULL, 4030982113414691532ULL, 7807925011210604955ULL, 17142182496418950968ULL, 8413121888185890327ULL, 6179772856778125411ULL, 12068644354382374423ULL, 9436517725675296122ULL, 7232779122205205600ULL, 16384826609439231134ULL, 6862587354124881055ULL, 17248189511999965576ULL, 9038586555751106685ULL, 11095410145471697175ULL, 3757019282247714034ULL, 15537438349047528280ULL, 8455215476688568241ULL, 15685547440435771800ULL, 1709564835245558037ULL, 12948099081465166360ULL, 450146941766423213ULL, 11191239560523454272ULL, 4012527811007011139ULL, 6883664299164076420ULL, 3826933378203077721ULL, 14903870087252246300ULL, 14646004384842439660ULL, 5552296166688992831ULL, 17020332566561430616ULL, 14141509291818875960ULL, 17766022269685018327ULL, 12368195267582794412ULL, 3039963620860976010ULL, 13258507746996622330ULL, 4669238426005475892ULL, 7306060018530684653ULL, 5199174751110718392ULL, 6921912246090863351ULL, 1152428639997461760ULL, 1302519311394873230ULL, 7178152480048959496ULL, 2835049175312588522ULL, 1412181455315810835ULL, 12811490626475109963ULL, 15929982858603090189ULL, 5820115615240011095ULL, 6595497363938321114ULL, 10358739874625168080ULL, 3636263903722622834ULL, 5962547455565718094ULL, 7386190469891254602ULL, 15805127161949359309ULL, 8433431915857022683ULL, 15035537266644739264ULL, 3197241151620903039ULL, 16257075086443022311ULL, 10209016152237336951ULL, 6019896668854652729ULL, 8682663237499947741ULL, 8140541025033425922ULL, 3550391931853265663ULL, 10284715978998345124ULL, 17963947675485481702ULL, 14534280173129152781ULL, 16693577308012502161ULL, 440889681933011737ULL, 7416042034543129513ULL, 14979185565200755138ULL, 9898002558289927259ULL, 17079293577965272709ULL, 15558802641753335682ULL, 7023806442758911380ULL, 8953986948621830234ULL, 12894556581002852953ULL, 3888376880486207209ULL, 16503946261518137638ULL, 8477669915822337849ULL, 17087382437264372622ULL, 17573031842761637666ULL, 11035010766913100585ULL, 8219838752355703070ULL, 7235764082868066099ULL, 14143235338987058395ULL, 9025634948083651267ULL, 17297264301099796863ULL, 939872652036263865ULL, 10656080303117348564ULL, 7244909541420153053ULL, 9261809231799071264ULL, 5724483147832484179ULL, 2804683954651583540ULL, 3321611163207228762ULL, 2528362023756991692ULL, 11323964568292407170ULL, 7834652255933920026ULL, 16561535212593086129ULL, 14896262807861051804ULL, 10944803353900409ULL, 1225204550703353447ULL, 3075075714978752699ULL, 17558925943832918803ULL, 12867035878395902451ULL, 872270544651312045ULL, 11469699280077079476ULL, 15729762875563896626ULL, 7611106953658649807ULL, 3002451008721207862ULL, 5120531053933761598ULL, 6680061477955888735ULL, 5942402361569153933ULL, 3713181907957169132ULL, 9441986749029681947ULL, 10384354634280891189ULL, 14493989841643005839ULL, 17527952055471175960ULL, 13252725631537091971ULL, 13061906945598100198ULL, 3027364250910709220ULL, 9052990702160304868ULL, 13495853800793214215ULL, 16820139676096707468ULL, 8168523847463272611ULL, 13439217052848144897ULL, 10874243802196693953ULL, 3210514065222361107ULL, 17971003154228766469ULL, 17331972856075808308ULL, 18314716318561559791ULL, 4182784058874514432ULL, 18108157827490612131ULL, 8162374656541055899ULL, 1942203428647001423ULL, 1117150270668713085ULL, 626129625423455898ULL, 9453924528068863726ULL, 17295492069127782219ULL, 631674933598335661ULL, 1994275795585646276ULL, 5271243145887833741ULL, 3135959296909108254ULL, 17245563097965681236ULL, 7898146824987997718ULL, 1027540800335677407ULL, 10462214274340632030ULL, 2122454490759770637ULL, 14549383453078055199ULL, 8232512930088741928ULL, 15937955492933015529ULL, 9537475499890880544ULL, 16382766086269057228ULL, 910846485791178492ULL, 519613352681206622ULL, 4724733717533965660ULL, 18059114523368415368ULL, 13594740066534274164ULL, 2568741305265272913ULL, 15124672811450907489ULL, 15021493339489975848ULL, 5874176286011044246ULL, 3175647371016669510ULL, 9448367578543144300ULL, 17129694252927994779ULL, 5892278191441573399ULL, 4430437819476828826ULL, 16810074949133001489ULL, 14054355089507067591ULL, 4551857525491309088ULL, 16055634188738499387ULL, 8563633767109800777ULL, 9699136173358685003ULL, 5123918841527167698ULL, 16802360079662288079ULL, 12593487153072667687ULL, 12946301027340997551ULL, 15837946286565318430ULL, 5403588250250896055ULL, 9459694303787363017ULL, 3358478116993469464ULL, 4285490664746366942ULL, 13405149029408027982ULL, 11409632446797510494ULL, 10744466254785294214ULL, 17261957640066765123ULL, 944385084571085435ULL, 220036017951850409ULL, 12976147158841848854ULL, 14364422219415352165ULL, 15320697515514904491ULL, 11993993499423060294ULL, 10355515460709079484ULL, 7157829100214703608ULL, 14788500291960541959ULL, 2789341736572050973ULL, 9311850079756139345ULL, 9656443454566582802ULL, 5815833967059108353ULL, 10693900934291851315ULL, 4713679297632201267ULL, 6609625825874948293ULL, 13203516971884436595ULL, 643707473696532104ULL, 15981367418022715758ULL, 1137486369224510782ULL, 12198813957918328720ULL, 5279204362755245792ULL, 6933263802168953407ULL, 15746618922991787657ULL, 15836056368761207809ULL, 8866698354905145390ULL, 484450052375510705ULL, 4212436985518828868ULL, 12430832846698594529ULL, 8076782890367591972ULL, 7356679031008276946ULL, 6895119212963555481ULL, 14185201922559143811ULL, 550261971434962957ULL, 14752454540301467218ULL, 11900046633828217319ULL, 321936324165106847ULL, 13668247969675834500ULL, 1290626341468016817ULL, 10736456731787344426ULL, 5252983960583781079ULL, 14658079907089113628ULL, 7947834161852096496ULL, 16870763013714596162ULL, 2349051861118713474ULL, 17059149602813520196ULL, 4864300222293336925ULL, 7433362783328356395ULL, 5469071435015178894ULL, 16017581063534926006ULL, 4604364674308206215ULL, 8201331706076723711ULL, 3531100377780061969ULL, 14724108889750360501ULL, 177456909693485922ULL, 4663892207528972926ULL, 3407552007794503941ULL, 2384013931246898089ULL, 3878613613323120222ULL, 2022805253174798688ULL, 11534472861098728552ULL, 3272540468735637191ULL, 6275494661211799920ULL, 8523421395712636429ULL, 4220085914733221290ULL, 9118945763666087117ULL, 15345091930373589697ULL, 17846019037822740894ULL, 6591315164212644903ULL, 17806011248621649027ULL, 11999779262780520708ULL, 5652279769315989702ULL, 8765608314908714337ULL, 8134523433789517916ULL, 5971325011761944660ULL, 783255320595442559ULL, 9294487786089885921ULL, 13457401299387278264ULL, 8942350674116757449ULL, 13840822753771821441ULL, 3408666551361846021ULL, 7393956174467040177ULL, 10646952746586228728ULL, 17327723350809252574ULL, 16817911986121729054ULL, 1761818441722922755ULL, 13459359673495951942ULL, 17986488659628096091ULL, 11060024588324920732ULL, 13428002658940814182ULL, 1975100947738652288ULL, 671868120376033371ULL, 13733214297214134872ULL, 9908024178551321937ULL, 17816908615398689935ULL, 771656266119076645ULL, 12755163178304627856ULL, 11456294863405181371ULL, 16619601815123735037ULL, 17847463976607896361ULL, 9575845900998981348ULL, 17348406868968743034ULL, 14853354703679684318ULL, 17945111410018173095ULL, 330428342314483794ULL, 11311033008219095348ULL, 16132683164238459935ULL, 16173625339831672205ULL, 15381881938105119676ULL, 1097804494645072932ULL, 9278533152166355237ULL, 823285273499912466ULL, 11770039515852000070ULL, 8749977948880856348ULL, 8626542913776197712ULL, 1460297196261522082ULL, 2094304584232850481ULL, 6653407231875155575ULL, 3525373909735944821ULL, 2600443145106784337ULL, 7320793908477554199ULL, 301701575925065996ULL, 745543312197207105ULL, 420911447232803308ULL, 14336499934097377759ULL, 3913956963508481669ULL, 745407682518258168ULL, 10352384243980296993ULL, 745490407954695631ULL, 3355761947268104794ULL, 5294842547950608627ULL, 3527725199244386496ULL, 16664797658482385127ULL, 2652883177032024747ULL, 5475319014259076162ULL, 3053477648250201605ULL, 9853055210099433364ULL, 12475376441331059463ULL, 12174330461148672467ULL, 7854842158084002686ULL, 10800575116344288972ULL, 2875600274112740582ULL, 11916702384786428367ULL, 8433017686587569272ULL, 4175045899043435627ULL, 13937188739816932436ULL, 10852744883153744444ULL, 16941152258468741967ULL, 14322768088693033479ULL, 12631775412788448684ULL, 11951178867618091078ULL, 8934159513753876999ULL, 11258705575809294038ULL, 3130315234516782917ULL, 7864935737567774256ULL, 17348033291747971722ULL, 7365939734320710779ULL, 8339910816380426005ULL, 4930167307673758531ULL, 10511754687554295879ULL, 15336397204080430279ULL, 1119903149739449558ULL, 10660433539573900631ULL, 14423641125531866739ULL, 5375059421230324249ULL, 17063742988348357819ULL, 10026353307080739845ULL, 9406364837098618719ULL, 8837170290669357763ULL, 16699762578717659673ULL, 7971097379228366708ULL, 7143776409284046343ULL, 15707090185530548878ULL, 5764720597421110412ULL, 6643362790097050510ULL, 17143096512245478437ULL, 15202930820687632629ULL, 17535222740160595333ULL, 9297958534747412201ULL, 18117709416883968764ULL, 4438216060430338415ULL, 11015192289722125940ULL, 12680684654794731643ULL, 6805126828956628329ULL, 4084081524595020093ULL, 3293402975373320580ULL, 12303067226856518882ULL, 3771035490119339463ULL, 8988270290616980723ULL, 4752189042272941044ULL, 6856963484602716662ULL, 14607948814267151307ULL, 14607767022065970563ULL, 2742229215736438548ULL, 10496192523608278911ULL, 2858969103666340497ULL, 13894594266928833ULL, 14375726796438156190ULL, 11884365082439900867ULL, 13723637642314502030ULL, 1884797662897622787ULL, 10861389792925708149ULL, 14790248857970306827ULL, 511450668237404954ULL, 7696828488816517710ULL, 6432846379594075600ULL, 11230815215463018403ULL, 12590332785905394854ULL, 10392922656449731948ULL, 15434173770801607632ULL, 9799936090244685098ULL, 8103223797988077604ULL, 15137307079479035703ULL, 660323882351530338ULL, 1157361097124774994ULL, 15497635176317074005ULL, 5648539157614273884ULL, 1089829165886876ULL, 8808911906626576089ULL, 4293387324748576011ULL, 17899849958089015544ULL, 10189954808783314051ULL, 11372782738083131713ULL, 1179546201345864571ULL, 4144379609522818236ULL, 12448942292023332565ULL, 3993465782121982310ULL, 3090096687640140487ULL, 172753067907364599ULL, 15079124169983684023ULL, 4373438032267680931ULL, 549361584461377963ULL, 711144378695554405ULL, 10496342186021555702ULL, 17800899327042417320ULL, 7053955819823240340ULL, 12493152739073667060ULL, 4363310736911234186ULL, 18103729674309456164ULL, 222688125525841996ULL, 9500915974263946146ULL, 8413867423194850139ULL, 10061425646773276557ULL, 16676709090730842448ULL, 17441308824435700088ULL, 12752485820190581797ULL, 9672562476976598030ULL, 8380542434060856678ULL, 3799362629100875531ULL, 13789860209234596643ULL, 8810608937111777145ULL, 4371183765075736878ULL, 7642634076981280029ULL, 16040466036366531043ULL, 9977808804959486824ULL, 1777237608932529089ULL, 11304646699951898967ULL, 9673698068476408770ULL, 4513307592611757218ULL, 3613237438230569561ULL, 17544039117194461089ULL, 10138784223575069809ULL, 11768539004389552153ULL, 11286580857620103213ULL, 16161162816718645517ULL, 1998870209801941888ULL, 13575190491193438247ULL, 156363902583001517ULL, 10606832843353940515ULL, 17985642152316190860ULL, 10132628141316581592ULL, 603379928681765921ULL, 17954220654197053261ULL, 2546958370527126728ULL, 6084781478758229395ULL, 14801605266656615796ULL, 7223845733417339051ULL, 16309168703639808991ULL, 11082962024502751355ULL, 5020483032044909035ULL, 17815434753789126799ULL, 1608684844785524562ULL, 17771860346162534519ULL, 10472414625059677697ULL, 18210021813704633127ULL, 3012656419023249462ULL, 13668829787011233196ULL, 2020702208507862494ULL, 10527869418330528983ULL, 11879335070900696645ULL, 14804900977943907825ULL, 13891769002811353400ULL, 3082413377954335347ULL, 3565790902873435350ULL, 6839030778310196105ULL, 14136096743024225815ULL, 13189670765211641801ULL, 7814464595175079987ULL, 12008548288484372041ULL, 5901595585890637996ULL, 8315387223931653823ULL, 2224664174920727015ULL, 3512017407929714369ULL, 5581640832285626197ULL, 14773509103905582880ULL, 12165022583821241131ULL, 1375760756928870103ULL, 17799105989092486245ULL, 14801916093953046615ULL, 13907542387479148269ULL, 10049421225958724322ULL, 15699934033142499107ULL, 14597389651444034348ULL, 17213092310918722160ULL, 15067361549933483843ULL, 8365668772026089315ULL, 17282415885004899081ULL, 973463241254481825ULL, 17425373904384597240ULL, 373147832092038175ULL, 1050089252447843111ULL, 5886920084292739291ULL, 11895807850556184493ULL, 4508791570152452759ULL, 15978377047622173650ULL, 4709875329972269265ULL, 14548148827608787588ULL, 16449714669840779748ULL, 14043948894944207747ULL, 5708795488973392011ULL, 2314745240124573747ULL, 16874659415509035175ULL, 14912665938432495273ULL, 5216136634449106838ULL, 6621056630695433914ULL, 10617912609699043100ULL, 9358464794394338764ULL, 9416592372638622013ULL, 226672593314459883ULL, 6088611467335848086ULL, 3258959012672982570ULL, 9158594969039092683ULL, 1561972579213582565ULL, 742575536972916868ULL, 1078334539465153560ULL, 18045884535985695367ULL, 13507291141326245278ULL, 9343148952661184646ULL, 17947293398819285412ULL, 4679654437312032469ULL, 8361868859443437047ULL, 2651585630683768203ULL, 11044570696105880806ULL, 3252279415460060836ULL, 17777271761739318070ULL, 15209528820715002165ULL, 9068149427388885283ULL, 6621835438206313999ULL, 5078998223004783887ULL, 3329405504089107094ULL, 13449424956278447347ULL, 14462273558578476930ULL, 11791013456615552172ULL, 16901353769665173023ULL, 5889154489750180004ULL, 9683501360203314571ULL, 5118868590944775088ULL, 4302730910073269131ULL, 5244410234705221464ULL, 16941818708819735006ULL, 17249169388152853794ULL, 17818814984499794561ULL, 11935399269618954638ULL, 13912079852425082769ULL, 7449285039010691905ULL, 13272733936669852823ULL, 13077154112711074903ULL, 2544476422635720571ULL, 9917622555771523735ULL, 1401373283955211150ULL, 4131387292353983474ULL, 9221319519442322619ULL, 10271096730818380987ULL, 4349348541264102181ULL, 15608920019685690241ULL, 13870050185713501452ULL, 11890374698958552516ULL, 15349447739813429213ULL, 17437619774274474126ULL, 12248299835922725678ULL, 5291445165740621936ULL, 8038671103241778789ULL, 12060028942846433639ULL, 4509233015459420475ULL, 643180764943347744ULL, 994157228082765472ULL, 10887607236534736558ULL, 934798733919360659ULL, 3368165583255026370ULL, 2076839466195007405ULL, 13443469130031139409ULL, 8367682099489166253ULL, 2125674162751811223ULL, 9990807060880866882ULL, 17394088508122231814ULL, 11757529717494926914ULL, 3187824009385859411ULL, 10966777805957954623ULL, 3870341214616800184ULL, 6023551681934394607ULL, 17589502580855815210ULL, 7172648547929977317ULL, 4058349330184047178ULL, 2211901395395798706ULL, 12743774863108918303ULL, 15320135379886977789ULL, 17147637292945212545ULL, 13987566276345734720ULL, 4067023674166500722ULL, 5870180151344150815ULL, 11932062297827142492ULL, 12950434668599572809ULL, 16819056124206693395ULL, 3291855202668117537ULL, 7757976903511781626ULL, 122107839697862470ULL, 3413142924802729464ULL, 7454187765893598468ULL, 7968503300258229110ULL, 5470183611835298691ULL, 14474864210835245721ULL, 3954356648594381876ULL, 1203576788389720011ULL, 13602180523878073346ULL, 6716116329796267946ULL, 6590373262065052569ULL, 17274595806792552259ULL, 15333358509884607467ULL, 4226869313685499837ULL, 18150876205965279116ULL, 7470217877614645414ULL, 16449603603895044121ULL, 5671080049107181205ULL, 1445351349851319119ULL, 9864740424984853830ULL, 9827706874003814974ULL, 14837280824310196055ULL, 6560851287923595953ULL, 9836472848208188013ULL, 14691829054664810672ULL, 1336561861487759111ULL, 8661852267849592999ULL, 4579961346361874892ULL, 17452937695690152372ULL, 2407685403987878928ULL, 8940878107572396130ULL, 14493870008348064143ULL, 12117015166546605096ULL, 15458201882160254741ULL, 12479563054900113679ULL, 4997537931085004093ULL, 6456054670061613603ULL, 4596074859203471785ULL, 2247202908835719559ULL, 11790179722589051907ULL, 2802846863328190045ULL, 6175668446932214881ULL, 15388134621795555068ULL, 15841825884072675653ULL, 2768170468407956284ULL, 8934197641561484762ULL, 16097445743471219327ULL, 17753041514565282075ULL, 14425815027897914658ULL, 10827915952169273519ULL, 17280291941746394173ULL, 8187451720382932139ULL, 141441271333998660ULL, 17893869509508841966ULL, 10581348484784225885ULL, 17762963401331238101ULL, 2167293615188131181ULL, 2200050483577847924ULL, 12988156401759680354ULL, 13245035733164596392ULL, 1548372814394988466ULL, 5378096591718715433ULL, 2074912868947358658ULL, 8964923667179207153ULL, 1366756423179302770ULL, 16567809928206146636ULL, 15419232348049359544ULL, 10652520587055804607ULL, 15493163217003127304ULL, 2207040903029103652ULL, 16363222667994704889ULL, 12742577209190520233ULL, 12390124032809051068ULL, 5875298930604826007ULL, 4159950081840610345ULL, 1942550469455131123ULL, 6120084820195929489ULL, 4189359863922943742ULL, 8316221204566775255ULL, 3406448494150058290ULL, 14977527868723308074ULL, 3771350577658747951ULL, 2816011047888933129ULL, 2395895766440909881ULL, 5112064748503861655ULL, 16414337328521294985ULL, 1708599863801602850ULL, 3663957190589297758ULL, 4957488558324390979ULL, 13144279342191749006ULL, 6813923573274028356ULL, 3516089207819659534ULL, 8495936761223186148ULL, 14942103494662035456ULL, 8696656931805617185ULL, 5710302710393869967ULL, 14232375125546869355ULL, 11703120560058190753ULL, 8369891784536285845ULL, 12449169751035566267ULL, 6289690297444355826ULL, 14265367171767269436ULL, 10531605808489440433ULL, 2854253393116336280ULL, 17539528451750896645ULL, 14989146486837134234ULL, 12788087835298615807ULL, 9401614376319490239ULL, 17989882563198331548ULL, 7642760839170341120ULL, 4563983619718351671ULL, 13310369065591483047ULL, 11985461715745397294ULL, 13679706540415479286ULL, 1125513908676837562ULL, 314994494561372920ULL, 15796679806720198361ULL, 10625119312901365711ULL, 3332434413502694470ULL, 1797260467503516716ULL, 793899453678181398ULL, 11087375562591858754ULL, 14725373244267567835ULL, 10253358618372865718ULL, 5185727477821764106ULL, 5388917257996973734ULL, 2303924165488920218ULL, 9619103795628174450ULL, 16674248551048994212ULL, 3559417062803223541ULL, 4467082467385230037ULL, 6874564979062261936ULL, 14782093411657118117ULL, 18232166913550761464ULL, 4807293091714203147ULL, 13573404852514895082ULL, 4734366808752906418ULL, 11353370389311699377ULL, 1875048800876749307ULL, 12603169512723050616ULL, 11512882478911021099ULL, 2809329710034347897ULL, 17017690132360359500ULL, 12957025922505133147ULL, 6699220427367202732ULL, 15832783117361669028ULL, 10619449008272680199ULL, 17023477352262652510ULL, 5034117659648645291ULL, 8879195316242279251ULL, 10014006411953872938ULL, 9841368135266036811ULL, 953437937678241701ULL, 7916173360992510373ULL, 16183106400064249147ULL, 5061118182006080292ULL, 18272533593429154668ULL, 1902179109959303086ULL, 15224583361250793333ULL, 1377240555995868512ULL, 11290002100751824090ULL, 8526169512885810416ULL, 10606277177867585391ULL, 17462320529339968987ULL, 9841461657138378404ULL, 12530621499994985368ULL, 12138394037526426204ULL, 1713329132912131441ULL, 11925969462168970433ULL, 1196117278294756762ULL, 15465615912611140029ULL, 16865448750301457966ULL, 13959079561515528205ULL, 17197070979728289415ULL, 9129899997424783581ULL, 16305277681370751852ULL, 7079385869117722687ULL, 14136526846777001970ULL, 5622873477415569546ULL, 14132074370034880822ULL, 14257227251381485304ULL, 15725972158050181847ULL, 16191468147976598955ULL, 14779586799596654434ULL, 17178872915791942893ULL, 15706585890681224675ULL, 6370337329812294537ULL, 4546624560547631037ULL, 7671355514835876872ULL, 6482591104502910504ULL, 18133818494665376690ULL, 5518697097150184817ULL, 11648765833865018541ULL, 5237191791472238656ULL, 16282740965922880917ULL, 11704435650846533959ULL, 9622902423791280227ULL, 1112814598050646685ULL, 11836726278245198239ULL, 17470347079497274103ULL, 11572814632807053691ULL, 3126426021795331508ULL, 16834068405210842666ULL, 16367898849264301694ULL, 14168272408053460200ULL, 17842879903250809374ULL, 4460459246723434776ULL, 17390839691096567097ULL, 5238320966107584217ULL, 804279582174123726ULL, 382643145896732389ULL, 7031560320750579182ULL, 2268109989486140767ULL, 10732868279561702107ULL, 15588189569888174340ULL, 15051374802410365535ULL, 11863487355546879331ULL, 5668954682138106931ULL, 14894969265510545190ULL, 9930937415817545832ULL, 12270273018688756969ULL, 6424275580222443892ULL, 15346649413470052925ULL, 6124876194997938686ULL, 15202298559304146220ULL, 9717206781603568070ULL, 17403037834543754494ULL, 8155862396012969616ULL, 2196965974337589297ULL, 6359186515398487765ULL, 6920699125992576275ULL, 2552656107685148741ULL, 9706571795437031658ULL, 17391266168587319952ULL, 2441904481355834235ULL, 9939734839946509364ULL, 16157918500405035385ULL, 5855974745488839504ULL, 8809728218402189346ULL, 3997781792457880401ULL, 3026726811101610212ULL, 10349627118579533940ULL, 652865985408329757ULL, 5660899694052107117ULL, 2481858537755811504ULL, 8823259127303582830ULL, 10495074797750663416ULL, 15611931697817472123ULL, 3736375065273859429ULL, 12082969862062340722ULL, 8192101484950684324ULL, 13563715416017446866ULL, 6447557136478108869ULL, 3177929464759158638ULL, 2556931193036154743ULL, 17374739867116517483ULL, 12943515678389628824ULL, 18233405297727696042ULL, 12987786471326508001ULL, 6402293521216520704ULL, 9844544932198108522ULL, 5840665125696484458ULL, 17618871334912345347ULL, 584036089002092265ULL, 3826372451367254979ULL, 13547732253342725230ULL, 9090088398589304368ULL, 13651088999521826549ULL, 11922118582127648293ULL, 16726162116612567991ULL, 173021407810447376ULL, 13023296345829657693ULL, 13395604246261145198ULL, 17704372952581520808ULL, 14145339548022084169ULL, 10547024225770925277ULL, 5188652153533062213ULL, 2369073765187618339ULL, 3585607009344477896ULL, 14466267149204275179ULL, 6439379484291556522ULL, 3466133161808725230ULL, 7592574339978489225ULL, 3296361696107442985ULL, 5629486732526367029ULL, 10178071873203748022ULL, 7836620321844132001ULL, 15832561432102095736ULL, 15789048915323099971ULL, 6978441970986331673ULL, 16543294822827063297ULL, 4001729876585152305ULL, 13167149618809565499ULL, 12351486316497308285ULL, 2168285614853559746ULL, 691662334599017619ULL, 7078956410680169520ULL, 15931487650081957445ULL, 10598208142670071070ULL, 14497058747951975037ULL, 10947328661084452789ULL, 12346641253943876196ULL, 16712960915509016730ULL, 18190650413003021162ULL, 2807603792126257040ULL, 10758546223867394756ULL, 92217154942760320ULL, 7309571029250507886ULL, 6130765302164479206ULL, 8370303958678535935ULL, 6876209718879309960ULL, 16726782578721505146ULL, 3211106240849817623ULL, 6008037829755053226ULL, 15581977177004958242ULL, 17171892203245079682ULL, 9979580141361963708ULL, 587448701938823063ULL, 13156953023564821908ULL, 9626116087985467825ULL, 14430208058868163728ULL, 13969197981666929019ULL, 1493656712749060737ULL, 943973757795526842ULL, 8360946925228878863ULL, 6340609169021813332ULL, 7698402192838266913ULL, 3547950907788145927ULL, 10125988332848537436ULL, 4268053794341246090ULL, 13605251106659994131ULL, 7825878450443999450ULL, 8902742129731191072ULL, 8460622463665615764ULL, 7876580965634993839ULL, 13453923412387957035ULL, 179794810042508686ULL, 10157815493870213921ULL, 9980085253668495848ULL, 3048760658759075617ULL, 1378357739699714861ULL, 11759580766373769453ULL, 1053286204682908267ULL, 15819615191596606337ULL, 14892138325878786511ULL, 12934757226770722084ULL, 273937187243421642ULL, 10087508769563436705ULL, 8761919875055770361ULL, 1431340134041776746ULL, 2615704612030635682ULL, 411109257764326709ULL, 13059876097172311106ULL, 5659009748861691970ULL, 6629700633104833081ULL, 12828953999979684528ULL, 6955966383889929295ULL, 3269704691566558524ULL, 7240571617038436830ULL, 15316273429789400393ULL, 17753495152232435094ULL, 2317057612399635402ULL, 3507492426240400459ULL, 15577479071427190734ULL, 16624612412411031523ULL, 14324334926671237511ULL, 55005550089714232ULL, 8207543599467475364ULL, 4404370892754459531ULL, 2560428258147803813ULL, 9682645457845509264ULL, 8928125739343013147ULL, 6710233698293424470ULL, 18394922620639371491ULL, 10742467300066031090ULL, 6834921168086442109ULL, 15437402565750985978ULL, 9298440047464458375ULL, 2805365750264488565ULL, 7729560059545738619ULL, 2103461790371736804ULL, 16275285124420485996ULL, 411746384530318601ULL, 13638448325396739538ULL, 7785494749810973100ULL, 5753704762731717790ULL, 299663777073461576ULL, 11047572734344411622ULL, 5833005655271187322ULL, 15633925920833652058ULL, 12708796682637478368ULL, 11358798529978425534ULL, 15353584282273949843ULL, 11674257421439531993ULL, 15533482268102463928ULL, 4377492385224474374ULL, 10044464015935306887ULL, 7680192936083965540ULL, 13838213572986632188ULL, 3746216992597880201ULL, 2531345133349587284ULL, 4167817782496222920ULL, 7541704319801109656ULL, 11502536999400839849ULL, 12433805204681247938ULL, 12203486111693024575ULL, 4838018977213282157ULL, 13455362964947194800ULL, 2524930298991450437ULL, 3365731876033633337ULL, 17840696255995219990ULL, 5055044690493673170ULL, 1994210558112178299ULL, 877859518894907480ULL, 10360514534730457706ULL, 13812314268590143916ULL, 12973075502205279686ULL, 9642563649435311801ULL, 6686859806080664539ULL, 1918737590075340668ULL, 14223518965800750764ULL, 8923776035028773399ULL, 8082553177219888377ULL, 16551570976778601324ULL, 6199514704888570297ULL, 9379273114158266126ULL, 8132750451833700491ULL, 15174536967701315427ULL, 13108703619856181167ULL, 9706895172766285330ULL, 13287295009451765088ULL, 7060006720104096229ULL, 15547993400500510910ULL, 3152863293760947472ULL, 10711108073776194194ULL, 11030853293456198570ULL, 9417511979289886739ULL, 3901932409507901091ULL, 2130555960651378199ULL, 7648022943207627164ULL, 11576084718197933542ULL, 3007687143675783151ULL, 2667284041385430459ULL, 7685525638058298493ULL, 440244184159234791ULL, 12775657703919498679ULL, 7960685070333844945ULL, 9081325433440124259ULL, 16506955021259853026ULL, 17145863478138494195ULL, 17542523179403292320ULL, 9920998098127465863ULL, 7855515761917124674ULL, 8377644556827237650ULL, 8299333371566803416ULL, 18429492874391446294ULL, 1083165370535998516ULL, 1266789808074555572ULL, 12450037707208327004ULL, 10627493659357899382ULL, 2667509059387534658ULL, 6706026483647579000ULL, 9777833515196960112ULL, 9177314826855846807ULL, 8028857461632562217ULL, 7600550706114967070ULL, 7601872075422039531ULL, 1026791241979300709ULL, 14771229760569606518ULL, 12863551060107893119ULL, 18257487233157711552ULL, 17239913281916521615ULL, 3584389773773061937ULL, 1706331133572750023ULL, 89221867743410950ULL, 9304876668775702936ULL, 12342157889814776723ULL, 13827018139374206506ULL, 11505786564322561755ULL, 15524515213869047249ULL, 1751921260424074909ULL, 13959897162133756498ULL, 11001709590168950151ULL, 8967575802201698328ULL, 13423672601977520913ULL, 13209192309367947513ULL, 8978847893107536378ULL, 14965716353394758094ULL, 6990213367993054273ULL, 14102968413577227966ULL, 10735252987548201178ULL, 11325153936290785732ULL, 10952265642388153073ULL, 13614151480120059109ULL, 14043495571678553199ULL, 11591292484451300719ULL, 15372622979936782085ULL, 6918697893134038430ULL, 17727317059109054673ULL, 13129752230461024745ULL, 18091426562073057528ULL, 16486478671684867544ULL, 14976000574156171936ULL, 6929927207671471076ULL, 12923356386927721413ULL, 10322662329451311593ULL, 13467816543454056173ULL, 3212284635638125670ULL, 3769759457453575706ULL, 13067015062744417527ULL, 12061425259542538728ULL, 6969116134649337727ULL, 4491452266391946955ULL, 11582471713435793814ULL, 3145822266295535261ULL, 2358960635244569410ULL, 12813014532981092208ULL, 17242039097139490960ULL, 18168979516335478789ULL, 4714407687464481916ULL, 6078434131117076308ULL, 7575556534676297678ULL, 3866649752098041549ULL, 4788152935683236961ULL, 7996601468066849438ULL, 10506532853031313598ULL, 823279797819684363ULL, 9857561386598593269ULL, 15128768365473859447ULL, 7870838550899552019ULL, 14303265666189456777ULL, 17225771712384627374ULL, 14321143804969213531ULL, 13928382936081411551ULL, 125404555640574459ULL, 3396150649711425399ULL, 3937469439294286815ULL, 13077295489689852802ULL, 7072434510961156367ULL, 17716049455592766085ULL, 1189210936305857040ULL, 803986269877148373ULL, 5923075891731922728ULL, 17598343695435984994ULL, 5555286758671978191ULL, 4287558852100868492ULL, 6234591414742396777ULL, 10032896413606109795ULL, 4948102257370528026ULL, 3962346168746322565ULL, 6792866022098760183ULL, 7496840472752724050ULL, 14047266419288473282ULL, 17689641818754147934ULL, 4280924343792801604ULL, 12917393697394781260ULL, 15398704948740098379ULL, 237919657965645623ULL, 449077133773936943ULL, 10564504801479163133ULL, 6186653685076469971ULL, 5642247834070865280ULL, 10862889033909590591ULL, 10460872049899274970ULL, 14480494971121185848ULL, 7674127105688418921ULL, 1483837077098188088ULL, 5579576753518742623ULL, 327403928124524675ULL, 1048652765325909944ULL, 9732906961508739748ULL, 6052160058422614539ULL, 9153637052954086956ULL, 4241663136948479588ULL, 4757250669355141570ULL, 10713383977092581835ULL, 14897980904418863173ULL, 2782409315404502595ULL, 3306623171766658624ULL, 16792642247981579707ULL, 8844818087104871090ULL, 15722850917996985635ULL, 4525339646839593853ULL, 2165871126735808843ULL, 489263708619032053ULL, 10022450408147499864ULL, 17775269356463302050ULL, 2889296419696964116ULL, 17990363573637051481ULL, 2597023539416840330ULL, 5683936714582039640ULL, 14530247612066141502ULL, 16574114334984760334ULL, 3521553201323381173ULL, 11614173340648119637ULL, 12791106730549499451ULL, 7702783699664053469ULL, 2155097561958662844ULL, 10891791160845644221ULL, 8319274981802036674ULL, 10988196830122188976ULL, 7376538179813516557ULL, 1102866567315922083ULL, 11512878802516763323ULL, 12425741229827666695ULL, 18336366052981587520ULL, 2118348952275183238ULL, 18033338004607460722ULL, 17316865190130567144ULL, 13771338478824527002ULL, 8472862867332243023ULL, 7901496556613372666ULL, 9852185765071539282ULL, 13267283636080418779ULL, 16950387508968781639ULL, 4092033603783467894ULL, 9219908345158155148ULL, 2311096345450122226ULL, 10120433178744606605ULL, 9685752505903662422ULL, 6047323209436860262ULL, 15465188139752016799ULL, 8028250806910547069ULL, 10951464862458759610ULL, 7349701937962425912ULL, 15908657631742751508ULL, 3338977238862330196ULL, 8553277017373603621ULL, 15455965377616646418ULL, 15530844264505781933ULL, 11406327456004149974ULL, 7193351823341580297ULL, 12268159561197947662ULL, 1598948486722442902ULL, 9734178213247413633ULL, 494946208623695703ULL, 6242217916679007984ULL, 13185877995528082216ULL, 2514014514725505370ULL, 6872771970752053768ULL, 1348320544999670881ULL, 5398133074672585721ULL, 4485817000488389585ULL, 16994647614840702108ULL, 8297041685642147927ULL, 3028383353663189262ULL, 1453734289836351329ULL, 2834045116388374515ULL, 9136403968924532574ULL, 15048244857565900711ULL, 1751240667656816069ULL, 6062222652262421222ULL, 15784168067075077348ULL, 816761595138178765ULL, 4534474478386745414ULL, 13781515075497821723ULL, 13342613786209482938ULL, 17453701765329728645ULL, 8151171140925638624ULL, 2922786353106499013ULL, 11900999437725651107ULL, 13945809928581273683ULL, 7802027295929165438ULL, 14924086231697963100ULL, 10913701766830368934ULL, 9777238489010221725ULL, 9997107583655984729ULL, 7590053360166699517ULL, 14955885736937498711ULL, 8714864102204299653ULL, 4056768185189398095ULL, 16800643623329583907ULL, 15874852558853506273ULL, 15527646554284333040ULL, 9875594063869406457ULL, 9771054165614466913ULL, 4855539746356941360ULL, 12985641122190453171ULL, 15047418131480883318ULL, 5033972233913973535ULL, 11992864504664703833ULL, 17772729344635645937ULL, 9580902613913802679ULL, 3612514584380675076ULL, 4461442915991505621ULL, 13938343423656538356ULL, 10935112568252155024ULL, 10725275106169791266ULL, 12464706577445911994ULL, 2009036350316964177ULL, 4244537232679209636ULL, 14899717590774522271ULL, 15334027381316242506ULL, 11908482308274900809ULL, 13632774467848960692ULL, 3024498040702583825ULL, 15477743557970797652ULL, 6064752903590408242ULL, 10842000201732452495ULL, 6412225338456765891ULL, 5857455038764354332ULL, 11741488095098810986ULL, 4658312818726858402ULL, 12962486790752943564ULL, 8859998448780066746ULL, 2767767966845634869ULL, 17989004387205099458ULL, 14360792151720641165ULL, 16797901722813283200ULL, 3850266765615321175ULL, 12958825873876665060ULL, 5378885436728422250ULL, 9430158636464686675ULL, 1677067699475902967ULL, 6152807634570655482ULL, 12632122686032526186ULL, 674804058271666904ULL, 8192525674517562401ULL, 5115351160260378919ULL, 3758887264943980972ULL, 16743990368080096080ULL, 13446632154750987521ULL, 8061757586363929481ULL, 1391647454027440361ULL, 2860893831986220355ULL, 2262925051424139552ULL, 10528437010123622553ULL, 12334944825220074911ULL, 6445320809537664583ULL, 5969947979382846752ULL, 6815644878948814139ULL, 14590032671250127116ULL, 9291195192806838215ULL, 17795534194487544282ULL, 15845355605400163842ULL, 15735938002942249878ULL, 2185953524748213386ULL, 8494886120825377793ULL, 477904482660169593ULL, 7212379354909644623ULL, 1865070808753012894ULL, 7803191509914197048ULL, 40069376576273197ULL, 9593713699013262266ULL, 2303211428596741253ULL, 17183177998218787491ULL, 1969526769533390984ULL, 5553633000056343299ULL, 10808031971135780913ULL, 1353166007908611502ULL, 7025219767362473626ULL, 12455899243491984486ULL, 813988564837822037ULL, 10130082485061864609ULL, 4474122430041291050ULL, 14684233305397693655ULL, 771366386026315466ULL, 16535437055852377240ULL, 14398293093768281038ULL, 15668432830562998633ULL, 15395060331245336140ULL, 5676422980506939035ULL, 18039045123533766501ULL, 639585958864861717ULL, 8226502996810335351ULL, 1939853363213990592ULL, 17136441807213227145ULL, 6512585125693071065ULL, 13810945258065130429ULL, 4925234401035825134ULL, 18202714893503733219ULL, 6296334705029916396ULL, 4695726586125540991ULL, 16781454468169027416ULL, 7731280292805062004ULL, 7188129480347582452ULL, 9117971445288370713ULL, 2530555638876198915ULL, 13404372431889828304ULL, 16874466351271862494ULL, 13253959773289443527ULL, 7856475753668315373ULL, 15655977194945010083ULL, 1537207384085023782ULL, 3481728681327757311ULL, 16771161183622917910ULL, 11951378022165942698ULL, 9617774942850888644ULL, 7747531162090216265ULL, 3545520387807433815ULL, 2197405516555076508ULL, 15079201025100918199ULL, 6288931882434851676ULL, 1560699106226986227ULL, 5872043722791498290ULL, 12387027819742630076ULL, 5841220061504552222ULL, 15596389618071387279ULL, 14966085889969527865ULL, 4660991993663118959ULL, 13229767849791128634ULL, 5657374588918090856ULL, 2550516954253481289ULL, 9201004109649753692ULL, 8184845602104040167ULL, 17390011871927406610ULL, 14635347065500267231ULL, 15269004136287979371ULL, 18429906313722199764ULL, 5495519432913393332ULL, 2357815369544670941ULL, 12854806502152344855ULL, 12176742726176523264ULL, 1430372901447596059ULL, 17581355612292133327ULL, 13019868030189287544ULL, 16821247737215005587ULL, 2705492589166809009ULL, 406233234325085315ULL, 10313768466749209551ULL, 7251266766986407823ULL, 10159671875868821000ULL, 16453124574420729018ULL, 16030163490611791314ULL, 15446847534330270494ULL, 10769108147592477673ULL, 697047151993978354ULL, 7277202607316926093ULL, 16143377228741024077ULL, 12126908386843641771ULL, 11328160218236161268ULL, 7428911124872267738ULL, 12480622834486186741ULL, 15476386830289252028ULL, 4330804178939170871ULL, 917895642729551698ULL, 10826029823079582211ULL, 7296103285531740735ULL, 12512714708841852467ULL, 14977017068812242585ULL, 18328787272256734709ULL, 9679229319848354290ULL, 951620572361468323ULL, 6155281346369236317ULL, 17452724970718365630ULL, 11471165883426482268ULL, 2068932383403115112ULL, 10987303892392051181ULL, 1105452545322201544ULL, 14814462596390239830ULL, 13817463470581064590ULL, 3185348475142831543ULL, 2684231028392439626ULL, 16368489961777153974ULL, 16541788652974985137ULL, 3577493263425020755ULL, 17702631528051100367ULL, 17188904736690863936ULL, 15497635176317074005ULL, 1722669088634598450ULL, 13053856558576894875ULL, 10365194786257117781ULL, 15208083129515418899ULL, 11699395792937341831ULL, 9450184079722412036ULL, 10279137405857516040ULL, 10888306426468547132ULL, 5648539157614273884ULL, 14504664740911952813ULL, 8340444004634755506ULL, 9553654456359369471ULL, 18415969399135437606ULL, 1089829165886876ULL, 1187794066021145903ULL, 8504764218087372678ULL, 10296784751923362354ULL, 14011938312753349668ULL, 3521226240215449834ULL, 1155908442676742760ULL, 16508688996574861463ULL, 4046795530128646034ULL, 2256091224581368161ULL, 10414603057682827043ULL, 10624516874793769568ULL, 11436463571818154810ULL, 3149278422181523015ULL, 12094499301886392632ULL, 11895166872864069099ULL, 15445688837827607391ULL, 12561122843165184530ULL, 11399689481376141498ULL, 3364184664385766994ULL, 13922353501185731628ULL, 5819494556654210069ULL, 2340263804446904473ULL, 14256681708204131248ULL, 12757680644233730143ULL, 9650378166736091392ULL, 16663105710936130720ULL, 15666599611464520839ULL, 2262018189430996373ULL, 17088830329331898360ULL, 13373858611530166261ULL, 693261321896374204ULL, 5014479171320657970ULL, 10723124401507522231ULL, 12808913369859239517ULL, 8947425054903605554ULL, 6992823561859747974ULL, 9656617278051271785ULL, 18422438902674080601ULL, 9346310326508733830ULL, 2465586800758884677ULL, 4724259221520307195ULL, 14900509712425587034ULL, 15811686239760841193ULL, 2658954143637700875ULL, 10233352841486995870ULL, 16089278544739472594ULL, 17398388011664825616ULL, 9168605762332126767ULL, 8689106367580033330ULL, 1551008274117355017ULL, 18192068283123754028ULL, 17257823232034399158ULL, 4403217569307963824ULL, 3143544209934004723ULL, 16699442099531285552ULL, 1322683710817441894ULL, 16132383755187385572ULL, 4715963141607907566ULL, 6088309674674901267ULL, 10935964635523403009ULL, 16937705981750363824ULL, 16677317176183584684ULL, 10334739175664580167ULL, 13245364342490407817ULL, 578696604421114052ULL, 5184839568758261086ULL, 715445734445806423ULL, 5756438856720126673ULL, 5069270844774398534ULL, 12162311476725314948ULL, 13888620899446829742ULL, 5440648290834427759ULL, 1179546201345864571ULL, 8076655944586413328ULL, 18182912746461692684ULL, 10224164967437787659ULL, 16903563177476147756ULL, 1829441738987155986ULL, 13159572589122715501ULL, 15761045890309617460ULL, 13307771184331469386ULL, 3606757056193547838ULL, 9425156101535907938ULL, 6326674334448362379ULL, 6561916175435693874ULL, 14920774597871188803ULL, 11635659428760873225ULL, 6318290540118789273ULL, 5995313048053390465ULL, 9429677512939480906ULL, 6798874524408791822ULL, 10257871843076423249ULL, 17844128445449493899ULL, 9534999243936829831ULL, 17144099578991901770ULL, 4904201931292808011ULL, 14569178382395764916ULL, 7159018911665463465ULL, 8140230784284775462ULL, 2008150088075807935ULL, 6993300572593769725ULL, 14767597392983942197ULL, 17763403966559189918ULL, 6271709850268479833ULL, 9780525143749043184ULL, 83256957380639589ULL, 741943723852905757ULL, 17899091254207328750ULL, 3969940406547851857ULL, 792925767903938331ULL, 17583439055629156215ULL, 14030413543262401993ULL, 12184005635228435754ULL, 15476207329842866131ULL, 11253491926918093321ULL, 5117300161541091944ULL, 14682289023843452843ULL, 12738901340967608967ULL, 14810573270526166595ULL, 6599174734255163614ULL, 14489081571422082069ULL, 2346234607488820391ULL, 7940517079616877987ULL, 14324892873596646079ULL, 15222126906831573561ULL, 7913010095481135565ULL, 8817813369309415179ULL, 8350125465393601022ULL, 18178047288320379339ULL, 10835623418745774879ULL, 2814106181019246941ULL, 7976903684406235317ULL, 5861043930025569749ULL, 7996471993190633353ULL, 11113577592640770827ULL, 18953340064802113ULL, 13807810151508650600ULL, 5652682471736763863ULL, 567409500482036178ULL, 1552548337141446055ULL, 7958770766922705094ULL, 9577288305714753195ULL, 9975830208336251858ULL, 5445584802296000319ULL, 15979224632986974259ULL, 4082007551415002919ULL, 3558668096404020578ULL, 16467573595576885763ULL, 8048914519610134171ULL, 6116954885839784197ULL, 748425034809988121ULL, 5297025509697421798ULL, 5171757525804160082ULL, 11388129932302224637ULL, 135974319098806564ULL, 16685779012383685843ULL, 13103667156860588797ULL, 5474263352075413784ULL, 16237110213106344388ULL, 9523517418617636759ULL, 4178328570922750725ULL, 12294781721574936102ULL, 7291529598641943287ULL, 6793069115073391233ULL, 18417286838782639661ULL, 988706220942524236ULL, 16494019901179939068ULL, 2776520897796416256ULL, 1033009221664655109ULL, 5078098025876179018ULL, 4295083472261997887ULL, 7956637826954024726ULL, 12055596634545353497ULL, 10244481968966192230ULL, 13973285589182757339ULL, 5445984843860700994ULL, 10197735958096192370ULL, 8282975945415316112ULL, 3430251493364323886ULL, 2636404694429143299ULL, 10905332898631014978ULL, 3326915930450312102ULL, 16728005257542966759ULL, 4072951372654104776ULL, 6253190044084695187ULL, 3917996529398023244ULL, 12160269792085582131ULL, 2755849692529783919ULL, 17159768311873594495ULL, 6852240006851999173ULL, 6807602927507567074ULL, 8954509838974228037ULL, 15400693985846288449ULL, 700684598026766866ULL, 13721032953788907081ULL, 1296918612925269625ULL, 15733006908761235131ULL, 16502891145549199597ULL, 1956831145557982855ULL, 15769327680528030145ULL, 17504956797743066611ULL, 3367727739860402851ULL, 7594234119895895494ULL, 2548835308041708912ULL, 8830070231595888835ULL, 15218855315423367211ULL, 11935496554385838695ULL, 13074856169955382052ULL, 1334297964312791780ULL, 9133913577425834496ULL, 14249698395371498104ULL, 372294027268755456ULL, 9911915515031418466ULL, 15809032220143070565ULL, 12460788556699455949ULL, 16723831277861437487ULL, 15259051474383415094ULL, 13366539795142296637ULL, 15088451259421164579ULL, 5256331182759571756ULL, 10282334631932213558ULL, 10871138445427778436ULL, 3206226566004247310ULL, 2459234329792504867ULL, 9003019870745026647ULL, 6569797083804191807ULL, 6520458070196844843ULL, 18181091813259197611ULL, 14636991714448807086ULL, 10809641390161193911ULL, 6866210578179986741ULL, 3468845822108893326ULL, 14480104814431683061ULL, 15138990259586220246ULL, 7305922389263898008ULL, 11484358740887562450ULL, 15825076657531556860ULL, 14601713131043546273ULL, 12717604332822212064ULL, 17285912191272877428ULL, 12305813653905088998ULL, 16791101717542584700ULL, 13739716529888735272ULL, 5333167666677422702ULL, 3785723354136361884ULL, 5539671518314289143ULL, 442658735595423942ULL, 6392020646017671383ULL, 16178697939789293132ULL, 14820222883507623125ULL, 9449763778559715036ULL, 7579618059667070361ULL, 5779143146714968220ULL, 9801706916698406240ULL, 10926524956500774905ULL, 4521213001454393562ULL, 10180770180874357456ULL, 9299341434841857482ULL, 5105139903635994607ULL, 18215045920336678961ULL, 16824787378390103680ULL, 1561485762857015986ULL, 13509287182023935589ULL, 6637919154593321922ULL, 16752794319501500463ULL, 11753396954689760412ULL, 2578929706322444072ULL, 14294182185187086717ULL, 1284317915139324319ULL, 14836051037113268645ULL, 7147314691265851215ULL, 1977916644858708137ULL, 5407957272651073980ULL, 16478961722134403279ULL, 448324758988060880ULL, 9296278620717142525ULL, 17667973069005530023ULL, 17321301663801212619ULL, 7700258617698292174ULL, 18075830820136532401ULL, 5129672577202515997ULL, 9527976848665989806ULL, 4602854401342521302ULL, 10761825511470470158ULL, 13318674223840545575ULL, 14927630321371516483ULL, 9066435573359101873ULL, 7628238684950467005ULL, 14360919074309353401ULL, 3725516593862846509ULL, 711798305904845378ULL, 12129145236756859161ULL, 8363001574736063654ULL, 12009005718292617913ULL, 16963444433617107147ULL, 3227548205257503038ULL, 5850140091721369489ULL, 7481660875056912223ULL, 310622245754574257ULL, 13669276426761419691ULL, 10539094099010629801ULL, 11496510928444281563ULL, 11567854588103676450ULL, 15437681226057428888ULL, 16721704768490813109ULL, 13545106394425944507ULL, 13472661449244455447ULL, 2740659738249193687ULL, 6940195244722776160ULL, 17551121690504463691ULL, 8405903893303112539ULL, 6351498971065207551ULL, 7331391462115598948ULL, 17120503773676156620ULL, 15819875446437677197ULL, 17120503773676156620ULL, 13731173786604973492ULL, 146258855674464274ULL, 15677886690634020777ULL, 1132762110576260518ULL, 13392547991668367415ULL, 9241670268609416412ULL, 13446203889843457684ULL, 3880031649410167688ULL, 18191785201461428376ULL, 4489264282744631629ULL, 2296295843144459280ULL, 196966345534845565ULL, 455453292631051075ULL, 17789369135806005789ULL, 11756799960996204791ULL, 9040551916821593478ULL, 12650733727462400986ULL, 5020083589835693221ULL, 11851946519941575179ULL, 8169000187550643169ULL, 2828121828594382383ULL, 18225988676327529074ULL, 5457075905965756018ULL, 9254226347118384978ULL, 1687142658007595333ULL, 15953286494240873026ULL, 13515658327032498954ULL, 15253298575753614323ULL, 14780131331311627973ULL, 10069623596928559043ULL, 8132809830614009693ULL, 12567022996883089451ULL, 1337168788538487544ULL, 12641218689559489380ULL, 17446863119231669851ULL, 12039811720703761553ULL, 7137478950133823148ULL, 17071051149055422947ULL, 1109121436604050340ULL, 14078959672648114043ULL, 7155747468615156209ULL, 2553350700241628189ULL, 4927995263577092397ULL, 16324239418333927161ULL, 11166329211705286472ULL, 2152300038479170395ULL, 11193751168836769325ULL, 3656836303881079130ULL, 8897743663916133738ULL, 5607998051741345518ULL, 16193883563862256503ULL, 16719006980142586198ULL, 3871111842167626960ULL, 13996139097301588410ULL, 7971166347432570063ULL, 12325759512272791623ULL, 11388646155092299170ULL, 7924743010107696283ULL, 9351211303933353072ULL, 7199687931060128170ULL, 13318843157644312717ULL, 8777084840139312343ULL, 7395004160689754912ULL, 3518660993910313795ULL, 9338243288200251581ULL, 3999915306501197216ULL, 14857076738989482976ULL, 14478930027425962019ULL, 3007695521711326376ULL, 16246537312895901452ULL, 7510149809767402414ULL, 1736466410522444866ULL, 16357258260569222648ULL, 5723261503627093972ULL, 7270641590488042167ULL, 10740241259666478142ULL, 358538029265080173ULL, 17858178807885198266ULL, 13039182213461111945ULL, 12251816841814996995ULL, 18104063710056035657ULL, 10416970956730607074ULL, 12377965698588495915ULL, 11297511364486382612ULL, 7475319219115718167ULL, 17926785947245169052ULL, 12347346061923667426ULL, 2565491103604593898ULL, 7294887076158248212ULL, 3108230885488207361ULL, 17264291989559541030ULL, 6072290755091790213ULL, 1294806680687551016ULL, 12741917918371279375ULL, 667250430632121258ULL, 7973997519928711511ULL, 14693914285840381047ULL, 18439984004549887129ULL, 11949268265693408264ULL, 1258850354355180853ULL, 7718500876329371492ULL, 6336482824050737631ULL, 13560910770277686995ULL, 12709992059818000108ULL, 3716666045711940591ULL, 14071970313285143487ULL, 11199344083113594499ULL, 4516902589739692858ULL, 6697864101108723493ULL, 3931484023808215520ULL, 15420591318354810402ULL, 18120146173224564924ULL, 396890527290010211ULL, 13041122055766962818ULL, 12357660549794721641ULL, 8842475626833725699ULL, 18363221204967686364ULL, 9000087578302077797ULL, 8396791438810111190ULL, 16104845001429102064ULL, 6597335780905594619ULL, 8087604609482250757ULL, 11337626243478354270ULL, 2616078833293994928ULL, 8343213078985730442ULL, 280896761569618065ULL, 2472005528706721771ULL, 767086061994195543ULL, 11362539100360299413ULL, 6509528056869713549ULL, 9799949706135235177ULL, 11794135999140623334ULL, 1186883323292313835ULL, 1402251958721428408ULL, 17684506670234538905ULL, 14490425537487787462ULL, 18387932802918894391ULL, 4224387220678079087ULL, 3779401754901312320ULL, 4279627536915868106ULL, 14831708664027458294ULL, 12180566950545649970ULL, 6989125644315029848ULL, 5679880325577407312ULL, 4886190725919778567ULL, 2995736634151280868ULL, 1842731432512977019ULL, 4285331944586463299ULL, 12158395382656641781ULL, 13205746328533325845ULL, 1897104316585098545ULL, 4260022622688520643ULL, 3659511840172328413ULL, 9611272828836084151ULL, 2728393795730389366ULL, 14822028840618428260ULL, 5100808094142299866ULL, 7522783405138268531ULL, 13487145179116882860ULL, 6313935865828285986ULL, 11367971724756475207ULL, 17864297793310391248ULL, 12336318799178954191ULL, 16155221362471073301ULL, 714212726269508100ULL, 15396114840369713291ULL, 1300957759838301490ULL, 17053933805600587265ULL, 14174522544396862951ULL, 12600807741090437102ULL, 13589292355732039890ULL, 10324062787388533443ULL, 7841228355943969701ULL, 5790662469254770509ULL, 16647139223178760688ULL, 13487145179116882860ULL, 6369431948010286676ULL, 8161441478859771203ULL, 7954245161011870145ULL, 17442131496099297469ULL, 3744534407808355214ULL, 18277984890180908139ULL, 11474093195483400651ULL, 15247394080151408103ULL, 18048870528948392507ULL, 11846451780362262406ULL, 5977486331726161248ULL, 18326132143905279374ULL, 16451980689455935873ULL, 207030992414673837ULL, 1979963282685217456ULL, 3832943377478615858ULL, 220163787915455019ULL, 14890599941129225171ULL, 6592332268645825211ULL, 17842516024248391714ULL, 10005925239862749715ULL, 9519237936668610912ULL, 12386782535519462674ULL, 10035940286747600198ULL, 645739593613913499ULL, 10905792556632378976ULL, 6624877445356988520ULL, 8321137467649093742ULL, 234907564831130791ULL, 11996837367417375022ULL, 16718841558051108312ULL, 13589104003690428232ULL, 18372235925719160987ULL, 792430413454161082ULL, 7784926393360235928ULL, 3252593725550336129ULL, 15642046942067528906ULL, 4189429409271947388ULL, 13563349798547284658ULL, 11132403908539775750ULL, 5111431536044618439ULL, 13276353361583779745ULL, 1327227078252723094ULL, 2550322210088400447ULL, 3416820094170741318ULL, 12154378950763869505ULL, 4842188018010896687ULL, 14391262535675189583ULL, 12660461709226987251ULL, 16082454056620536820ULL, 6777229081074789029ULL, 1955582546074618535ULL, 5377307359703458557ULL, 2834586500991459245ULL, 8835083381988058220ULL, 457043112118462180ULL, 9139068565196588710ULL, 14099600258880096539ULL, 10079572935912006876ULL, 17954729550472671842ULL, 13463345547394735128ULL, 6001186992400010689ULL, 10518827080575948464ULL, 3428786255299711846ULL, 8090820892181338144ULL, 105877577554339197ULL, 16783040591103657683ULL, 14265182005372556661ULL, 12307889867544011161ULL, 14065937616591227356ULL, 7530952475121469983ULL, 15431212762049122847ULL, 15877608319203836852ULL, 200881052012446098ULL, 14994448637621805761ULL, 14095952701920123913ULL, 3520739413492026376ULL, 2533287030119510660ULL, 6332255748871657388ULL, 9552805879725581571ULL, 1940440615593134682ULL, 10541229323642211281ULL, 3805380965143227175ULL, 9804493742135096124ULL, 15154667584812520705ULL, 17778289410823831894ULL, 8635135639380924097ULL, 3052138376564076036ULL, 6546723033718097386ULL, 16626244033580007876ULL, 315547370034080122ULL, 13788235191220273834ULL, 12969768066762158850ULL, 14863671826123609192ULL, 1998476212610279451ULL, 10773846406597653115ULL, 9066861429166542057ULL, 17907030213514991465ULL, 6387472312344054593ULL, 3260802980721285854ULL, 18022761819532226771ULL, 3592026814186427273ULL, 17708932233343992669ULL, 13141365424639341583ULL, 8860102317456951515ULL, 5376231239164196174ULL, 14291548120562853131ULL, 11267097086692193669ULL, 16940660638208409658ULL, 6852852981769577236ULL, 8847637188831145873ULL, 11035431076183023052ULL, 17994623054672747402ULL, 10664052708031164888ULL, 14814801694342897889ULL, 11069641866218242831ULL, 15833115684663434654ULL, 945765000977867018ULL, 11479370278874952284ULL, 16898429249779860247ULL, 7170275243251157300ULL, 10234444154862377494ULL, 6961677769416078942ULL, 7435752138590705461ULL, 2158030254801183399ULL, 6975806076642828564ULL, 10394194913390387173ULL, 14189443850746438797ULL, 14940925902461184815ULL, 11225895788900758149ULL, 9066521065986960394ULL, 8326199567061031361ULL, 3679762955437140140ULL, 14029513716160072437ULL, 7168488559595090384ULL, 3338453788146874472ULL, 6629856861486850267ULL, 1875553928598058432ULL, 18402694373035106869ULL, 421155827412389818ULL, 16190726277946786637ULL, 4710379397870268055ULL, 9164999575071029220ULL, 16038203132229591188ULL, 17003373143843175736ULL, 4409191420398637121ULL, 13357043228190529726ULL, 13602814643861320014ULL, 16040053258643381594ULL, 16467428079409753163ULL, 13850364476037957401ULL, 7749952067094735453ULL, 14110109705876257160ULL, 1903701109181662906ULL, 10248123369965291333ULL, 16404788228487311492ULL, 3064972845250299211ULL, 11090403051420606019ULL, 500049940675931179ULL, 18188441259588287962ULL, 16849280833070737752ULL, 227045780727455608ULL, 951685701403073397ULL, 13911874150405221784ULL, 2572057153955426911ULL, 3720859935862321487ULL, 6495519389856423793ULL, 9234131622593152370ULL, 3466481472019429970ULL, 14252219118686253914ULL, 17106033340395136187ULL, 11978357875736602602ULL, 13139438446929933943ULL, 5066644223176469744ULL, 8477484223270919353ULL, 10766653442077782969ULL, 13524848396545128542ULL, 274358138526114497ULL, 11535066950645896921ULL, 14983689290132618417ULL, 6747033478743033486ULL, 3860455299042526670ULL, 1599533001702829070ULL, 8962662634789107827ULL, 6623392770382786250ULL, 9250059520757083784ULL, 4923986323416474147ULL, 17629600102556914485ULL, 17170297918889620491ULL, 13473444096831679337ULL, 13444238614376067370ULL, 14120876613179764318ULL, 1244976767353240573ULL, 9394921469409983459ULL, 3994948826979234746ULL, 2002600980421641412ULL, 12559221898510869058ULL, 1613993286205484233ULL, 8144692387383153532ULL, 6633758622439859049ULL, 17485889858698907775ULL, 7692505586363575918ULL, 8639689708408851298ULL, 13920377832442449693ULL, 4278332190572080284ULL, 3741019683082796893ULL, 15860143028341501357ULL, 1008992732384545452ULL, 16642479954927891343ULL, 1665669976591201392ULL, 6935950096032710904ULL, 16331113891087679341ULL, 10989979876102920602ULL, 7163317770378673812ULL, 16047149927928621075ULL, 5571833412522306314ULL, 7142090221573273577ULL, 3765978229372855634ULL, 10572752186498234368ULL, 13250907027028076834ULL, 8477062277550035563ULL, 14292940223241090532ULL, 17860735602257198005ULL, 6941078352177875886ULL, 16059926665463951116ULL, 7101188684534582141ULL, 1727072590381782401ULL, 7707758754092453827ULL, 2890101812173096397ULL, 3521431793860302036ULL, 8634076523875386288ULL, 2495654048573400855ULL, 4290246958955697444ULL, 9495070237147945489ULL, 18296130019164219800ULL, 14264816563503588909ULL, 16604417345988099355ULL, 4565373179533231321ULL, 1385919672585369624ULL, 16923276443918929071ULL, 548294452068151319ULL, 5596510063375992728ULL, 6137383766799473747ULL, 11466899945950374202ULL, 12686140761834221611ULL, 2605801439390047595ULL, 12916987317243018797ULL, 17068654972997551516ULL, 15934010062770647403ULL, 1867986544728141729ULL, 1840716017189797151ULL, 14253930935930933050ULL, 17260646180584086994ULL, 16463803460626779249ULL, 2095178263557159930ULL, 14194155688695516401ULL, 14104122209801689782ULL, 8700889757688053650ULL, 15715857349073649318ULL, 11075367046159303384ULL, 963046205704765222ULL, 10383293276269327833ULL, 13586076235105499504ULL, 17185087463987752085ULL, 18063372420796646872ULL, 16517275838011591014ULL, 15632805699274799353ULL, 14973768003884950962ULL, 3549388992979422549ULL, 2942134343540767115ULL, 16098508825355365027ULL, 3323808569965745094ULL, 1704607023681899734ULL, 17319616796868763224ULL, 375432592521788815ULL, 5760273901536330192ULL, 4090790746191589671ULL, 17254769855745716197ULL, 486768865300230170ULL, 9301706573264932713ULL, 1139152886227878309ULL, 15735547617260398374ULL, 3594616469006494624ULL, 7921668390096544384ULL, 3008839812542216275ULL, 16593937057240667212ULL, 32284676426371340ULL, 3421612789355890815ULL, 7724482408888646528ULL, 6654813525586852917ULL, 16836249105172680355ULL, 4247914132627837786ULL, 11996385595291419541ULL, 12907171797899493236ULL, 17923767388292200031ULL, 13897532453007999147ULL, 14765157715938141601ULL, 11111239236551554712ULL, 7862008834310513143ULL, 18293205773557933630ULL, 5556466416148372685ULL, 10211100778469828838ULL, 493525773798057518ULL, 14943471122109586740ULL, 14443654341571537589ULL, 13897968727775390301ULL, 15661069127571872568ULL, 3603861280518876179ULL, 5657079779138665910ULL, 11585322321711042801ULL, 17768548524682436543ULL, 9035138149036189622ULL, 9855706826507483495ULL, 1163103078605792125ULL, 16822413654290555685ULL, 11217193697645937540ULL, 1716618719896887530ULL, 71266925529139540ULL, 11786859973617655103ULL, 2917892999575394459ULL, 16131768174980169434ULL, 11159537061590158982ULL, 2111639572037176831ULL, 14392524017528474368ULL, 4341981226786043515ULL, 1220045281197240578ULL, 6690135658734550109ULL, 15950447617188370613ULL, 9008074978545566037ULL, 14082049708616927281ULL, 10967847919791717902ULL, 9003056100442792240ULL, 9598570795569517452ULL, 17104088356956588319ULL, 8536586054020240228ULL, 14853849630443574947ULL, 500879355572580303ULL, 10046374512757336728ULL, 9225803724444505731ULL, 13283648443209110879ULL, 7122452886376861180ULL, 18194711026513019100ULL, 10133803816096608256ULL, 11836739294723586683ULL, 1035744546769052370ULL, 9922366699099137817ULL, 7393420143186982672ULL, 10544829186597024313ULL, 13171880853985201048ULL, 8040102697882199382ULL, 13700104392697655031ULL, 734050986077465865ULL, 15176283758881611702ULL, 96494418923886932ULL, 2736366661004030445ULL, 12361717417702962500ULL, 9404793695961591029ULL, 1323795654699027466ULL, 10209213992611864484ULL, 17437138238579047048ULL, 15289960472621792479ULL, 15038180696495039679ULL, 2914447783388839241ULL, 15843928410797591259ULL, 10514139925996907745ULL, 6692277600186155441ULL, 3857186284943242586ULL, 1727361650005981655ULL, 17935520478862402188ULL, 12668675625860210741ULL, 11570535653246665849ULL, 8862480513639759917ULL, 9754967970471888710ULL, 8028269968669568115ULL, 9457487591245134004ULL, 9391978495557779020ULL, 432687230653698492ULL, 8281805608676996124ULL, 9680558705220163702ULL, 11815699030930260668ULL, 4696553301762525581ULL, 12191703266865224025ULL, 10932284033808189413ULL, 17955387299755657539ULL, 7765842881924368718ULL, 17967782035427808474ULL, 5900297614114100070ULL, 3549149894511081576ULL, 14176073969488544732ULL, 3621748162985565988ULL, 8554941796094555879ULL, 10922985632040796561ULL, 7535925828011626524ULL, 4672577974505829359ULL, 16894602154707159322ULL, 860217380718535236ULL, 5553393167245659064ULL, 11611029189171597176ULL, 2934744362212727054ULL, 15380557854424639211ULL, 12818908336130698227ULL, 2123144833909283852ULL, 11320085460890676657ULL, 13134082906746001997ULL, 11345243675496506805ULL, 7318824140666418585ULL, 12624419592529831517ULL, 8477500727986965571ULL, 3259776827704926790ULL, 9562874361835031766ULL, 18104207838650914473ULL, 6438635252556897213ULL, 8306992085424275369ULL, 16415174088350956575ULL, 15681212344933142812ULL, 13846817173868647884ULL, 4129885096112629754ULL, 12714037692644669515ULL, 5669721918023650147ULL, 10332269748833396841ULL, 14036436395324823434ULL, 14997023042778913309ULL, 14983853063737406531ULL, 17101693105276283801ULL, 6998928353304973586ULL, 11760290072981044746ULL, 4677739490219384527ULL, 3745926692709784941ULL, 13642573358654372992ULL, 15707082301286080379ULL, 2788051994698637345ULL, 17377585851599032569ULL, 17376392326093277434ULL, 3068579427660274801ULL, 6079524568539044012ULL, 4375456885136429053ULL, 8167579976708058561ULL, 9109065524326828106ULL, 8847103179269665092ULL, 17232374073463673435ULL, 4062421742108180330ULL, 3167438977687498536ULL, 2163267231947563279ULL, 17376866563795550126ULL, 3153372801006664537ULL, 10373581928819888150ULL, 8250394076845661716ULL, 11860753088711492073ULL, 4233968564218513597ULL, 12481581661439360825ULL, 16495835167522175515ULL, 18414649866935522162ULL, 1903163314599471107ULL, 13027859011763496119ULL, 8896292901086887490ULL, 15174661417798797761ULL, 8504339157127487465ULL, 8453925592145746277ULL, 6374604919926706321ULL, 15179594231668171679ULL, 2963840706624820138ULL, 9303117877193312700ULL, 3483494314882833135ULL, 17784178289958512937ULL, 3031721478869120641ULL, 4609847194791492140ULL, 17166997457069387010ULL, 9444504467686143002ULL, 6271170356940453894ULL, 17487908678659842965ULL, 15317494335457263658ULL, 1182610092390832446ULL, 5935271851307418831ULL, 7727886665915933998ULL, 454200888729186931ULL, 12791517825571331884ULL, 18198538789413397493ULL, 2215040003277972344ULL, 15424679668598492114ULL, 9854987959370975231ULL, 7940679909968567560ULL, 13771584842524801381ULL, 7023403070003419429ULL, 7344855298028115259ULL, 8419211386511864272ULL, 1333806107532203782ULL, 183354088383355439ULL, 9773162973099835149ULL, 8020673822942330678ULL, 4992886405398866470ULL, 16345779068781090887ULL, 14722491054754389054ULL, 4576955606816027523ULL, 17604097467096621874ULL, 7918188732866015129ULL, 13165548585025934618ULL, 8274779233481886237ULL, 6062786993582542042ULL, 17139776791299126541ULL, 10822759937912541795ULL, 1958073158696438166ULL, 8966270733143033845ULL, 11688192419318207531ULL, 16323366147386797698ULL, 4515059919199649508ULL, 10914154998019845787ULL, 2599899248848360820ULL, 5983074539588494639ULL, 8527912050202368362ULL, 11159966218756818568ULL, 18271294946948351015ULL, 18305712251398725854ULL, 642524466010624959ULL, 18292005297901477484ULL, 890054661296057286ULL, 2974106595861660097ULL, 9060518230027282149ULL, 6768084551966558121ULL, 10445824701136367595ULL, 10427527024141200938ULL, 8154189548674371782ULL, 16088943159720929477ULL, 5425312852382594669ULL, 18173883271754751771ULL, 15082009850260307406ULL, 3387553581881884273ULL, 5115172809763590366ULL, 7653945281869207867ULL, 609106497710682503ULL, 7690402719979114623ULL, 9810288536073514827ULL, 17586323831141690124ULL, 15828597198533340748ULL, 16181039404381803771ULL, 15917025184660064114ULL, 2520111147273955708ULL, 4981046865102453303ULL, 5986967429679794112ULL, 11608577891495764142ULL, 13148720240238505632ULL, 11482696272457418307ULL, 6694434523878309067ULL, 16103580274653275342ULL, 5929827671661831745ULL, 2812038691491496836ULL, 3671483801832904958ULL, 4868051210139487664ULL, 17313306531137796902ULL, 6664264298130342748ULL, 14168708499334435787ULL, 17450646835869820632ULL, 13236683175038612539ULL, 9242318703717094671ULL, 4553804930871631271ULL, 10751339769153508878ULL, 10697974930743060307ULL, 8856685116641403638ULL, 7180223139314881739ULL, 17755065622354217909ULL, 575400529060771341ULL, 10930490573439720237ULL, 16973772172559421444ULL, 18276224364254662240ULL, 4800882629767626077ULL, 13960891486251827794ULL, 14441537660376559938ULL, 6538689969154493913ULL, 16516562346894563247ULL, 13288641520473481394ULL, 9630431586182629945ULL, 14990377005182345611ULL, 3643364070516786588ULL, 5896246632428518249ULL, 13483925498841891079ULL, 60805129681855169ULL, 779179576490234872ULL, 3874840407299712570ULL, 5628017066921385753ULL, 13880727252362387537ULL, 3065143959891218903ULL, 2090989375596490548ULL, 11651778952894044659ULL, 366729735634408585ULL, 10561471638962447748ULL, 7656715166813668741ULL, 4313202424079034079ULL, 9626162760195576614ULL, 9283381549266468440ULL, 260804798711224734ULL, 13935819933372908322ULL, 1176694405652734032ULL, 16850037808392704109ULL, 4263551166390619994ULL, 3909891896942740357ULL, 16381974289227186030ULL, 1752833768933025333ULL, 14152378374445194947ULL, 12689852080095404340ULL, 13954637176368028688ULL, 3107803324905289919ULL, 16407232286541083176ULL, 17055089170653843365ULL, 11106351196553049343ULL, 12200481917389709410ULL, 14178332452302952958ULL, 10222137491389786854ULL, 16402853202701365415ULL, 13180175198509732500ULL, 1455166639295552768ULL, 4979737427905538267ULL, 14373133390957169598ULL, 4754395508330068134ULL, 12693885839888888936ULL, 14746109286893937638ULL, 8256443300432846081ULL, 503470829401805495ULL, 18060283403484840865ULL, 223111905554076695ULL, 9807394769085160210ULL, 14524153012332703177ULL, 7972467419517226752ULL, 9481948494865433649ULL, 6421490798076910573ULL, 16506009181752747757ULL, 6544188641874020091ULL, 12431540855806523398ULL, 6618245978658207123ULL, 1904194992114517540ULL, 4690194968617477900ULL, 14108188800280108109ULL, 15862787664054268077ULL, 11567730374266554581ULL, 566460245240579986ULL, 9696200961293924155ULL, 17827591708672731451ULL, 12297585812252232655ULL, 10918248820356693534ULL, 7943078022158449782ULL, 11439166569592681111ULL, 16953037697774986329ULL, 10099035350833820881ULL, 5426307685524604809ULL, 3038650040793756099ULL, 3553537778964778396ULL, 12565565344072502413ULL, 14615410673750310221ULL, 3270511329827310046ULL, 2210873698912433630ULL, 4336211781059514708ULL, 2526856940938685553ULL, 15356564490459994656ULL, 13305253367388893541ULL, 12041457162714389802ULL, 17980810537855664424ULL, 16183733413348725174ULL, 9206290173512442238ULL, 1076087784552803205ULL, 13541769100454246797ULL, 16691089995012038641ULL, 8097136923724890918ULL, 12338178725452333109ULL, 8024306894580752178ULL, 10738610715893273420ULL, 705310671717682885ULL, 10896553797249757487ULL, 8548402185506907215ULL, 13975419118295597171ULL, 10011574200169311036ULL, 7327120261626698754ULL, 13001361845117582167ULL, 14829199722809107024ULL, 15866458406886713890ULL, 9740995454791413279ULL, 15903821788789179873ULL, 950498311103338347ULL, 11309823363426037284ULL, 18164376950504272441ULL, 9557806520826308848ULL, 6201795305747716642ULL, 17776230082235232674ULL, 8520000575969616895ULL, 8781445267061036756ULL, 270538119429159107ULL, 5925739443896034094ULL, 9299479815492576194ULL, 17524856212641035826ULL, 1149504369472189142ULL, 3249305323221474438ULL, 577859395740588557ULL, 10374486818177547603ULL, 2722648297481868677ULL, 14343991397745315524ULL, 6374027184725683455ULL, 1445767004720311312ULL, 18269627072922875919ULL, 10330562213031302803ULL, 14840495659246433667ULL, 9164112642890598023ULL, 905890393258871819ULL, 804640119687148870ULL, 11229379585982321658ULL, 17632457556982827740ULL, 11163369412005092930ULL, 16691530353120304544ULL, 14681395382902058857ULL, 15418086763916363030ULL, 5361599495544208911ULL, 2456891913702392502ULL, 15104703531100799955ULL, 10246138079115047701ULL, 10771499922132581514ULL, 11910580552051196964ULL, 1542036741617809581ULL, 10680784062588495303ULL, 2134796969204913051ULL, 13652765670422105814ULL, 3310537599618580283ULL, 9556951871404994733ULL, 3566352538807433775ULL, 1436607380454235721ULL, 5753428088816562753ULL, 670825264415961892ULL, 13949135213803925436ULL, 2273524394325797610ULL, 2345888976021271491ULL, 7364392071496461927ULL, 1378818006292379959ULL, 3973257568603402925ULL, 858112370402714433ULL, 10401676489325557336ULL, 1442847686498859522ULL, 48253065366238439ULL, 8507137633570275834ULL, 14273498263780863071ULL, 668897021147616397ULL, 9006159274974020521ULL, 391970115979121381ULL, 5428839793638170898ULL, 15733486159271243115ULL, 7578051076398556408ULL, 14276469336238872885ULL, 4565010008338206411ULL, 352482975037856331ULL, 8570637326287613791ULL, 14440093320447165059ULL, 13256954855142342787ULL, 5977274645827402825ULL, 12814842766738246412ULL, 11657799148863215862ULL, 3210024351740157418ULL, 8205514332702987679ULL, 2511486300927130584ULL, 3329517204047545754ULL, 8208330820783079447ULL, 7931954681266035409ULL, 4035676213893141113ULL, 10700429197004267608ULL, 5290765635871577917ULL, 12089900829150832754ULL, 6866951294288214810ULL, 10972007432955324471ULL, 11391491267850750618ULL, 6642828344251534284ULL, 7987689048966149319ULL, 17154508030719858176ULL, 9638195347144382594ULL, 16868501006432224047ULL, 15068183064820139480ULL, 11672298211180923873ULL, 9909613611469682386ULL, 6144789793064789625ULL, 6596040658930808244ULL, 14073765603350552011ULL, 801625908059292136ULL, 1543419288821605796ULL, 7618558260021138933ULL, 10317710018521732393ULL, 14884045360921915815ULL, 249398806962672218ULL, 17109400482621063905ULL, 15287297623279451806ULL, 15922035355180224998ULL, 5407580345299905699ULL, 4888345365163850603ULL, 11401845357898266478ULL, 9312229415980681437ULL, 14240988549072253043ULL, 2449500326371918447ULL, 16572900543257442708ULL, 18394951499126178644ULL, 16615698769896751355ULL, 15939776182072819585ULL, 6239820921339887109ULL, 14106298817871188464ULL, 1119534021120220290ULL, 10276103830559998049ULL, 2768301834634616663ULL, 5734016429524869205ULL, 7899565703570850370ULL, 6401225177640965672ULL, 1467787322467172925ULL, 8115794165864699575ULL, 3232265451663649251ULL, 16028194044004521014ULL, 752256321266746425ULL, 7659909124069369276ULL, 7285383152311811937ULL, 3542600618127674019ULL, 1288605986478417432ULL, 7344349918424130132ULL, 9015524238925531129ULL, 16539369502908468095ULL, 2766565367136064679ULL, 4392302990477963244ULL, 15863984353702454852ULL, 13791866813886728963ULL, 923590485688113219ULL, 17032003830767507451ULL, 3641942379860706017ULL, 1732048705832596873ULL, 12157161182277547843ULL, 5582379276417183829ULL, 3303177557290278141ULL, 11424580034451257541ULL, 6852534168831884751ULL, 11082472998063359532ULL, 4332618493727576221ULL, 18260383652885740206ULL, 12975018635334178099ULL, 2157658476816337610ULL, 4668865189510836563ULL, 8344845724680500165ULL, 11086012763777664897ULL, 16808837058134265130ULL, 16518852372734015275ULL, 8074692171060694622ULL, 10106853857940414860ULL, 6074900310288870771ULL, 11533124005983658392ULL, 6791838022926523326ULL, 257359508173027083ULL, 16547861393292780670ULL, 4334494008002184596ULL, 11394437127925947894ULL, 11484941705111361795ULL, 6408200098188122168ULL, 7510749929837998206ULL, 15018084551576309451ULL, 2525072741745223731ULL, 16660194758927029988ULL, 6494059677009724753ULL, 6532620775805873975ULL, 2614131097917440984ULL, 3982637015137686412ULL, 2525137629853604617ULL, 2873962527691607365ULL, 14205567674539691646ULL, 7013578251571401668ULL, 317128927781179106ULL, 3562024274923344129ULL, 2899629716801732627ULL, 11464114140357671142ULL, 5528996102125106080ULL, 15465187977971886225ULL, 10797879231554523021ULL, 517728393809434994ULL, 8355390818157937521ULL, 10808933191861522623ULL, 3980201697087125198ULL, 8068670330842307807ULL, 2206034729509733472ULL, 16176000828643217616ULL, 4107326124050422003ULL, 4261302568769914495ULL, 6573834983674741731ULL, 13183058754590453246ULL, 14863609730666696900ULL, 5428219860165521118ULL, 11078405938774475922ULL, 15888403091728211021ULL, 1414823218123123584ULL, 14449117115146110260ULL, 9415342835046915377ULL, 10082244795927085927ULL, 15321905527953406138ULL, 12772869485077058997ULL, 14786676736977359788ULL, 16366954654950671646ULL, 12278935412389620597ULL, 2380442439994500936ULL, 16744995341707002614ULL, 17163218122998542730ULL, 9752949850154018131ULL, 6725744020675158348ULL, 4801976868823386455ULL, 16952534183914676666ULL, 14851567991973766085ULL, 6086792625117696280ULL, 10590949811652078026ULL, 18321518096411518845ULL, 11350072612512648182ULL, 13871192325330467452ULL, 9162981778612802627ULL, 4424859174682681678ULL, 6599284937536033860ULL, 15484019390316064415ULL, 12953848416864586519ULL, 517099554370685775ULL, 7700207249825981304ULL, 6351479021546008736ULL, 4014591873230643871ULL, 11847399081009018567ULL, 559028932686483286ULL, 2173777069343479726ULL, 7419754410852462129ULL, 5863718589994020018ULL, 5570315961294345840ULL, 11452312027470944883ULL, 7678540332443919901ULL, 10655672607233785502ULL, 13548964556219507811ULL, 15703867487097708893ULL, 635431802677809170ULL, 3511044843439792549ULL, 8749939820205758469ULL, 16641100851398354379ULL, 16579913584853013410ULL, 2860000890027061143ULL, 5686971079923259932ULL, 16704313226797980845ULL, 12465533777907440402ULL, 16721517972089767726ULL, 3081085273849832827ULL, 14454246431875833643ULL, 13711104064454495754ULL, 13556721592383761295ULL, 1203917620800027117ULL, 6565059229007852048ULL, 4091128231014581085ULL, 1190503015868505561ULL, 13015962807729430070ULL, 17425592565615841784ULL, 4645200218815941165ULL, 8024829510281657164ULL, 17523216612475719970ULL, 12542739339666551196ULL, 9892890286828436137ULL, 14459191643501879690ULL, 17211797760636115062ULL, 2359497679167552654ULL, 8912223334063929535ULL, 7941508769180229423ULL, 6520978387474968530ULL, 9177574953959405154ULL, 10570524388057156454ULL, 9591476736548824451ULL, 5365477835702836602ULL, 18282708219953439541ULL, 10675405260882325529ULL, 4111364082651134865ULL, 13354971593163146200ULL, 15278380379866199474ULL, 3916796757054548741ULL, 10255301153118473226ULL, 11218818017395378451ULL, 18199972171316199814ULL, 15835192868053046121ULL, 17263684841549330914ULL, 11062866309293624331ULL, 6227321897897629496ULL, 353630029907650271ULL, 3523400764112538118ULL, 7201649299779298743ULL, 2395129313921031310ULL, 2148285522302009459ULL, 8984162342722902626ULL, 438870319050497926ULL, 4784542304497524290ULL, 1535296304186293308ULL, 18156227382645116207ULL, 9116403414927450509ULL, 17682476890377988001ULL, 7342041589427523494ULL, 10465669611741865418ULL, 7587852749592860317ULL, 13970362271603846881ULL, 12806994729369312160ULL, 14576012959627738556ULL, 11344411464570745071ULL, 16141054063446615314ULL, 17417751941543466126ULL, 7167537835446755389ULL, 8791130914220180596ULL, 14508464268795815076ULL, 9989025427981015211ULL, 2324712211473518453ULL, 14393744007810057723ULL, 4154667155580299997ULL, 14397495111547232209ULL, 14614902884941517187ULL, 14103608014145752644ULL, 14247888300446498473ULL, 6625160353677663590ULL, 18340761418155558898ULL, 8156477033561538569ULL, 1420800945002467066ULL, 5212269837197149039ULL, 10471882435706547080ULL, 8012169697907594461ULL, 7095733166245235782ULL, 11042078796808228086ULL, 6168551548506037618ULL, 4590970638561285765ULL, 4779805325908989585ULL, 7237003122666740608ULL, 6054886848806457069ULL, 6949156210491550655ULL, 16285281660892720973ULL, 16410054239032241285ULL, 3273959370146289803ULL, 8470326293357303292ULL, 16422997300759042657ULL, 11620358142775987774ULL, 5029604686597303618ULL, 4494702051450930131ULL, 10325154349795834533ULL, 16997810131993466409ULL, 17486259595113526467ULL, 10598865875248242901ULL, 17902976944493522212ULL, 13854078177218706875ULL, 17888430304907373152ULL, 12685139671565584444ULL, 13833128177232553500ULL, 9835699045772546575ULL, 5668178551297289676ULL, 2340134241526441475ULL, 753436891757673272ULL, 615698389131366316ULL, 10156688433854455047ULL, 11525289401331658219ULL, 4881738137926906800ULL, 2058205709817021123ULL, 3991120745420612947ULL, 7639248091924834630ULL, 13202106123103473578ULL, 17654881816051191290ULL, 17278668556136726594ULL, 11570432399481153098ULL, 3988389909121239202ULL, 17091622881582178462ULL, 4646208563474167604ULL, 14106451295779125672ULL, 10320120576645331914ULL, 12352845852427107953ULL, 14899884351265122265ULL, 11903870881002315385ULL, 9325617668189469648ULL, 14598981506432428056ULL, 10372459402038349496ULL, 6156593784171166651ULL, 12545625877168818339ULL, 7306863242197644549ULL, 666678194678414598ULL, 989905834432164197ULL, 9401909345577780134ULL, 11138299130673465283ULL, 18091995195105618240ULL, 9655077302136646914ULL, 16629827344897372840ULL, 3277743911245713220ULL, 3731260043836443598ULL, 12028700701332907689ULL, 2755517292365471353ULL, 14117484903723218984ULL, 8405966141343551147ULL, 2460365840816978098ULL, 3084698348460621705ULL, 16877669134537860219ULL, 4692733610775533309ULL, 6687894846888649943ULL, 18200485782319732931ULL, 8115281591836015458ULL, 3505776172882505081ULL, 9455074786481930389ULL, 868562879685071735ULL, 13706664967368568336ULL, 10919741247013111758ULL, 6834630531859180434ULL, 4621407577930771075ULL, 10618080096334227009ULL, 11437746103811309264ULL, 15675833321805422940ULL, 6075209348719321507ULL, 6783084403656000602ULL, 5574471127885898972ULL, 14489386878401071203ULL, 5562663845846095512ULL, 6176943955488204952ULL, 10108745001547036152ULL, 7085798424112915491ULL, 17101583329010070816ULL, 10265144651245083245ULL, 9229795126994901711ULL, 13121942059182209427ULL, 5380822570802827374ULL, 6213420290406371390ULL, 16994135099500516479ULL, 5311729593068917780ULL, 13914474594064683184ULL, 10294280742210115419ULL, 10676549584223602652ULL, 14964626312377022323ULL, 4060474717972033032ULL, 8655174219459165787ULL, 3933760189253469459ULL, 1934553336086304369ULL, 14999594410372973815ULL, 9721720883676649034ULL, 17995882330449937817ULL, 4754377650026209282ULL, 5047861918011100580ULL, 7942728043298293248ULL, 163571059010590768ULL, 18364405827107400850ULL, 5764465207084065842ULL, 17361856150526161203ULL, 2203504364851205526ULL, 16639257772366718318ULL, 10261892135711281702ULL, 5752607922798510416ULL, 7483809040402288727ULL, 12671556035681699979ULL, 3173398710793036878ULL, 5608461027089012844ULL, 18433194427043711306ULL, 4853344608313074695ULL, 6960475574329768041ULL, 17856758933408630525ULL, 10375604847958350097ULL, 13241904152391552449ULL, 3929901330855494012ULL, 4262901104370426306ULL, 6789561922862578032ULL, 8967186525299355515ULL, 4533217413093813275ULL, 5643809189292007051ULL, 495169116096013531ULL, 12789788495048968654ULL, 3671592867726928597ULL, 11540188521144517538ULL, 2542060706027564144ULL, 8503537316519248735ULL, 4041715278058699385ULL, 2371002316211506661ULL, 6026778051648044722ULL, 16224603993537761311ULL, 2715046259776181603ULL, 726517895425566240ULL, 15808372696473412102ULL, 10923341336360680008ULL, 14376117752057142530ULL, 5794188995003200529ULL, 7112388884803681907ULL, 14631534284611984966ULL, 18140458773366175981ULL, 18108017203931876231ULL, 16411813322034815824ULL, 9425736345428388810ULL, 681769034669892768ULL, 17302356545317662801ULL, 17229061363757438192ULL, 7462688934640402880ULL, 3873831564485110343ULL, 14447655290514797123ULL, 3204697688175724303ULL, 9017250591306492777ULL, 8606967019226274914ULL, 18341515903210492860ULL, 12448428375904165220ULL, 6852846706383550083ULL, 14849962227781994194ULL, 15695405892567116701ULL, 8298709553165075454ULL, 2068932383403115112ULL, 10282330483807844168ULL, 11128666535570799066ULL, 1873046192768279226ULL, 10398361192866809411ULL, 5124744001566603863ULL, 5685746340671115365ULL, 14218408944825783065ULL, 16797706003748462146ULL, 17207563922843033620ULL, 4982395803794514206ULL, 6047267691529793221ULL, 9883533531288749712ULL, 14216807392798412691ULL, 17307295394612055334ULL, 6868286489288706449ULL, 10635566991257996815ULL, 10162335767850313279ULL, 6620076281871975852ULL, 4026190233492150127ULL, 3124297855929917761ULL, 7521661794930467711ULL, 1066340990602657303ULL, 2636494999739001924ULL, 12032146588970726383ULL, 8279038900559342741ULL, 10008892957410689668ULL, 14202650502531736817ULL, 14854826779434161303ULL, 4953853040606446492ULL, 5379966367891113702ULL, 13015822609736681502ULL, 8543523156850098962ULL, 14043677148179279456ULL, 10559053636038629705ULL, 2086308554844071497ULL, 15824510410079630867ULL, 3227714471131370975ULL, 9522342031796734383ULL, 16158673861979479153ULL, 12038157437040981119ULL, 11895166872864069099ULL, 1197811376683642228ULL, 13287662539885843563ULL, 9544872781832099071ULL, 4222233824102404110ULL, 2133998761859595484ULL, 5819494556654210069ULL, 14816211808724849418ULL, 10176886659237434172ULL, 2501085927953676756ULL, 6112568572292747443ULL, 1220127868369435755ULL, 17005803960319347306ULL, 16787616116892133575ULL, 16207719857604131382ULL, 13594948105060827697ULL, 6530612604039981252ULL, 13977170874341892019ULL, 9042970266133777753ULL, 11234856800208830769ULL, 7767854113364544268ULL, 1749330339295278183ULL, 14060641551230624404ULL, 11352950071080864403ULL, 15471811346457819845ULL, 6796164306155903940ULL, 15303084408201456249ULL, 16707985054069450864ULL, 15348527471740531690ULL, 2054486066928282679ULL, 6629789268351040246ULL, 4139725410560897941ULL, 10789060182933816928ULL, 14649618792009204095ULL, 9533762887844972792ULL, 9055181833376373829ULL, 3704143350857419041ULL, 13165788964259745440ULL, 10504542472085275604ULL, 3143544209934004723ULL, 16699442099531285552ULL, 17386572975779346746ULL, 14440354485847513184ULL, 1411337828388295592ULL, 17586972803408651810ULL, 6722710933583173143ULL, 11281233424233979571ULL, 15838886002743246952ULL, 13015085553311968107ULL, 15493397730376770996ULL, 1422785807327970234ULL, 15748561593943897087ULL, 4431934102445986024ULL, 9978616396639292812ULL, 16469013057559674312ULL, 7259596544646623366ULL, 13957272777987050596ULL, 14377859761745729324ULL, 9500915974263946146ULL, 10958885459561016085ULL, 7510088822976649866ULL, 14856037375915845631ULL, 6657124356498334900ULL, 6432585051348612088ULL, 7472010280626180191ULL, 12650712874408069031ULL, 13307771184331469386ULL, 3606757056193547838ULL, 10753225301487641561ULL, 17127960234419583228ULL, 12662871163771014089ULL, 1361696617464743971ULL, 15310290981014831989ULL, 5049631916612880907ULL, 10291163003812672231ULL, 3728459600828211806ULL, 7113899679274835967ULL, 1381437621560877367ULL, 14678876090558848776ULL, 8572205611699271354ULL, 11656076436850098712ULL, 3353107229658858410ULL, 14977880680140355898ULL, 1154230544477699172ULL, 10344611414856468298ULL, 7531078612677779707ULL, 444408462496519394ULL, 12178076787382474551ULL, 16676709090730842448ULL, 17441308824435700088ULL, 5633032710809931202ULL, 12159146730893835319ULL, 5979397431393513722ULL, 7814705992690851118ULL, 8161366737147755117ULL, 6270401257124382187ULL, 17457842949730015190ULL, 8869503855610046715ULL, 1367321540179147359ULL, 3622066133180912371ULL, 1047618614043992660ULL, 16424646758938930201ULL, 12787262320080161173ULL, 18177069184701173844ULL, 12958402103292464894ULL, 2877816285340623922ULL, 8074164302424695822ULL, 6323961188256795897ULL, 1028001267681189916ULL, 5714370383653021262ULL, 15486923617954395422ULL, 4625777142890033834ULL, 15990680293261317289ULL, 11358668678364618319ULL, 8753696579237620639ULL, 12183302499610977852ULL, 486578702830777799ULL, 2159074250987184315ULL, 10443546954350278478ULL, 2968794296852091770ULL, 5373685372805190878ULL, 11948078885379084897ULL, 12336292153750786897ULL, 16807383662567374304ULL, 7759300378778574973ULL, 4485931103045023657ULL, 1544942395645729893ULL, 1297496278182191271ULL, 10956050669054913639ULL, 1769291150319953740ULL, 14721008120084954420ULL, 14999752571577229826ULL, 17820584995130852873ULL, 7440651447095946766ULL, 9987457956715433125ULL, 9889642071555538770ULL, 12048632758099208228ULL, 15423504582711899700ULL, 5552781593205844370ULL, 18395650918324890495ULL, 9279219951722003324ULL, 16685779012383685843ULL, 13103667156860588797ULL, 5474263352075413784ULL, 1802303426534537993ULL, 3995963125335748242ULL, 16177465519771071462ULL, 13679989420549895228ULL, 18127744656978625365ULL, 7123064711227635813ULL, 9747720381598450822ULL, 1473299526565814178ULL, 1666034491430050154ULL, 7704402999060839246ULL, 767546777413541285ULL, 10005388627283438978ULL, 8229157422178385640ULL, 2214479335261691753ULL, 8224278484242953448ULL, 494258966297178159ULL, 4969387893030431675ULL, 535542922430921712ULL, 5937877118203968353ULL, 5599462575412553881ULL, 15749110530893874790ULL, 18353412484654647679ULL, 9079979948107713375ULL, 14888061354029764539ULL, 10016664043768009830ULL, 2031817058464698196ULL, 5797460757847049188ULL, 7872912553647104140ULL, 18289069232539508674ULL, 5215145459368815265ULL, 5864383471831836378ULL, 8491208422244948316ULL, 7587037325524155271ULL, 11949877882217158810ULL, 8381598910683988369ULL, 3715937119397627817ULL, 5377277016270200372ULL, 9099558031350088018ULL, 367144875670768153ULL, 188648165405249319ULL, 10289329211846470265ULL, 4889529454063682569ULL, 6824053921959398429ULL, 9134396574212887182ULL, 812667774815093473ULL, 10040546039401489200ULL, 1986760338268682633ULL, 7958742571910822443ULL, 13687476500541062980ULL, 4830191918830935317ULL, 9037921931202846380ULL, 1531696582914947421ULL, 15130306086294689821ULL, 10603032535962039417ULL, 14467466580454892187ULL, 6719806077783739719ULL, 304832169538977902ULL, 7270826855766565060ULL, 2986615780328031733ULL, 6479707210440683016ULL, 6727141681144320989ULL, 16546030572016867954ULL, 13718044368284317239ULL, 718208230537977406ULL, 14017599392756460177ULL, 560524405952296676ULL, 5637326204055771419ULL, 11550317109918646290ULL, 8518204115800444519ULL, 13892384781543623850ULL, 708886165990047519ULL, 521856816411877008ULL, 12224065831913282030ULL, 7100909301345564229ULL, 14375129012709244172ULL, 8292500769862814408ULL, 14597548536564108634ULL, 9710027768722536569ULL, 2789057975584840068ULL, 12208698343893875160ULL, 9274181188426044820ULL, 2587524840718576616ULL, 5283731783253342481ULL, 2674321220718462281ULL, 9866410392458578854ULL, 15258347057534972137ULL, 16056419307937022392ULL, 17248897535063579233ULL, 16693967340197181122ULL, 7370802758592081787ULL, 18110513780739605330ULL, 4182328690942437781ULL, 2280530535003795327ULL, 4625067236323182685ULL, 15039646624295973319ULL, 1233105111039262830ULL, 1049898734546971236ULL, 7438953705203579140ULL, 4890034534460494658ULL, 3682896501995365299ULL, 1287011115747498929ULL, 1943857222626688382ULL, 14168264380826889554ULL, 15936282183338676693ULL, 1801363654962061494ULL, 11490473960945601728ULL, 5494027806580750721ULL, 4226755751436839168ULL, 12968386155438644777ULL, 7624286457384852440ULL, 11947602136291652975ULL, 4118832019624349464ULL, 1261366687163294470ULL, 12313505250481360316ULL, 13382455321341479390ULL, 1693160180553584595ULL, 2432453324166190869ULL, 6010846956850374986ULL, 13569885434517693700ULL, 14297911357958435248ULL, 916457708689693757ULL, 16343255615852500604ULL, 17080809607049040174ULL, 2344620405691926760ULL, 17357412027177565255ULL, 18220226561191256701ULL, 11079770241377136693ULL, 8572299553537558830ULL, 15045890847099503773ULL, 14864972512233323605ULL, 9457012794167444866ULL, 17496304225090418576ULL, 7167007156644073831ULL, 1232518562916969827ULL, 15231965457331640472ULL, 5697700595045133405ULL, 15241798555541989999ULL, 14027209396631041100ULL, 15120631807759470712ULL, 8331724800817204913ULL, 15745827131014900309ULL, 14281519675380817768ULL, 771470964839806020ULL, 2177186744775860483ULL, 119850466036019855ULL, 15051040198205640499ULL, 15360149764912089938ULL, 6340787344213891088ULL, 10347258672759644663ULL, 16890381986084016975ULL, 14746624262240377603ULL, 14953834377077690225ULL, 18168099855836039297ULL, 6840777819954990667ULL, 12240162065902994382ULL, 5883854292069091333ULL, 9472963121153353366ULL, 16701356703380165783ULL, 13247064146881705687ULL, 6459488422401765837ULL, 15296627432046994254ULL, 16275445701590908538ULL, 18441963025624205421ULL, 13918579586859537738ULL, 3446987008605642595ULL, 17807988251960307333ULL, 2822689808379047393ULL, 16976957736821297431ULL, 3873772909797598787ULL, 12383678519331059026ULL, 2658988696945629519ULL, 17792154146330693620ULL, 6949461939865044520ULL, 13290656350019450373ULL, 16362310026134112967ULL, 9313382589157530527ULL, 15499936787890509571ULL, 13553142720622741706ULL, 9637312203984147193ULL, 9821959006496886068ULL, 14001161369560815590ULL, 3613211846255867725ULL, 5887497494808230514ULL, 15649865485915700198ULL, 4051758046024016378ULL, 1939918942921679444ULL, 10003485229032038884ULL, 7330156478314427490ULL, 109866288851925579ULL, 13096780538080228279ULL, 249235540300039238ULL, 7308788405859324890ULL, 10911913854085679482ULL, 11479040470701642191ULL, 3283504501904119019ULL, 15350519847080756143ULL, 12013249357803038388ULL, 4929729200845624878ULL, 10276438134663857497ULL, 9829506236079320901ULL, 17535074031916558935ULL, 10439025358116919108ULL, 5360597719630852057ULL, 6095781034833298859ULL, 3561753763610714882ULL, 14436466097168743024ULL, 10202632279755063011ULL, 5958698481843734627ULL, 359471770145820431ULL, 1826881803240972795ULL, 2407399441255625035ULL, 12606464042774544654ULL, 14139007083642608568ULL, 9106766577492813516ULL, 1738946112944729211ULL, 7685584802863232735ULL, 11865633484170735390ULL, 1242439607532162101ULL, 13749864526909002734ULL, 1833538451436110733ULL, 10617090282479668741ULL, 17450744431157176799ULL, 10422915670600694510ULL, 11444353070626684732ULL, 16236369424370065914ULL, 4356651211554328663ULL, 7538167813800904086ULL, 9337339218448769812ULL, 16681992071139702958ULL, 8239986442678156400ULL, 16255387639910107790ULL, 2993106237016221406ULL, 8425252826859140803ULL, 1861840703178678587ULL, 9739597023135480731ULL, 14726317394379993399ULL, 16561961960743295454ULL, 14300172422492964432ULL, 3576425345824420531ULL, 16445859096081052660ULL, 14872623052195781100ULL, 12454120927638943339ULL, 2247824947767592425ULL, 7700551132547028956ULL, 1170920657933467375ULL, 11824927514276724281ULL, 7331051236830922220ULL, 15717323351549677341ULL, 4890809363339690114ULL, 3865139405343396495ULL, 16057600699038890538ULL, 5234374798738346701ULL, 2632032810095640441ULL, 6013842857594557073ULL, 12433471431044669357ULL, 14262771896054399489ULL, 6446658014718628302ULL, 7229558444577096448ULL, 13872199742687901116ULL, 952395606119921280ULL, 1472547022800074667ULL, 8500847235576475910ULL, 308623728639457857ULL, 7831106959981110673ULL, 14221846736516958824ULL, 5796240453490623563ULL, 12981576648874809765ULL, 16434266822974705037ULL, 14745018226935587220ULL, 4034090235427373645ULL, 14749224580281562362ULL, 14619634180236348185ULL, 9818463256038657547ULL, 9782515843026543392ULL, 8396791438810111190ULL, 16104845001429102064ULL, 6597335780905594619ULL, 8087604609482250757ULL, 11337626243478354270ULL, 2616078833293994928ULL, 6263120375526399459ULL, 7324624097771048729ULL, 15443556104029229341ULL, 865441867960435303ULL, 3385276471020662123ULL, 16606810265410520019ULL, 7733758044026987105ULL, 15901646217779410993ULL, 1931107594768615801ULL, 13595885339956443727ULL, 1734226471811708653ULL, 11787006789854968365ULL, 13473925697040207793ULL, 6795191086277285332ULL, 5466423536230328879ULL, 4942304358314660017ULL, 3376417763157001025ULL, 9047370796157560970ULL, 4265309484812175522ULL, 15869425263964541288ULL, 10612908973244385058ULL, 1765709248820518498ULL, 4206621156796187626ULL, 14898559392982525110ULL, 11537058932194148555ULL, 1299512315128822008ULL, 14371546888998537039ULL, 3857049708503498040ULL, 12048162241163569199ULL, 2340712079674384862ULL, 2179415699324172893ULL, 18023918117044002902ULL, 4855486034807059661ULL, 6087094860859447912ULL, 17143718291476367922ULL, 1256458344033345896ULL, 17579980229608795528ULL, 11557093398096277741ULL, 1007719678801444034ULL, 6779988640333755950ULL, 3907362273528096998ULL, 14774772624552968330ULL, 7544548194281689303ULL, 6636615097688206307ULL, 15003748247635326837ULL, 10104040818681312159ULL, 15321864528297940978ULL, 17567938070037591442ULL, 3856413681424027856ULL, 4292783871711268528ULL, 16140138884719999023ULL, 8478866772430350647ULL, 13281328343108678779ULL, 171305776311540294ULL, 11943708025943116824ULL, 6748039362155657697ULL, 2183205461918838428ULL, 9561785359617558686ULL, 3652031999230675404ULL, 16321005275195823506ULL, 7959930929889385718ULL, 17999521541459422754ULL, 14635154601438918724ULL, 553396447970528490ULL, 15862024157345514069ULL, 17371777207680026427ULL, 3359137361049609599ULL, 6943258586876168947ULL, 3784445195417718744ULL, 2274581329675865992ULL, 6275031359059690984ULL, 11241808407807133385ULL, 3877224075612620234ULL, 16870756477494728679ULL, 734849408908923633ULL, 14240209950286982891ULL, 1986420651953859073ULL, 11387147591969404274ULL, 18077713890942016162ULL, 15139168790964589319ULL, 6477404551395445767ULL, 10436081832179488348ULL, 17744304487681668043ULL, 2128714340480324937ULL, 15859910632245313294ULL, 5995483074590576473ULL, 6509071106206968172ULL, 2163169824462988025ULL, 3863527045317748751ULL, 8237633082069433530ULL, 9841515757799528500ULL, 14294777371676485915ULL, 11934704548363029679ULL, 7120673287036134126ULL, 12863291578303148023ULL, 16334238291080691817ULL, 15014506184267627951ULL, 6278562041408864249ULL, 15773927532538725983ULL, 11862278774206137517ULL, 10108495441138000208ULL, 3643644710480806625ULL, 13183190725774765491ULL, 3330440592821674651ULL, 11067801741714748965ULL, 6289527073896131021ULL, 8391475369929113254ULL, 12419071995301955387ULL, 13941914633954149972ULL, 17929047276773532931ULL, 14081854430417507654ULL, 4122773549016528789ULL, 5866939267497024327ULL, 5633445301560111924ULL, 15148806336003336018ULL, 8513300211206524724ULL, 1081483082384699582ULL, 2252431285306913990ULL, 3315840836598307395ULL, 8611672253026635924ULL, 7692447144052698970ULL, 17826945265428611077ULL, 4407929597728693553ULL, 4129202124902798391ULL, 16210351311974513110ULL, 7013462431341857666ULL, 10679378862009417773ULL, 10698731448227451997ULL, 10214937890834175264ULL, 4867308398708952696ULL, 16617172368428362162ULL, 17467275560723318293ULL, 470191739110710031ULL, 3685562506811825781ULL, 2905805415767189564ULL, 8084502220906609248ULL, 11780115290117768470ULL, 3336485534708884ULL, 474902672695954096ULL, 8069687131739545408ULL, 12628114540585355261ULL, 8107372665444409348ULL, 18260745212654035178ULL, 13512723775043200645ULL, 14293297435359794275ULL, 4561193852693787246ULL, 3277932672758347822ULL, 6239112440980812612ULL, 12069158410244739986ULL, 12764136687241441664ULL, 9103612728729402301ULL, 46300154371201744ULL, 12608266932428043680ULL, 12706319272633005305ULL, 8402553077618110420ULL, 14275372808014136081ULL, 9074687190955529423ULL, 3442732534623606404ULL, 8699185602383950480ULL, 14782484856197519501ULL, 1522378370519043561ULL, 2734276335299301842ULL, 300033226049380445ULL, 2807029055204421976ULL, 8558096721253991069ULL, 14921253645916145844ULL, 6449350720490621488ULL, 7930533333175007233ULL, 14184697264816514386ULL, 2326955563868169757ULL, 2035352265515334775ULL, 8153640834898357023ULL, 14984855137181870290ULL, 13099153861198352490ULL, 13773789017351586912ULL, 15200492287234951007ULL, 14483739337732840655ULL, 6403559166266407540ULL, 13753602020720880910ULL, 3249279661898332800ULL, 9181123651142087069ULL, 1836691926200600341ULL, 9764040110125927332ULL, 6663212195462700590ULL, 7109449835495623894ULL, 4636864470301046378ULL, 13265891422234620013ULL, 15463288374108596917ULL, 11038262548913554028ULL, 18158132467259812090ULL, 4815114688002177032ULL, 2416784426869281784ULL, 6732180818225346150ULL, 17393071132099316992ULL, 5525092284629074569ULL, 1634870171524771550ULL, 8292439854432961292ULL, 6061222638298614019ULL, 15285721643957612773ULL, 18283248164276310734ULL, 15476901838986528369ULL, 6119315299220687605ULL, 11702689397173333134ULL, 10012597160382659150ULL, 13320301609044099934ULL, 4681970170429611174ULL, 18241817896048179829ULL, 16849453277189735160ULL, 10049127378905201116ULL, 10708164598968648570ULL, 15755017209579804185ULL, 3752335377410341683ULL, 1238205409631919049ULL, 1152656054405278355ULL, 16558359693635275419ULL, 7430194032384888672ULL, 543271656567182335ULL, 5194796692717647744ULL, 5665258811938242506ULL, 2300807873645502127ULL, 165812044371428982ULL, 3939294062048718949ULL, 74844995338929047ULL, 10052744505958225167ULL, 9892303744120031155ULL, 15893654591597836243ULL, 10459156830982532517ULL, 10008127731394723705ULL, 14225162161544672503ULL, 610469786679517176ULL, 6085777220071229420ULL, 3968732725768765983ULL, 16905520723506218102ULL, 9015244267864269380ULL, 836844216733147253ULL, 15522943025565498336ULL, 1227680606736318287ULL, 10551664483035308391ULL, 8893304960486742702ULL, 228633503764621721ULL, 8618128597575781798ULL, 9389074572030515665ULL, 10467270419232710888ULL, 2789669318845938730ULL, 4540693032004158783ULL, 15365997957373073222ULL, 14452220300689696232ULL, 9611295392598286195ULL, 9386731512417660260ULL, 13011975161709967294ULL, 13887417423927953272ULL, 15141666041390241207ULL, 13801670979443617350ULL, 11912010898649251793ULL, 3216755092744419688ULL, 7719909053524096732ULL, 6570210936025285383ULL, 15519528288614946296ULL, 5896026138526019560ULL, 18241091861601073858ULL, 12882790989651701026ULL, 2355463796642582687ULL, 5034442258850573194ULL, 4119189770149100604ULL, 1169198788330860086ULL, 8990660269277164964ULL, 11855231768356149717ULL, 4312914019301092679ULL, 18133972076692991117ULL, 10339301334373249631ULL, 6224236857920519326ULL, 2145883984422340647ULL, 10499737541019029130ULL, 9605014900239155921ULL, 8774629887936906915ULL, 13658562455465335922ULL, 2455471276117274492ULL, 1599794828360808557ULL, 6409710086524383258ULL, 13572848057069272691ULL, 15859792500999344780ULL, 14833852200807153620ULL, 18373300168636597590ULL, 1647276602047959173ULL, 6966105289391190876ULL, 16548350976601842266ULL, 10244487721105664919ULL, 7368498157055229230ULL, 1050505464345977270ULL, 14642008335963743180ULL, 15946168150732473042ULL, 9559473511359696133ULL, 16892266906861528962ULL, 4381326281718415033ULL, 18287679639551536447ULL, 7073033387009385311ULL, 5995412771461993422ULL, 17142672245759427204ULL, }; const std::pair GameFiles[] = { { "BOOST.TXT", 1 }, { "LUA5.TXT", 1 }, { "ZLIB123.TXT", 1 }, { "default.xex", 3 }, { "win32/archives/enemy_data.arc", 1 }, { "win32/archives/event_data.arc", 1 }, { "win32/archives/particle_data.arc", 1 }, { "win32/archives/player_amy.arc", 1 }, { "win32/archives/player_blaze.arc", 1 }, { "win32/archives/player_common.arc", 1 }, { "win32/archives/player_knuckles.arc", 1 }, { "win32/archives/player_omega.arc", 1 }, { "win32/archives/player_princess.arc", 1 }, { "win32/archives/player_rouge.arc", 1 }, { "win32/archives/player_shadow.arc", 1 }, { "win32/archives/player_silver.arc", 1 }, { "win32/archives/player_sonic.arc", 1 }, { "win32/archives/player_supershadow.arc", 1 }, { "win32/archives/player_supersilver.arc", 1 }, { "win32/archives/player_supersonic.arc", 1 }, { "win32/archives/player_tails.arc", 1 }, { "win32/archives/radarmap.arc", 1 }, { "win32/archives/sprite.arc", 1 }, { "win32/archives/stage_aqa_a.arc", 1 }, { "win32/archives/stage_aqa_b.arc", 1 }, { "win32/archives/stage_boss_dr1_dtd.arc", 1 }, { "win32/archives/stage_boss_dr1_wap.arc", 1 }, { "win32/archives/stage_boss_dr2.arc", 1 }, { "win32/archives/stage_boss_dr3.arc", 1 }, { "win32/archives/stage_boss_iblis02.arc", 1 }, { "win32/archives/stage_boss_iblis03.arc", 1 }, { "win32/archives/stage_boss_mefi01.arc", 1 }, { "win32/archives/stage_boss_mefi02.arc", 1 }, { "win32/archives/stage_boss_rct.arc", 1 }, { "win32/archives/stage_boss_solaris.arc", 1 }, { "win32/archives/stage_csc_a.arc", 1 }, { "win32/archives/stage_csc_b.arc", 1 }, { "win32/archives/stage_csc_c.arc", 1 }, { "win32/archives/stage_csc_e.arc", 1 }, { "win32/archives/stage_csc_f.arc", 1 }, { "win32/archives/stage_csc_iblis01.arc", 1 }, { "win32/archives/stage_dtd_a.arc", 1 }, { "win32/archives/stage_dtd_b.arc", 1 }, { "win32/archives/stage_e0003.arc", 1 }, { "win32/archives/stage_e0009.arc", 1 }, { "win32/archives/stage_e0010.arc", 1 }, { "win32/archives/stage_e0012.arc", 1 }, { "win32/archives/stage_e0021.arc", 1 }, { "win32/archives/stage_e0022.arc", 1 }, { "win32/archives/stage_e0023.arc", 1 }, { "win32/archives/stage_e0026.arc", 1 }, { "win32/archives/stage_e0028.arc", 1 }, { "win32/archives/stage_e0031.arc", 1 }, { "win32/archives/stage_e0104.arc", 1 }, { "win32/archives/stage_e0105.arc", 1 }, { "win32/archives/stage_e0106.arc", 1 }, { "win32/archives/stage_e0120.arc", 1 }, { "win32/archives/stage_e0125.arc", 1 }, { "win32/archives/stage_e0206.arc", 1 }, { "win32/archives/stage_e0214.arc", 1 }, { "win32/archives/stage_e0216.arc", 1 }, { "win32/archives/stage_e0221.arc", 1 }, { "win32/archives/stage_e0304.arc", 1 }, { "win32/archives/stage_flc_a.arc", 1 }, { "win32/archives/stage_flc_b.arc", 1 }, { "win32/archives/stage_kdv_a.arc", 1 }, { "win32/archives/stage_kdv_b.arc", 1 }, { "win32/archives/stage_kdv_c.arc", 1 }, { "win32/archives/stage_kdv_d.arc", 1 }, { "win32/archives/stage_rct_a.arc", 1 }, { "win32/archives/stage_rct_b.arc", 1 }, { "win32/archives/stage_tpj_a.arc", 1 }, { "win32/archives/stage_tpj_b.arc", 1 }, { "win32/archives/stage_tpj_c.arc", 1 }, { "win32/archives/stage_twn_a.arc", 1 }, { "win32/archives/stage_twn_b.arc", 1 }, { "win32/archives/stage_twn_c.arc", 1 }, { "win32/archives/stage_twn_d.arc", 1 }, { "win32/archives/stage_wap_a.arc", 1 }, { "win32/archives/stage_wap_b.arc", 1 }, { "win32/archives/stage_wvo_a.arc", 1 }, { "win32/archives/stage_wvo_b.arc", 1 }, { "xenon/archives/cache.arc", 1 }, { "xenon/archives/enemy.arc", 1 }, { "xenon/archives/event.arc", 1 }, { "xenon/archives/game.arc", 1 }, { "xenon/archives/human.arc", 1 }, { "xenon/archives/object.arc", 1 }, { "xenon/archives/particle.arc", 1 }, { "xenon/archives/player.arc", 1 }, { "xenon/archives/scripts.arc", 1 }, { "xenon/archives/shader.arc", 1 }, { "xenon/archives/shader_lt.arc", 1 }, { "xenon/archives/sound.arc", 1 }, { "xenon/archives/stage.arc", 1 }, { "xenon/archives/system.arc", 1 }, { "xenon/archives/text.arc", 1 }, { "xenon/event/e0000/e0000.wmv", 1 }, { "xenon/event/e0001/E0001_00_TL.xma", 1 }, { "xenon/event/e0001/E0001_01_SN.xma", 1 }, { "xenon/event/e0001/E0001_02_TL.xma", 1 }, { "xenon/event/e0001/E0001_03_TL.xma", 1 }, { "xenon/event/e0001/E0001_06_TL.xma", 1 }, { "xenon/event/e0001/E0001_07_TL.xma", 1 }, { "xenon/event/e0001/E0001_08_SN.xma", 1 }, { "xenon/event/e0001/E0001_09_TL.xma", 1 }, { "xenon/event/e0001/J0001_00_TL.xma", 1 }, { "xenon/event/e0001/J0001_01_SN.xma", 1 }, { "xenon/event/e0001/J0001_02_TL.xma", 1 }, { "xenon/event/e0001/J0001_03_TL.xma", 1 }, { "xenon/event/e0001/J0001_04_TL.xma", 1 }, { "xenon/event/e0001/J0001_06_TL.xma", 1 }, { "xenon/event/e0001/J0001_07_TL.xma", 1 }, { "xenon/event/e0001/J0001_08_SN.xma", 1 }, { "xenon/event/e0001/J0001_09_TL.xma", 1 }, { "xenon/event/e0002/E0002_00_TL.xma", 1 }, { "xenon/event/e0002/E0002_01_TL.xma", 1 }, { "xenon/event/e0002/J0002_00_TL.xma", 1 }, { "xenon/event/e0002/J0002_01_TL.xma", 1 }, { "xenon/event/e0003/E0003_00_SR.xma", 1 }, { "xenon/event/e0003/E0003_02_EL.xma", 1 }, { "xenon/event/e0003/E0003_03_SN.xma", 1 }, { "xenon/event/e0003/E0003_04_EL.xma", 1 }, { "xenon/event/e0003/E0003_05_SN.xma", 1 }, { "xenon/event/e0003/E0003_06_TL.xma", 1 }, { "xenon/event/e0003/E0003_07_EG.xma", 1 }, { "xenon/event/e0003/E0003_09_EG.xma", 1 }, { "xenon/event/e0003/E0003_10_SN.xma", 1 }, { "xenon/event/e0003/E0003_11_TL.xma", 1 }, { "xenon/event/e0003/J0003_00_SR.xma", 1 }, { "xenon/event/e0003/J0003_02_EL.xma", 1 }, { "xenon/event/e0003/J0003_03_SN.xma", 1 }, { "xenon/event/e0003/J0003_04_EL.xma", 1 }, { "xenon/event/e0003/J0003_05_SN.xma", 1 }, { "xenon/event/e0003/J0003_06_TL.xma", 1 }, { "xenon/event/e0003/J0003_07_EG.xma", 1 }, { "xenon/event/e0003/J0003_09_EG.xma", 1 }, { "xenon/event/e0003/J0003_10_SN.xma", 1 }, { "xenon/event/e0003/J0003_11_TL.xma", 1 }, { "xenon/event/e0004/E0004_00_SN.xma", 1 }, { "xenon/event/e0004/E0004_01_TL.xma", 1 }, { "xenon/event/e0004/E0004_02_TL.xma", 1 }, { "xenon/event/e0004/E0004_03_SN.xma", 1 }, { "xenon/event/e0004/E0004_04_TL.xma", 1 }, { "xenon/event/e0004/E0004_05_SN.xma", 1 }, { "xenon/event/e0004/E0004_06_EL.xma", 1 }, { "xenon/event/e0004/j0004_00_sn.xma", 1 }, { "xenon/event/e0004/j0004_01_tl.xma", 1 }, { "xenon/event/e0004/j0004_02_tl.xma", 1 }, { "xenon/event/e0004/j0004_03_sn.xma", 1 }, { "xenon/event/e0004/j0004_04_tl.xma", 1 }, { "xenon/event/e0004/j0004_05_sn.xma", 1 }, { "xenon/event/e0004/j0004_06_el.xma", 1 }, { "xenon/event/e0005/e0005.wmv", 1 }, { "xenon/event/e0006/E0006_00_EL.xma", 1 }, { "xenon/event/e0006/E0006_01_SV.xma", 1 }, { "xenon/event/e0006/E0006_02_SV.xma", 1 }, { "xenon/event/e0006/E0006_03_SN.xma", 1 }, { "xenon/event/e0006/E0006_04_SV.xma", 1 }, { "xenon/event/e0006/E0006_05_SV.xma", 1 }, { "xenon/event/e0006/J0006_00_EL.xma", 1 }, { "xenon/event/e0006/J0006_01_SV.xma", 1 }, { "xenon/event/e0006/J0006_02_SV.xma", 1 }, { "xenon/event/e0006/J0006_03_SN.xma", 1 }, { "xenon/event/e0006/J0006_04_SV.xma", 1 }, { "xenon/event/e0006/J0006_05_SV.xma", 1 }, { "xenon/event/e0007/E0007_00_SN.xma", 1 }, { "xenon/event/e0007/E0007_01A_SV.xma", 1 }, { "xenon/event/e0007/E0007_01B_SV.xma", 1 }, { "xenon/event/e0007/E0007_02_SN.xma", 1 }, { "xenon/event/e0007/E0007_03_SN.xma", 1 }, { "xenon/event/e0007/E0007_04A_SV.xma", 1 }, { "xenon/event/e0007/E0007_04B_SV.xma", 1 }, { "xenon/event/e0007/E0007_04_SV.xma", 1 }, { "xenon/event/e0007/E0007_06_SN.xma", 1 }, { "xenon/event/e0007/E0007_07_SV.xma", 1 }, { "xenon/event/e0007/E0007_09_EL.xma", 1 }, { "xenon/event/e0007/E0007_10_SN.xma", 1 }, { "xenon/event/e0007/E0007_11A_SV.xma", 1 }, { "xenon/event/e0007/E0007_11C_SV.xma", 1 }, { "xenon/event/e0007/E0007_12_SN.xma", 1 }, { "xenon/event/e0007/E0007_13_SV.xma", 1 }, { "xenon/event/e0007/E0007_14_AM.xma", 1 }, { "xenon/event/e0007/E0007_15_SV.xma", 1 }, { "xenon/event/e0007/E0007_16_SV.xma", 1 }, { "xenon/event/e0007/E0007_18_AM.xma", 1 }, { "xenon/event/e0007/E0007_19_SV.xma", 1 }, { "xenon/event/e0007/E0007_20_SN.xma", 1 }, { "xenon/event/e0007/E0007_21_AM.xma", 1 }, { "xenon/event/e0007/E0026_06_SN.xma", 1 }, { "xenon/event/e0007/J0007_00_SN.xma", 1 }, { "xenon/event/e0007/J0007_01_SV.xma", 1 }, { "xenon/event/e0007/J0007_02_SN.xma", 1 }, { "xenon/event/e0007/J0007_03_SN.xma", 1 }, { "xenon/event/e0007/J0007_04_SV.xma", 1 }, { "xenon/event/e0007/J0007_06_SN.xma", 1 }, { "xenon/event/e0007/J0007_07_SV.xma", 1 }, { "xenon/event/e0007/J0007_09_EL.xma", 1 }, { "xenon/event/e0007/J0007_10_SN.xma", 1 }, { "xenon/event/e0007/J0007_11_SV.xma", 1 }, { "xenon/event/e0007/J0007_12_SN.xma", 1 }, { "xenon/event/e0007/J0007_13_SV.xma", 1 }, { "xenon/event/e0007/J0007_14_AM.xma", 1 }, { "xenon/event/e0007/J0007_15_SV.xma", 1 }, { "xenon/event/e0007/J0007_16_SV.xma", 1 }, { "xenon/event/e0007/J0007_18_AM.xma", 1 }, { "xenon/event/e0007/J0007_19_SV.xma", 1 }, { "xenon/event/e0007/J0007_20_SN.xma", 1 }, { "xenon/event/e0007/J0007_21_AM.xma", 1 }, { "xenon/event/e0008/e0008.wmv", 1 }, { "xenon/event/e0009/E0009_00_EG.xma", 1 }, { "xenon/event/e0009/E0009_01_KN.xma", 1 }, { "xenon/event/e0009/E0009_02_SN.xma", 1 }, { "xenon/event/e0009/E0009_03_EG.xma", 1 }, { "xenon/event/e0009/E0009_04_EL.xma", 1 }, { "xenon/event/e0009/E0009_05_EG.xma", 1 }, { "xenon/event/e0009/E0009_06_EG.xma", 1 }, { "xenon/event/e0009/E0009_08_EG.xma", 1 }, { "xenon/event/e0009/E0009_09_EG.xma", 1 }, { "xenon/event/e0009/E0009_10A_EG.xma", 1 }, { "xenon/event/e0009/E0009_10B_EG.xma", 1 }, { "xenon/event/e0009/E0009_10C_EG.xma", 1 }, { "xenon/event/e0009/E0009_11A_EG.xma", 1 }, { "xenon/event/e0009/E0009_11B_EG.xma", 1 }, { "xenon/event/e0009/E0009_11_EG.xma", 1 }, { "xenon/event/e0009/E0009_12_TL.xma", 1 }, { "xenon/event/e0009/E0009_13_KN.xma", 1 }, { "xenon/event/e0009/E0009_14_KN.xma", 1 }, { "xenon/event/e0009/E0009_16_KN.xma", 1 }, { "xenon/event/e0009/E0009_17A_EG.xma", 1 }, { "xenon/event/e0009/E0009_17B_EG.xma", 1 }, { "xenon/event/e0009/E0009_18_EG.xma", 1 }, { "xenon/event/e0009/E0009_20_EL.xma", 1 }, { "xenon/event/e0009/E0009_21_EG.xma", 1 }, { "xenon/event/e0009/E0009_23_SN.xma", 1 }, { "xenon/event/e0009/E0009_24_KN.xma", 1 }, { "xenon/event/e0009/E0009_25_TL.xma", 1 }, { "xenon/event/e0009/E0009_26_EG.xma", 1 }, { "xenon/event/e0009/E0009_27_EG.xma", 1 }, { "xenon/event/e0009/E0009_28_EG.xma", 1 }, { "xenon/event/e0009/E0009_30_EG.xma", 1 }, { "xenon/event/e0009/E0009_31_EG.xma", 1 }, { "xenon/event/e0009/E0009_32_EL.xma", 1 }, { "xenon/event/e0009/E0009_33_EG.xma", 1 }, { "xenon/event/e0009/E0009_35_EG.xma", 1 }, { "xenon/event/e0009/E0009_37_EG.xma", 1 }, { "xenon/event/e0009/E0009_38_EG.xma", 1 }, { "xenon/event/e0009/J0009_00_EG.xma", 1 }, { "xenon/event/e0009/J0009_01_KN.xma", 1 }, { "xenon/event/e0009/J0009_02_SN.xma", 1 }, { "xenon/event/e0009/J0009_03_EG.xma", 1 }, { "xenon/event/e0009/J0009_04_EL.xma", 1 }, { "xenon/event/e0009/J0009_05_EG.xma", 1 }, { "xenon/event/e0009/J0009_06_EG.xma", 1 }, { "xenon/event/e0009/J0009_08_EG.xma", 1 }, { "xenon/event/e0009/J0009_10_EG.xma", 1 }, { "xenon/event/e0009/J0009_11_EG.xma", 1 }, { "xenon/event/e0009/J0009_12_TL.xma", 1 }, { "xenon/event/e0009/J0009_13_KN.xma", 1 }, { "xenon/event/e0009/J0009_14_KN.xma", 1 }, { "xenon/event/e0009/J0009_16_KN.xma", 1 }, { "xenon/event/e0009/J0009_17A_EG.xma", 1 }, { "xenon/event/e0009/J0009_17B_EG.xma", 1 }, { "xenon/event/e0009/J0009_18_EG.xma", 1 }, { "xenon/event/e0009/J0009_20_EL.xma", 1 }, { "xenon/event/e0009/J0009_21_EG.xma", 1 }, { "xenon/event/e0009/J0009_23_SN.xma", 1 }, { "xenon/event/e0009/J0009_24_KN.xma", 1 }, { "xenon/event/e0009/J0009_25_TL.xma", 1 }, { "xenon/event/e0009/J0009_26_EG.xma", 1 }, { "xenon/event/e0009/J0009_27_EG.xma", 1 }, { "xenon/event/e0009/J0009_28_EG.xma", 1 }, { "xenon/event/e0009/J0009_30_EG.xma", 1 }, { "xenon/event/e0009/J0009_31_EG.xma", 1 }, { "xenon/event/e0009/J0009_32_EL.xma", 1 }, { "xenon/event/e0009/J0009_33_EG.xma", 1 }, { "xenon/event/e0009/J0009_35_EG.xma", 1 }, { "xenon/event/e0009/J0009_37_EG.xma", 1 }, { "xenon/event/e0009/J0009_38_EG.xma", 1 }, { "xenon/event/e0010/E0010_00_SN.xma", 1 }, { "xenon/event/e0010/E0010_03_KN.xma", 1 }, { "xenon/event/e0010/E0010_04_TL.xma", 1 }, { "xenon/event/e0010/E0010_05_TL.xma", 1 }, { "xenon/event/e0010/E0010_07_RG.xma", 1 }, { "xenon/event/e0010/E0010_08_TL.xma", 1 }, { "xenon/event/e0010/E0010_09_RG.xma", 1 }, { "xenon/event/e0010/J0010_00_SN.xma", 1 }, { "xenon/event/e0010/J0010_01_TL.xma", 1 }, { "xenon/event/e0010/J0010_02_KN.xma", 1 }, { "xenon/event/e0010/J0010_03_KN.xma", 1 }, { "xenon/event/e0010/J0010_04_TL.xma", 1 }, { "xenon/event/e0010/J0010_05_TL.xma", 1 }, { "xenon/event/e0010/J0010_07_RG.xma", 1 }, { "xenon/event/e0010/J0010_08_TL.xma", 1 }, { "xenon/event/e0010/J0010_09_RG.xma", 1 }, { "xenon/event/e0011/E0011_00_SH.xma", 1 }, { "xenon/event/e0011/E0011_01_TL.xma", 1 }, { "xenon/event/e0011/E0011_02_TL.xma", 1 }, { "xenon/event/e0011/E0011_03_SN.xma", 1 }, { "xenon/event/e0011/E0011_04_KN.xma", 1 }, { "xenon/event/e0011/E0011_05_SH.xma", 1 }, { "xenon/event/e0011/E0011_06_SN.xma", 1 }, { "xenon/event/e0011/E0011_07_SH.xma", 1 }, { "xenon/event/e0011/E0011_08_SN.xma", 1 }, { "xenon/event/e0011/J0011_00_SH.xma", 1 }, { "xenon/event/e0011/J0011_01_TL.xma", 1 }, { "xenon/event/e0011/J0011_02_TL.xma", 1 }, { "xenon/event/e0011/J0011_03_SN.xma", 1 }, { "xenon/event/e0011/J0011_04_KN.xma", 1 }, { "xenon/event/e0011/J0011_05_SH.xma", 1 }, { "xenon/event/e0011/J0011_06_SN.xma", 1 }, { "xenon/event/e0011/J0011_07_SH.xma", 1 }, { "xenon/event/e0011/J0011_08_SN.xma", 1 }, { "xenon/event/e0012/E0012_00_KN.xma", 1 }, { "xenon/event/e0012/E0012_01_KN.xma", 1 }, { "xenon/event/e0012/E0012_02_TL.xma", 1 }, { "xenon/event/e0012/E0012_04_SV.xma", 1 }, { "xenon/event/e0012/E0012_05_SV.xma", 1 }, { "xenon/event/e0012/E0012_06_MF.xma", 1 }, { "xenon/event/e0012/E0012_07_MF.xma", 1 }, { "xenon/event/e0012/E0012_08_MF.xma", 1 }, { "xenon/event/e0012/E0012_09_MF.xma", 1 }, { "xenon/event/e0012/E0012_11_SV.xma", 1 }, { "xenon/event/e0012/E0012_12_SV.xma", 1 }, { "xenon/event/e0012/E0012_13_BZ.xma", 1 }, { "xenon/event/e0012/E0012_14_MF.xma", 1 }, { "xenon/event/e0012/E0012_15_KN.xma", 1 }, { "xenon/event/e0012/E0012_18_TL.xma", 1 }, { "xenon/event/e0012/E0012_19_TL.xma", 1 }, { "xenon/event/e0012/E0012_21_TL.xma", 1 }, { "xenon/event/e0012/E0012_22_TL.xma", 1 }, { "xenon/event/e0012/E0012_23_SN.xma", 1 }, { "xenon/event/e0012/E0012_25_TL.xma", 1 }, { "xenon/event/e0012/J0012_00_KN.xma", 1 }, { "xenon/event/e0012/J0012_01_KN.xma", 1 }, { "xenon/event/e0012/J0012_02_TL.xma", 1 }, { "xenon/event/e0012/J0012_04_SV.xma", 1 }, { "xenon/event/e0012/J0012_05_SV.xma", 1 }, { "xenon/event/e0012/J0012_06_MF.xma", 1 }, { "xenon/event/e0012/J0012_07_MF.xma", 1 }, { "xenon/event/e0012/J0012_08_MF.xma", 1 }, { "xenon/event/e0012/J0012_09_MF.xma", 1 }, { "xenon/event/e0012/J0012_11_SV.xma", 1 }, { "xenon/event/e0012/J0012_12_SV.xma", 1 }, { "xenon/event/e0012/J0012_13_BZ.xma", 1 }, { "xenon/event/e0012/J0012_14_MF.xma", 1 }, { "xenon/event/e0012/J0012_15_KN.xma", 1 }, { "xenon/event/e0012/J0012_18_TL.xma", 1 }, { "xenon/event/e0012/J0012_19_TL.xma", 1 }, { "xenon/event/e0012/J0012_21_TL.xma", 1 }, { "xenon/event/e0012/J0012_22_TL.xma", 1 }, { "xenon/event/e0012/J0012_23_SN.xma", 1 }, { "xenon/event/e0012/J0012_25_TL.xma", 1 }, { "xenon/event/e0013/E0013_00_RG.xma", 1 }, { "xenon/event/e0013/E0013_01_RG.xma", 1 }, { "xenon/event/e0013/E0013_02A_TL.xma", 1 }, { "xenon/event/e0013/E0013_02B_TL.xma", 1 }, { "xenon/event/e0013/E0013_03_RG.xma", 1 }, { "xenon/event/e0013/E0013_05_RG.xma", 1 }, { "xenon/event/e0013/E0013_06B_KN.xma", 1 }, { "xenon/event/e0013/E0013_06_KN.xma", 1 }, { "xenon/event/e0013/E0013_07_SH.xma", 1 }, { "xenon/event/e0013/E0013_08_TL.xma", 1 }, { "xenon/event/e0013/E0013_10_RG.xma", 1 }, { "xenon/event/e0013/E0013_11_KN.xma", 1 }, { "xenon/event/e0013/E0013_12_RG.xma", 1 }, { "xenon/event/e0013/E0013_13_SN.xma", 1 }, { "xenon/event/e0013/E0013_14_SH.xma", 1 }, { "xenon/event/e0013/J0013_00_RG.xma", 1 }, { "xenon/event/e0013/J0013_01_RG.xma", 1 }, { "xenon/event/e0013/J0013_02A_TL.xma", 1 }, { "xenon/event/e0013/J0013_02B_TL.xma", 1 }, { "xenon/event/e0013/J0013_03_RG.xma", 1 }, { "xenon/event/e0013/J0013_05_RG.xma", 1 }, { "xenon/event/e0013/J0013_06_KN.xma", 1 }, { "xenon/event/e0013/J0013_07_SH.xma", 1 }, { "xenon/event/e0013/J0013_08_TL.xma", 1 }, { "xenon/event/e0013/J0013_10_RG.xma", 1 }, { "xenon/event/e0013/J0013_11_KN.xma", 1 }, { "xenon/event/e0013/J0013_12_RG.xma", 1 }, { "xenon/event/e0013/J0013_13_SN.xma", 1 }, { "xenon/event/e0013/J0013_14_SH.xma", 1 }, { "xenon/event/e0014/E0014_00_RG.xma", 1 }, { "xenon/event/e0014/E0014_01_SH.xma", 1 }, { "xenon/event/e0014/E0014_02_RG.xma", 1 }, { "xenon/event/e0014/E0014_03_RG.xma", 1 }, { "xenon/event/e0014/J0014_00_RG.xma", 1 }, { "xenon/event/e0014/J0014_01_SH.xma", 1 }, { "xenon/event/e0014/J0014_02_RG.xma", 1 }, { "xenon/event/e0014/J0014_03_RG.xma", 1 }, { "xenon/event/e0015/E0015_00_SN.xma", 1 }, { "xenon/event/e0015/J0015_00_SN.xma", 1 }, { "xenon/event/e0015/J0015_01_SH.xma", 1 }, { "xenon/event/e0016/E0016_00_TL.xma", 1 }, { "xenon/event/e0016/E0016_01_KN.xma", 1 }, { "xenon/event/e0016/E0016_02_TL.xma", 1 }, { "xenon/event/e0016/E0016_03_SN.xma", 1 }, { "xenon/event/e0016/J0016_00_TL.xma", 1 }, { "xenon/event/e0016/J0016_01_KN.xma", 1 }, { "xenon/event/e0016/J0016_02_TL.xma", 1 }, { "xenon/event/e0016/J0016_03_SN.xma", 1 }, { "xenon/event/e0017/E0017_00_EL.xma", 1 }, { "xenon/event/e0017/E0017_02_SN.xma", 1 }, { "xenon/event/e0017/E0017_03_SN.xma", 1 }, { "xenon/event/e0017/E0017_04_SV.xma", 1 }, { "xenon/event/e0017/E0017_05_SV.xma", 1 }, { "xenon/event/e0017/E0017_06_EL.xma", 1 }, { "xenon/event/e0017/E0017_07_SV.xma", 1 }, { "xenon/event/e0017/E0017_08_SN.xma", 1 }, { "xenon/event/e0017/E0017_10A_EG.xma", 1 }, { "xenon/event/e0017/E0017_10B_EG.xma", 1 }, { "xenon/event/e0017/E0017_11_EL.xma", 1 }, { "xenon/event/e0017/E0017_12_SN.xma", 1 }, { "xenon/event/e0017/E0017_14_SN.xma", 1 }, { "xenon/event/e0017/E0017_16_SV.xma", 1 }, { "xenon/event/e0017/E0017_17_SV.xma", 1 }, { "xenon/event/e0017/E0017_18_SV.xma", 1 }, { "xenon/event/e0017/E0017_19_SH.xma", 1 }, { "xenon/event/e0017/J0017_00_EL.xma", 1 }, { "xenon/event/e0017/J0017_02_SN.xma", 1 }, { "xenon/event/e0017/J0017_03_SN.xma", 1 }, { "xenon/event/e0017/J0017_04_SV.xma", 1 }, { "xenon/event/e0017/J0017_05_SV.xma", 1 }, { "xenon/event/e0017/J0017_06_EL.xma", 1 }, { "xenon/event/e0017/J0017_09_SN.xma", 1 }, { "xenon/event/e0017/J0017_10A_EG.xma", 1 }, { "xenon/event/e0017/J0017_10B_EG.xma", 1 }, { "xenon/event/e0017/J0017_11_EL.xma", 1 }, { "xenon/event/e0017/J0017_12_SN.xma", 1 }, { "xenon/event/e0017/J0017_13_SV.xma", 1 }, { "xenon/event/e0017/J0017_14_SN.xma", 1 }, { "xenon/event/e0017/J0017_16_SV.xma", 1 }, { "xenon/event/e0017/J0017_17_SV.xma", 1 }, { "xenon/event/e0017/J0017_18_SV.xma", 1 }, { "xenon/event/e0017/J0017_19_SH.xma", 1 }, { "xenon/event/e0018/E0018_00_EG.xma", 1 }, { "xenon/event/e0018/E0018_01_EL.xma", 1 }, { "xenon/event/e0018/E0018_02_EG.xma", 1 }, { "xenon/event/e0018/E0018_04_EL.xma", 1 }, { "xenon/event/e0018/E0018_05_EG.xma", 1 }, { "xenon/event/e0018/E0018_06_EL.xma", 1 }, { "xenon/event/e0018/E0018_07_EG.xma", 1 }, { "xenon/event/e0018/J0018_00_EG.xma", 1 }, { "xenon/event/e0018/J0018_01_EL.xma", 1 }, { "xenon/event/e0018/J0018_02_EG.xma", 1 }, { "xenon/event/e0018/J0018_04_EL.xma", 1 }, { "xenon/event/e0018/J0018_05_EG.xma", 1 }, { "xenon/event/e0018/J0018_06_EL.xma", 1 }, { "xenon/event/e0018/J0018_07_EG.xma", 1 }, { "xenon/event/e0019/E0019_00_SN.xma", 1 }, { "xenon/event/e0019/E0019_01_EL.xma", 1 }, { "xenon/event/e0019/E0019_03_EL.xma", 1 }, { "xenon/event/e0019/E0019_04_SN.xma", 1 }, { "xenon/event/e0019/E0019_05_EL.xma", 1 }, { "xenon/event/e0019/E0019_06_SN.xma", 1 }, { "xenon/event/e0019/E0019_07_EL.xma", 1 }, { "xenon/event/e0019/J0019_00_SN.xma", 1 }, { "xenon/event/e0019/J0019_01_EL.xma", 1 }, { "xenon/event/e0019/J0019_03_EL.xma", 1 }, { "xenon/event/e0019/J0019_04_SN.xma", 1 }, { "xenon/event/e0019/J0019_05_EL.xma", 1 }, { "xenon/event/e0019/J0019_06_SN.xma", 1 }, { "xenon/event/e0019/J0019_07_EL.xma", 1 }, { "xenon/event/e0020/e0020.wmv", 1 }, { "xenon/event/e0021/E0021_00_EG.xma", 1 }, { "xenon/event/e0021/E0021_01_EG.xma", 1 }, { "xenon/event/e0021/E0021_02_EG.xma", 1 }, { "xenon/event/e0021/E0021_03_EG.xma", 1 }, { "xenon/event/e0021/E0021_05_MD.xma", 1 }, { "xenon/event/e0021/E0021_07_EL.xma", 1 }, { "xenon/event/e0021/E0021_08_MA.xma", 1 }, { "xenon/event/e0021/E0021_09_EL.xma", 1 }, { "xenon/event/e0021/E0021_10_EL.xma", 1 }, { "xenon/event/e0021/E0021_11_NPC.xma", 1 }, { "xenon/event/e0021/E0021_12_NPC.xma", 1 }, { "xenon/event/e0021/J0021_00_EG.xma", 1 }, { "xenon/event/e0021/J0021_01_EG.xma", 1 }, { "xenon/event/e0021/J0021_02_EG.xma", 1 }, { "xenon/event/e0021/J0021_03_EG.xma", 1 }, { "xenon/event/e0021/J0021_05_MD.xma", 1 }, { "xenon/event/e0021/J0021_07_EL.xma", 1 }, { "xenon/event/e0021/J0021_08_MA.xma", 1 }, { "xenon/event/e0021/J0021_09_EL.xma", 1 }, { "xenon/event/e0021/J0021_10_EL.xma", 1 }, { "xenon/event/e0021/J0021_11_NPC.xma", 1 }, { "xenon/event/e0021/J0021_12_NPC.xma", 1 }, { "xenon/event/e0022/E0022_00_EG.xma", 1 }, { "xenon/event/e0022/E0022_01_EG.xma", 1 }, { "xenon/event/e0022/E0022_02_EL.xma", 1 }, { "xenon/event/e0022/E0022_03_EL.xma", 1 }, { "xenon/event/e0022/E0022_04_EG.xma", 1 }, { "xenon/event/e0022/E0022_05_EG.xma", 1 }, { "xenon/event/e0022/E0022_07_EG.xma", 1 }, { "xenon/event/e0022/E0022_08_EG.xma", 1 }, { "xenon/event/e0022/E0022_09_EG.xma", 1 }, { "xenon/event/e0022/E0022_10_EL.xma", 1 }, { "xenon/event/e0022/E0022_11A_EG.xma", 1 }, { "xenon/event/e0022/E0022_11B_EG.xma", 1 }, { "xenon/event/e0022/E0022_12_EG.xma", 1 }, { "xenon/event/e0022/E0022_14_EG.xma", 1 }, { "xenon/event/e0022/E0022_15_EG.xma", 1 }, { "xenon/event/e0022/E0022_16_EG.xma", 1 }, { "xenon/event/e0022/E0022_17_COM.xma", 1 }, { "xenon/event/e0022/E0022_18_EG.xma", 1 }, { "xenon/event/e0022/J0022_00_EG.xma", 1 }, { "xenon/event/e0022/J0022_01_EG.xma", 1 }, { "xenon/event/e0022/J0022_02_EL.xma", 1 }, { "xenon/event/e0022/J0022_03_EL.xma", 1 }, { "xenon/event/e0022/J0022_04_EG.xma", 1 }, { "xenon/event/e0022/J0022_05_EG.xma", 1 }, { "xenon/event/e0022/J0022_07_EG.xma", 1 }, { "xenon/event/e0022/J0022_08_EG.xma", 1 }, { "xenon/event/e0022/J0022_09_EG.xma", 1 }, { "xenon/event/e0022/J0022_10_EL.xma", 1 }, { "xenon/event/e0022/J0022_11A_EG.xma", 1 }, { "xenon/event/e0022/J0022_11B_EG.xma", 1 }, { "xenon/event/e0022/J0022_12_EG.xma", 1 }, { "xenon/event/e0022/J0022_14_EG.xma", 1 }, { "xenon/event/e0022/J0022_15_EG.xma", 1 }, { "xenon/event/e0022/J0022_16_EG.xma", 1 }, { "xenon/event/e0022/J0022_17_COM.xma", 1 }, { "xenon/event/e0022/J0022_18_EG.xma", 1 }, { "xenon/event/e0023/E0023_01_SV.xma", 1 }, { "xenon/event/e0023/E0023_02_SV.xma", 1 }, { "xenon/event/e0023/E0023_03_SN.xma", 1 }, { "xenon/event/e0023/E0023_04_SV.xma", 1 }, { "xenon/event/e0023/E0023_05_SV.xma", 1 }, { "xenon/event/e0023/J0023_01_SV.xma", 1 }, { "xenon/event/e0023/J0023_02_SV.xma", 1 }, { "xenon/event/e0023/J0023_03_SN.xma", 1 }, { "xenon/event/e0023/J0023_04_SV.xma", 1 }, { "xenon/event/e0023/J0023_05_SV.xma", 1 }, { "xenon/event/e0024/E0024_00A_EG.xma", 1 }, { "xenon/event/e0024/E0024_00B_EG.xma", 1 }, { "xenon/event/e0024/E0024_00C_EG.xma", 1 }, { "xenon/event/e0024/E0024_01_EL.xma", 1 }, { "xenon/event/e0024/E0024_02_EG.xma", 1 }, { "xenon/event/e0024/E0024_03A_EG.xma", 1 }, { "xenon/event/e0024/E0024_03B_EG.xma", 1 }, { "xenon/event/e0024/E0024_03C_EG.xma", 1 }, { "xenon/event/e0024/E0024_04_EG.xma", 1 }, { "xenon/event/e0024/E0024_05_EG.xma", 1 }, { "xenon/event/e0024/J0024_00A_EG.xma", 1 }, { "xenon/event/e0024/J0024_00B_EG.xma", 1 }, { "xenon/event/e0024/J0024_00C_EG.xma", 1 }, { "xenon/event/e0024/J0024_02_EG.xma", 1 }, { "xenon/event/e0024/J0024_03A_EG.xma", 1 }, { "xenon/event/e0024/J0024_03B_EG.xma", 1 }, { "xenon/event/e0024/J0024_03C_EG.xma", 1 }, { "xenon/event/e0024/j0024_04_eg.xma", 1 }, { "xenon/event/e0026/E0026_00_SN.xma", 1 }, { "xenon/event/e0026/E0026_01_SV.xma", 1 }, { "xenon/event/e0026/E0026_02_SV.xma", 1 }, { "xenon/event/e0026/E0026_03_SN.xma", 1 }, { "xenon/event/e0026/E0026_04B_SV.xma", 1 }, { "xenon/event/e0026/E0026_04_SN.xma", 1 }, { "xenon/event/e0026/E0026_06A_SN.xma", 1 }, { "xenon/event/e0026/E0026_07_SN.xma", 1 }, { "xenon/event/e0026/E0026_09_SV.xma", 1 }, { "xenon/event/e0026/E0026_10_SN.xma", 1 }, { "xenon/event/e0026/E0026_11_SN.xma", 1 }, { "xenon/event/e0026/E0026_12_SN.xma", 1 }, { "xenon/event/e0026/E0026_13_SV.xma", 1 }, { "xenon/event/e0026/J0026_00_SN.xma", 1 }, { "xenon/event/e0026/J0026_01_SV.xma", 1 }, { "xenon/event/e0026/J0026_02B_SV.xma", 1 }, { "xenon/event/e0026/J0026_02_SV.xma", 1 }, { "xenon/event/e0026/J0026_03_SN.xma", 1 }, { "xenon/event/e0026/J0026_04_SN.xma", 1 }, { "xenon/event/e0026/J0026_04_SV.xma", 1 }, { "xenon/event/e0026/J0026_06_SN.xma", 1 }, { "xenon/event/e0026/J0026_07_SN.xma", 1 }, { "xenon/event/e0026/J0026_09_SV.xma", 1 }, { "xenon/event/e0026/J0026_10_SN.xma", 1 }, { "xenon/event/e0026/J0026_11_SN.xma", 1 }, { "xenon/event/e0026/J0026_12_SN.xma", 1 }, { "xenon/event/e0026/J0026_13_SV.xma", 1 }, { "xenon/event/e0028/E0028_00A_COM.xma", 1 }, { "xenon/event/e0028/E0028_00B_COM.xma", 1 }, { "xenon/event/e0028/E0028_00C_COM.xma", 1 }, { "xenon/event/e0028/E0028_01A_COM.xma", 1 }, { "xenon/event/e0028/E0028_01B_COM.xma", 1 }, { "xenon/event/e0028/J0028_00A_COM.xma", 1 }, { "xenon/event/e0028/J0028_00B_COM.xma", 1 }, { "xenon/event/e0028/J0028_00C_COM.xma", 1 }, { "xenon/event/e0028/J0028_01A_COM.xma", 1 }, { "xenon/event/e0028/J0028_01B_COM.xma", 1 }, { "xenon/event/e0029/E0029_00_SN.xma", 1 }, { "xenon/event/e0029/E0029_01_EL.xma", 1 }, { "xenon/event/e0029/E0029_02_SN.xma", 1 }, { "xenon/event/e0029/E0029_04A_EG.xma", 1 }, { "xenon/event/e0029/E0029_04B_EG.xma", 1 }, { "xenon/event/e0029/J0029_00_SN.xma", 1 }, { "xenon/event/e0029/J0029_01_EL.xma", 1 }, { "xenon/event/e0029/J0029_02_SN.xma", 1 }, { "xenon/event/e0029/J0029_04A_EG.xma", 1 }, { "xenon/event/e0029/J0029_04B_EG.xma", 1 }, { "xenon/event/e0030/e0030.wmv", 1 }, { "xenon/event/e0031/E0000_20_EL.xma", 1 }, { "xenon/event/e0031/E0000_21_SN.xma", 1 }, { "xenon/event/e0031/E0000_22_EL.xma", 1 }, { "xenon/event/e0031/E0000_23_EL.xma", 1 }, { "xenon/event/e0031/E0000_24_SN.xma", 1 }, { "xenon/event/e0031/E0000_25_EG.xma", 1 }, { "xenon/event/e0031/E0000_26_EL.xma", 1 }, { "xenon/event/e0031/E0000_27_SN.xma", 1 }, { "xenon/event/e0031/E0000_28_EL.xma", 1 }, { "xenon/event/e0031/E0000_29_EG.xma", 1 }, { "xenon/event/e0031/J0000_20_EL.xma", 1 }, { "xenon/event/e0031/J0000_21_SN.xma", 1 }, { "xenon/event/e0031/J0000_22_EL.xma", 1 }, { "xenon/event/e0031/J0000_23_EL.xma", 1 }, { "xenon/event/e0031/J0000_24_SN.xma", 1 }, { "xenon/event/e0031/J0000_25_EG.xma", 1 }, { "xenon/event/e0031/J0000_26_EL.xma", 1 }, { "xenon/event/e0031/J0000_27_SN.xma", 1 }, { "xenon/event/e0031/J0000_28_EL.xma", 1 }, { "xenon/event/e0031/J0000_29_EG.xma", 1 }, { "xenon/event/e0100/e0100.wmv", 1 }, { "xenon/event/e0102/E0102_00_RG.xma", 1 }, { "xenon/event/e0102/E0102_01_SH.xma", 1 }, { "xenon/event/e0102/E0102_02_RG.xma", 1 }, { "xenon/event/e0102/E0102_03_RG.xma", 1 }, { "xenon/event/e0102/E0102_04_RG.xma", 1 }, { "xenon/event/e0102/E0102_05_SH.xma", 1 }, { "xenon/event/e0102/J0102_00_RG.xma", 1 }, { "xenon/event/e0102/J0102_01_SH.xma", 1 }, { "xenon/event/e0102/J0102_02_RG.xma", 1 }, { "xenon/event/e0102/J0102_03_RG.xma", 1 }, { "xenon/event/e0102/J0102_04_RG.xma", 1 }, { "xenon/event/e0102/J0102_05_SH.xma", 1 }, { "xenon/event/e0103/E0103_01_RG.xma", 1 }, { "xenon/event/e0103/E0103_02_RG.xma", 1 }, { "xenon/event/e0103/E0103_03_RG.xma", 1 }, { "xenon/event/e0103/E0103_04_SH.xma", 1 }, { "xenon/event/e0103/E0103_05A_RG.xma", 1 }, { "xenon/event/e0103/E0103_05B_RG.xma", 1 }, { "xenon/event/e0103/E0103_06_RG.xma", 1 }, { "xenon/event/e0103/E0103_07_RG.xma", 1 }, { "xenon/event/e0103/E0103_09_SH.xma", 1 }, { "xenon/event/e0103/E0103_10_RG.xma", 1 }, { "xenon/event/e0103/J0103_01_RG.xma", 1 }, { "xenon/event/e0103/J0103_02_RG.xma", 1 }, { "xenon/event/e0103/J0103_03_RG.xma", 1 }, { "xenon/event/e0103/J0103_04_SH.xma", 1 }, { "xenon/event/e0103/J0103_05_RG.xma", 1 }, { "xenon/event/e0103/J0103_06_RG.xma", 1 }, { "xenon/event/e0103/J0103_09_SH.xma", 1 }, { "xenon/event/e0103/J0103_10_RG.xma", 1 }, { "xenon/event/e0104/E0104_00_RG.xma", 1 }, { "xenon/event/e0104/E0104_01_RG.xma", 1 }, { "xenon/event/e0104/E0104_02_SH.xma", 1 }, { "xenon/event/e0104/E0104_03_RG.xma", 1 }, { "xenon/event/e0104/E0104_04_RG.xma", 1 }, { "xenon/event/e0104/E0104_05_EG.xma", 1 }, { "xenon/event/e0104/E0104_06_EG.xma", 1 }, { "xenon/event/e0104/E0104_07_RG.xma", 1 }, { "xenon/event/e0104/E0104_08_EG.xma", 1 }, { "xenon/event/e0104/E0104_09_EG.xma", 1 }, { "xenon/event/e0104/E0104_10_RG.xma", 1 }, { "xenon/event/e0104/E0104_11_EG.xma", 1 }, { "xenon/event/e0104/E0104_12_SH.xma", 1 }, { "xenon/event/e0104/E0104_13_EG.xma", 1 }, { "xenon/event/e0104/E0104_15_RG.xma", 1 }, { "xenon/event/e0104/E0104_16_RG.xma", 1 }, { "xenon/event/e0104/E0104_19_MF.xma", 1 }, { "xenon/event/e0104/E0104_20_MF.xma", 1 }, { "xenon/event/e0104/E0104_22_SH.xma", 1 }, { "xenon/event/e0104/E0104_23_MF.xma", 1 }, { "xenon/event/e0104/E0104_24_MF.xma", 1 }, { "xenon/event/e0104/E0104_26_MF.xma", 1 }, { "xenon/event/e0104/E0104_27_MF.xma", 1 }, { "xenon/event/e0104/E0104_28_MF.xma", 1 }, { "xenon/event/e0104/E0104_29_MF.xma", 1 }, { "xenon/event/e0104/E0104_30_RG.xma", 1 }, { "xenon/event/e0104/E0104_31_SH.xma", 1 }, { "xenon/event/e0104/J0104_00_RG.xma", 1 }, { "xenon/event/e0104/J0104_01_RG.xma", 1 }, { "xenon/event/e0104/J0104_02_SH.xma", 1 }, { "xenon/event/e0104/J0104_03_RG.xma", 1 }, { "xenon/event/e0104/J0104_04_RG.xma", 1 }, { "xenon/event/e0104/J0104_05_EG.xma", 1 }, { "xenon/event/e0104/J0104_06_EG.xma", 1 }, { "xenon/event/e0104/J0104_07_RG.xma", 1 }, { "xenon/event/e0104/J0104_08_EG.xma", 1 }, { "xenon/event/e0104/J0104_09_EG.xma", 1 }, { "xenon/event/e0104/J0104_10_RG.xma", 1 }, { "xenon/event/e0104/J0104_11_EG.xma", 1 }, { "xenon/event/e0104/J0104_13_EG.xma", 1 }, { "xenon/event/e0104/J0104_14_SH.xma", 1 }, { "xenon/event/e0104/J0104_15_RG.xma", 1 }, { "xenon/event/e0104/J0104_16_RG.xma", 1 }, { "xenon/event/e0104/J0104_19_MF.xma", 1 }, { "xenon/event/e0104/J0104_20_MF.xma", 1 }, { "xenon/event/e0104/J0104_22_SH.xma", 1 }, { "xenon/event/e0104/J0104_23_MF.xma", 1 }, { "xenon/event/e0104/J0104_24_MF.xma", 1 }, { "xenon/event/e0104/J0104_26_MF.xma", 1 }, { "xenon/event/e0104/J0104_27_MF.xma", 1 }, { "xenon/event/e0104/J0104_28_MF.xma", 1 }, { "xenon/event/e0104/J0104_29_MF.xma", 1 }, { "xenon/event/e0104/J0104_30_RG.xma", 1 }, { "xenon/event/e0104/J0104_31_SH.xma", 1 }, { "xenon/event/e0105/E0105_00_RG.xma", 1 }, { "xenon/event/e0105/E0105_01_SH.xma", 1 }, { "xenon/event/e0105/E0105_02_RG.xma", 1 }, { "xenon/event/e0105/E0105_03A_SH.xma", 1 }, { "xenon/event/e0105/E0105_03B_SH.xma", 1 }, { "xenon/event/e0105/J0105_00_RG.xma", 1 }, { "xenon/event/e0105/J0105_01_SH.xma", 1 }, { "xenon/event/e0105/J0105_02_RG.xma", 1 }, { "xenon/event/e0105/J0105_03A_SH.xma", 1 }, { "xenon/event/e0105/J0105_03B_SH.xma", 1 }, { "xenon/event/e0106/E0106_00_RG.xma", 1 }, { "xenon/event/e0106/E0106_01_RG.xma", 1 }, { "xenon/event/e0106/E0106_02_RG.xma", 1 }, { "xenon/event/e0106/E0106_03_RG.xma", 1 }, { "xenon/event/e0106/E0106_04_SH.xma", 1 }, { "xenon/event/e0106/E0106_07_SH.xma", 1 }, { "xenon/event/e0106/E0106_08_RG.xma", 1 }, { "xenon/event/e0106/E0106_09_RG.xma", 1 }, { "xenon/event/e0106/E0106_10_RG.xma", 1 }, { "xenon/event/e0106/E0106_11_RG.xma", 1 }, { "xenon/event/e0106/E0106_12_SH.xma", 1 }, { "xenon/event/e0106/E0106_13_SH.xma", 1 }, { "xenon/event/e0106/J0106_00_RG.xma", 1 }, { "xenon/event/e0106/J0106_01_RG.xma", 1 }, { "xenon/event/e0106/J0106_02_RG.xma", 1 }, { "xenon/event/e0106/J0106_03_RG.xma", 1 }, { "xenon/event/e0106/J0106_04_SH.xma", 1 }, { "xenon/event/e0106/J0106_05_RG.xma", 1 }, { "xenon/event/e0106/J0106_06_RG.xma", 1 }, { "xenon/event/e0106/J0106_07_SH.xma", 1 }, { "xenon/event/e0106/J0106_08_RG.xma", 1 }, { "xenon/event/e0106/J0106_09_RG.xma", 1 }, { "xenon/event/e0106/J0106_10_RG.xma", 1 }, { "xenon/event/e0106/J0106_11_RG.xma", 1 }, { "xenon/event/e0106/J0106_12_SH.xma", 1 }, { "xenon/event/e0106/J0106_13_SH.xma", 1 }, { "xenon/event/e0109/E0109_00_RG.xma", 1 }, { "xenon/event/e0109/E0109_02_RG.xma", 1 }, { "xenon/event/e0109/E0109_03_RG.xma", 1 }, { "xenon/event/e0109/E0109_05_SH.xma", 1 }, { "xenon/event/e0109/E0109_06_RG.xma", 1 }, { "xenon/event/e0109/E0109_08_RG.xma", 1 }, { "xenon/event/e0109/E0109_09_RG.xma", 1 }, { "xenon/event/e0109/E0109_10_RG.xma", 1 }, { "xenon/event/e0109/E0109_11_RG.xma", 1 }, { "xenon/event/e0109/E0109_12_SH.xma", 1 }, { "xenon/event/e0109/J0109_00_RG.xma", 1 }, { "xenon/event/e0109/J0109_02_RG.xma", 1 }, { "xenon/event/e0109/J0109_03_RG.xma", 1 }, { "xenon/event/e0109/J0109_05_SH.xma", 1 }, { "xenon/event/e0109/J0109_06_RG.xma", 1 }, { "xenon/event/e0109/J0109_08_RG.xma", 1 }, { "xenon/event/e0109/J0109_09_RG.xma", 1 }, { "xenon/event/e0109/J0109_10_RG.xma", 1 }, { "xenon/event/e0109/J0109_11_RG.xma", 1 }, { "xenon/event/e0109/J0109_12_SH.xma", 1 }, { "xenon/event/e0113/E0113_00_RG.xma", 1 }, { "xenon/event/e0113/E0113_01_RG.xma", 1 }, { "xenon/event/e0113/E0113_02_GN.xma", 1 }, { "xenon/event/e0113/E0113_03_GN.xma", 1 }, { "xenon/event/e0113/E0113_04_RG.xma", 1 }, { "xenon/event/e0113/E0113_05_RG.xma", 1 }, { "xenon/event/e0113/J0113_00_RG.xma", 1 }, { "xenon/event/e0113/J0113_01_RG.xma", 1 }, { "xenon/event/e0113/J0113_02_GN.xma", 1 }, { "xenon/event/e0113/J0113_03_GN.xma", 1 }, { "xenon/event/e0113/J0113_04_RG.xma", 1 }, { "xenon/event/e0113/J0113_05_RG.xma", 1 }, { "xenon/event/e0114/E0114_00_RG.xma", 1 }, { "xenon/event/e0114/E0114_01_RG.xma", 1 }, { "xenon/event/e0114/E0114_02_OM.xma", 1 }, { "xenon/event/e0114/E0114_03_OM.xma", 1 }, { "xenon/event/e0114/E0114_04_RG.xma", 1 }, { "xenon/event/e0114/E0114_05_OM.xma", 1 }, { "xenon/event/e0114/E0114_06_OM.xma", 1 }, { "xenon/event/e0114/E0114_07_OM.xma", 1 }, { "xenon/event/e0114/E0114_08_OM.xma", 1 }, { "xenon/event/e0114/E0114_09A_OM.xma", 1 }, { "xenon/event/e0114/E0114_09B_OM.xma", 1 }, { "xenon/event/e0114/E0114_10_RG.xma", 1 }, { "xenon/event/e0114/J0114_00_RG.xma", 1 }, { "xenon/event/e0114/J0114_01_RG.xma", 1 }, { "xenon/event/e0114/J0114_02_OM.xma", 1 }, { "xenon/event/e0114/J0114_03_OM.xma", 1 }, { "xenon/event/e0114/J0114_04_RG.xma", 1 }, { "xenon/event/e0114/J0114_05_OM.xma", 1 }, { "xenon/event/e0114/J0114_06_OM.xma", 1 }, { "xenon/event/e0114/J0114_07_OM.xma", 1 }, { "xenon/event/e0114/J0114_08_OM.xma", 1 }, { "xenon/event/e0114/J0114_09A_OM.xma", 1 }, { "xenon/event/e0114/J0114_09B_OM.xma", 1 }, { "xenon/event/e0114/J0114_10_RG.xma", 1 }, { "xenon/event/e0115/E0115_00_MF.xma", 1 }, { "xenon/event/e0115/E0115_01_SH.xma", 1 }, { "xenon/event/e0115/E0115_02_MF.xma", 1 }, { "xenon/event/e0115/E0115_03_MF.xma", 1 }, { "xenon/event/e0115/E0115_04_MF.xma", 1 }, { "xenon/event/e0115/E0115_05_MF.xma", 1 }, { "xenon/event/e0115/E0115_06_MF.xma", 1 }, { "xenon/event/e0115/E0115_07_MF.xma", 1 }, { "xenon/event/e0115/E0115_09A_MF.xma", 1 }, { "xenon/event/e0115/E0115_09B_MF.xma", 1 }, { "xenon/event/e0115/E0115_10_MF.xma", 1 }, { "xenon/event/e0115/E0115_11_MF.xma", 1 }, { "xenon/event/e0115/E0115_12A_MF.xma", 1 }, { "xenon/event/e0115/E0115_12B_MF.xma", 1 }, { "xenon/event/e0115/E0115_13_MF.xma", 1 }, { "xenon/event/e0115/E0115_14_SH.xma", 1 }, { "xenon/event/e0115/E0115_15_SH.xma", 1 }, { "xenon/event/e0115/E0115_16_MF.xma", 1 }, { "xenon/event/e0115/E0115_17_SH.xma", 1 }, { "xenon/event/e0115/J0115_00_MF.xma", 1 }, { "xenon/event/e0115/J0115_01_SH.xma", 1 }, { "xenon/event/e0115/J0115_02_MF.xma", 1 }, { "xenon/event/e0115/J0115_03_MF.xma", 1 }, { "xenon/event/e0115/J0115_04_MF.xma", 1 }, { "xenon/event/e0115/J0115_05_MF.xma", 1 }, { "xenon/event/e0115/J0115_06_MF.xma", 1 }, { "xenon/event/e0115/J0115_07_MF.xma", 1 }, { "xenon/event/e0115/J0115_09A_MF.xma", 1 }, { "xenon/event/e0115/J0115_09B_MF.xma", 1 }, { "xenon/event/e0115/J0115_10_MF.xma", 1 }, { "xenon/event/e0115/J0115_11_MF.xma", 1 }, { "xenon/event/e0115/J0115_12A_MF.xma", 1 }, { "xenon/event/e0115/J0115_12B_MF.xma", 1 }, { "xenon/event/e0115/J0115_13_MF.xma", 1 }, { "xenon/event/e0115/J0115_14_SH.xma", 1 }, { "xenon/event/e0115/J0115_15_SH.xma", 1 }, { "xenon/event/e0115/J0115_16_MF.xma", 1 }, { "xenon/event/e0115/J0115_17_SH.xma", 1 }, { "xenon/event/e0116/E0116_00_SH.xma", 1 }, { "xenon/event/e0116/E0116_01_SH.xma", 1 }, { "xenon/event/e0116/E0116_02_OM.xma", 1 }, { "xenon/event/e0116/J0116_00_SH.xma", 1 }, { "xenon/event/e0116/J0116_01_SH.xma", 1 }, { "xenon/event/e0116/J0116_02_OM.xma", 1 }, { "xenon/event/e0117/E0117_00A_MF.xma", 1 }, { "xenon/event/e0117/E0117_01_SH.xma", 1 }, { "xenon/event/e0117/J0117_00_MF.xma", 1 }, { "xenon/event/e0117/J0117_01_SH.xma", 1 }, { "xenon/event/e0118/E0118_01_OM.xma", 1 }, { "xenon/event/e0118/J0118_01_OM.xma", 1 }, { "xenon/event/e0119/E0119_00_RG.xma", 1 }, { "xenon/event/e0119/E0119_02_RG.xma", 1 }, { "xenon/event/e0119/E0119_04_SH.xma", 1 }, { "xenon/event/e0119/E0119_05_OM.xma", 1 }, { "xenon/event/e0119/E0119_06_SH.xma", 1 }, { "xenon/event/e0119/E0119_07_RG.xma", 1 }, { "xenon/event/e0119/E0119_08_SH.xma", 1 }, { "xenon/event/e0119/E0119_09_RG.xma", 1 }, { "xenon/event/e0119/E0119_10_RG.xma", 1 }, { "xenon/event/e0119/E0119_11_RG.xma", 1 }, { "xenon/event/e0119/E0119_12_RG.xma", 1 }, { "xenon/event/e0119/J0119_00_RG.xma", 1 }, { "xenon/event/e0119/J0119_02_RG.xma", 1 }, { "xenon/event/e0119/J0119_04_SH.xma", 1 }, { "xenon/event/e0119/J0119_05_OM.xma", 1 }, { "xenon/event/e0119/J0119_06_SH.xma", 1 }, { "xenon/event/e0119/J0119_07_RG.xma", 1 }, { "xenon/event/e0119/J0119_08_SH.xma", 1 }, { "xenon/event/e0119/J0119_09_RG.xma", 1 }, { "xenon/event/e0119/J0119_10_RG.xma", 1 }, { "xenon/event/e0119/J0119_11_RG.xma", 1 }, { "xenon/event/e0119/J0119_12_RG.xma", 1 }, { "xenon/event/e0120/E0120_00_EG.xma", 1 }, { "xenon/event/e0120/E0120_01_EG.xma", 1 }, { "xenon/event/e0120/E0120_02_SH.xma", 1 }, { "xenon/event/e0120/E0120_03_EG.xma", 1 }, { "xenon/event/e0120/E0120_04_EG.xma", 1 }, { "xenon/event/e0120/E0120_05_SH.xma", 1 }, { "xenon/event/e0120/E0120_06_EG.xma", 1 }, { "xenon/event/e0120/E0120_07_EG.xma", 1 }, { "xenon/event/e0120/E0120_08_EG.xma", 1 }, { "xenon/event/e0120/J0120_00_EG.xma", 1 }, { "xenon/event/e0120/J0120_01_EG.xma", 1 }, { "xenon/event/e0120/J0120_02_SH.xma", 1 }, { "xenon/event/e0120/J0120_03_EG.xma", 1 }, { "xenon/event/e0120/J0120_04_EG.xma", 1 }, { "xenon/event/e0120/J0120_05_SH.xma", 1 }, { "xenon/event/e0120/J0120_06_EG.xma", 1 }, { "xenon/event/e0120/J0120_07_EG.xma", 1 }, { "xenon/event/e0120/J0120_08_EG.xma", 1 }, { "xenon/event/e0122/E0122_00_SV.xma", 1 }, { "xenon/event/e0122/E0122_01A_SH.xma", 1 }, { "xenon/event/e0122/E0122_01_SH.xma", 1 }, { "xenon/event/e0122/E0122_02_SV.xma", 1 }, { "xenon/event/e0122/E0122_03_SH.xma", 1 }, { "xenon/event/e0122/E0122_04_SH.xma", 1 }, { "xenon/event/e0122/E0122_05_SV.xma", 1 }, { "xenon/event/e0122/E0122_06_SV.xma", 1 }, { "xenon/event/e0122/E0122_07_SV.xma", 1 }, { "xenon/event/e0122/E0122_08_SV.xma", 1 }, { "xenon/event/e0122/E0122_09_SH.xma", 1 }, { "xenon/event/e0122/E0122_10_SV.xma", 1 }, { "xenon/event/e0122/E0122_11_SH.xma", 1 }, { "xenon/event/e0122/E0122_12_SV.xma", 1 }, { "xenon/event/e0122/E0122_13_SH.xma", 1 }, { "xenon/event/e0122/E0122_14_SV.xma", 1 }, { "xenon/event/e0122/E0122_15_SV.xma", 1 }, { "xenon/event/e0122/E0122_16_SH.xma", 1 }, { "xenon/event/e0122/E0122_17_SH.xma", 1 }, { "xenon/event/e0122/E0122_18_SV.xma", 1 }, { "xenon/event/e0122/E0122_19_SH.xma", 1 }, { "xenon/event/e0122/E0122_21_SH.xma", 1 }, { "xenon/event/e0122/J0122_00_SV.xma", 1 }, { "xenon/event/e0122/J0122_01_SH.xma", 1 }, { "xenon/event/e0122/J0122_02_SV.xma", 1 }, { "xenon/event/e0122/J0122_03_SH.xma", 1 }, { "xenon/event/e0122/J0122_04_SH.xma", 1 }, { "xenon/event/e0122/J0122_05_SV.xma", 1 }, { "xenon/event/e0122/J0122_06_SV.xma", 1 }, { "xenon/event/e0122/J0122_07_SV.xma", 1 }, { "xenon/event/e0122/J0122_08_SV.xma", 1 }, { "xenon/event/e0122/J0122_09_SH.xma", 1 }, { "xenon/event/e0122/J0122_10_SV.xma", 1 }, { "xenon/event/e0122/J0122_11_SH.xma", 1 }, { "xenon/event/e0122/J0122_12_SV.xma", 1 }, { "xenon/event/e0122/J0122_13_SH.xma", 1 }, { "xenon/event/e0122/J0122_14_SV.xma", 1 }, { "xenon/event/e0122/J0122_15_SV.xma", 1 }, { "xenon/event/e0122/J0122_16_SH.xma", 1 }, { "xenon/event/e0122/J0122_17_SH.xma", 1 }, { "xenon/event/e0122/J0122_18_SV.xma", 1 }, { "xenon/event/e0122/J0122_19_SH.xma", 1 }, { "xenon/event/e0122/J0122_21_SH.xma", 1 }, { "xenon/event/e0123/e0123.wmv", 1 }, { "xenon/event/e0124/e0124.wmv", 1 }, { "xenon/event/e0125/E0125_00_SV.xma", 1 }, { "xenon/event/e0125/E0125_01_SH.xma", 1 }, { "xenon/event/e0125/E0125_02A_SV.xma", 1 }, { "xenon/event/e0125/E0125_02B_SV.xma", 1 }, { "xenon/event/e0125/E0125_03_SV.xma", 1 }, { "xenon/event/e0125/E0125_04_SH.xma", 1 }, { "xenon/event/e0125/E0125_05_SH.xma", 1 }, { "xenon/event/e0125/E0125_06_SV.xma", 1 }, { "xenon/event/e0125/E0125_07_SH.xma", 1 }, { "xenon/event/e0125/E0125_08_SV.xma", 1 }, { "xenon/event/e0125/J0125_00_SV.xma", 1 }, { "xenon/event/e0125/J0125_01_SH.xma", 1 }, { "xenon/event/e0125/J0125_02A_SV.xma", 1 }, { "xenon/event/e0125/J0125_02B_SV.xma", 1 }, { "xenon/event/e0125/J0125_03_SV.xma", 1 }, { "xenon/event/e0125/J0125_04_SH.xma", 1 }, { "xenon/event/e0125/J0125_05_SH.xma", 1 }, { "xenon/event/e0125/J0125_06_SV.xma", 1 }, { "xenon/event/e0125/J0125_07_SH.xma", 1 }, { "xenon/event/e0125/J0125_08_SV.xma", 1 }, { "xenon/event/e0126/E0126_00_RG.xma", 1 }, { "xenon/event/e0126/E0126_01_RG.xma", 1 }, { "xenon/event/e0126/E0126_02_RG.xma", 1 }, { "xenon/event/e0126/E0126_03_RG.xma", 1 }, { "xenon/event/e0126/E0126_04_SH.xma", 1 }, { "xenon/event/e0126/E0126_05_SH.xma", 1 }, { "xenon/event/e0126/E0126_06A_RG.xma", 1 }, { "xenon/event/e0126/E0126_06B_RG.xma", 1 }, { "xenon/event/e0126/E0126_07_SH.xma", 1 }, { "xenon/event/e0126/J0126_00_RG.xma", 1 }, { "xenon/event/e0126/J0126_01_RG.xma", 1 }, { "xenon/event/e0126/J0126_02_RG.xma", 1 }, { "xenon/event/e0126/J0126_03_RG.xma", 1 }, { "xenon/event/e0126/J0126_04_SH.xma", 1 }, { "xenon/event/e0126/J0126_05_SH.xma", 1 }, { "xenon/event/e0126/J0126_06A_RG.xma", 1 }, { "xenon/event/e0126/J0126_06B_RG.xma", 1 }, { "xenon/event/e0126/J0126_07_SH.xma", 1 }, { "xenon/event/e0127/E0127_00_MF.xma", 1 }, { "xenon/event/e0127/E0127_01_MF.xma", 1 }, { "xenon/event/e0127/E0127_02_MF.xma", 1 }, { "xenon/event/e0127/E0127_03_MF.xma", 1 }, { "xenon/event/e0127/E0127_04_RG.xma", 1 }, { "xenon/event/e0127/E0127_06_OM.xma", 1 }, { "xenon/event/e0127/E0127_07_OM.xma", 1 }, { "xenon/event/e0127/E0127_08_RG.xma", 1 }, { "xenon/event/e0127/E0127_09_RG.xma", 1 }, { "xenon/event/e0127/E0127_10_RG.xma", 1 }, { "xenon/event/e0127/E0127_11_OM.xma", 1 }, { "xenon/event/e0127/E0127_12A_OM.xma", 1 }, { "xenon/event/e0127/E0127_12B_OM.xma", 1 }, { "xenon/event/e0127/E0127_13_RG.xma", 1 }, { "xenon/event/e0127/E0127_14_RG.xma", 1 }, { "xenon/event/e0127/E0127_15_RG.xma", 1 }, { "xenon/event/e0127/E0127_16_RG.xma", 1 }, { "xenon/event/e0127/E0127_17_SH.xma", 1 }, { "xenon/event/e0127/J0127_00_MF.xma", 1 }, { "xenon/event/e0127/J0127_01_MF.xma", 1 }, { "xenon/event/e0127/J0127_02_MF.xma", 1 }, { "xenon/event/e0127/J0127_03_MF.xma", 1 }, { "xenon/event/e0127/J0127_04_RG.xma", 1 }, { "xenon/event/e0127/J0127_06_OM.xma", 1 }, { "xenon/event/e0127/J0127_07_OM.xma", 1 }, { "xenon/event/e0127/J0127_08_RG.xma", 1 }, { "xenon/event/e0127/J0127_09_RG.xma", 1 }, { "xenon/event/e0127/J0127_10_RG.xma", 1 }, { "xenon/event/e0127/J0127_11_OM.xma", 1 }, { "xenon/event/e0127/J0127_12A_OM.xma", 1 }, { "xenon/event/e0127/J0127_12B_OM.xma", 1 }, { "xenon/event/e0127/J0127_13_RG.xma", 1 }, { "xenon/event/e0127/J0127_14_RG.xma", 1 }, { "xenon/event/e0127/J0127_15_RG.xma", 1 }, { "xenon/event/e0127/J0127_16_RG.xma", 1 }, { "xenon/event/e0127/J0127_17_SH.xma", 1 }, { "xenon/event/e0128/E0128_00_RG.xma", 1 }, { "xenon/event/e0128/E0128_01_RG.xma", 1 }, { "xenon/event/e0128/E0128_02_SH.xma", 1 }, { "xenon/event/e0128/E0128_03_SH.xma", 1 }, { "xenon/event/e0128/E0128_05_OM.xma", 1 }, { "xenon/event/e0128/J0128_00_RG.xma", 1 }, { "xenon/event/e0128/J0128_01_RG.xma", 1 }, { "xenon/event/e0128/J0128_02_SH.xma", 1 }, { "xenon/event/e0128/J0128_03_SH.xma", 1 }, { "xenon/event/e0128/J0128_05_OM.xma", 1 }, { "xenon/event/e0129/E0129_00_SH.xma", 1 }, { "xenon/event/e0129/E0129_01_MF.xma", 1 }, { "xenon/event/e0129/E0129_02_MF.xma", 1 }, { "xenon/event/e0129/E0129_03A_MF.xma", 1 }, { "xenon/event/e0129/E0129_03B_MF.xma", 1 }, { "xenon/event/e0129/E0129_04_SH.xma", 1 }, { "xenon/event/e0129/E0129_05_MF.xma", 1 }, { "xenon/event/e0129/E0129_06_SH.xma", 1 }, { "xenon/event/e0129/E0129_07_SH.xma", 1 }, { "xenon/event/e0129/E0129_09A_MF.xma", 1 }, { "xenon/event/e0129/E0129_09B_MF.xma", 1 }, { "xenon/event/e0129/J0129_00_SH.xma", 1 }, { "xenon/event/e0129/J0129_01_MF.xma", 1 }, { "xenon/event/e0129/J0129_02_MF.xma", 1 }, { "xenon/event/e0129/J0129_03A_MF.xma", 1 }, { "xenon/event/e0129/J0129_03B_MF.xma", 1 }, { "xenon/event/e0129/J0129_04_SH.xma", 1 }, { "xenon/event/e0129/J0129_05_MF.xma", 1 }, { "xenon/event/e0129/J0129_06_SH.xma", 1 }, { "xenon/event/e0129/J0129_07_SH.xma", 1 }, { "xenon/event/e0129/J0129_09A_MF.xma", 1 }, { "xenon/event/e0129/J0129_09B_MF.xma", 1 }, { "xenon/event/e0130/e0130.wmv", 1 }, { "xenon/event/e0200/e0200.wmv", 1 }, { "xenon/event/e0201/E0201_00_SV.xma", 1 }, { "xenon/event/e0201/J0201_00_SV.xma", 1 }, { "xenon/event/e0202/E0202_00_BZ.xma", 1 }, { "xenon/event/e0202/E0202_01_SV.xma", 1 }, { "xenon/event/e0202/E0202_02_SV.xma", 1 }, { "xenon/event/e0202/E0202_03_BZ.xma", 1 }, { "xenon/event/e0202/E0202_04_SV.xma", 1 }, { "xenon/event/e0202/E0202_05_SV.xma", 1 }, { "xenon/event/e0202/E0202_06_MF.xma", 1 }, { "xenon/event/e0202/E0202_07_MF.xma", 1 }, { "xenon/event/e0202/E0202_08_MF.xma", 1 }, { "xenon/event/e0202/E0202_09_MF.xma", 1 }, { "xenon/event/e0202/E0202_10_SV.xma", 1 }, { "xenon/event/e0202/E0202_11_SV.xma", 1 }, { "xenon/event/e0202/J0202_00_BZ.xma", 1 }, { "xenon/event/e0202/J0202_01_SV.xma", 1 }, { "xenon/event/e0202/J0202_02_SV.xma", 1 }, { "xenon/event/e0202/J0202_03_BZ.xma", 1 }, { "xenon/event/e0202/J0202_04_SV.xma", 1 }, { "xenon/event/e0202/J0202_05_SV.xma", 1 }, { "xenon/event/e0202/J0202_06_MF.xma", 1 }, { "xenon/event/e0202/J0202_07_MF.xma", 1 }, { "xenon/event/e0202/J0202_08_MF.xma", 1 }, { "xenon/event/e0202/J0202_09_MF.xma", 1 }, { "xenon/event/e0202/J0202_10_SV.xma", 1 }, { "xenon/event/e0202/J0202_11_SV.xma", 1 }, { "xenon/event/e0203/E0203_00_MF.xma", 1 }, { "xenon/event/e0203/E0203_01_SV.xma", 1 }, { "xenon/event/e0203/E0203_02_MF.xma", 1 }, { "xenon/event/e0203/E0203_03_SV.xma", 1 }, { "xenon/event/e0203/E0203_04_MF.xma", 1 }, { "xenon/event/e0203/E0203_05_MF.xma", 1 }, { "xenon/event/e0203/E0203_06_MF.xma", 1 }, { "xenon/event/e0203/J0203_00_MF.xma", 1 }, { "xenon/event/e0203/J0203_01_SV.xma", 1 }, { "xenon/event/e0203/J0203_02_MF.xma", 1 }, { "xenon/event/e0203/J0203_03_SV.xma", 1 }, { "xenon/event/e0203/J0203_04_MF.xma", 1 }, { "xenon/event/e0203/J0203_05_MF.xma", 1 }, { "xenon/event/e0203/J0203_06_MF.xma", 1 }, { "xenon/event/e0204/E0204_00_SV.xma", 1 }, { "xenon/event/e0204/E0204_01_SV.xma", 1 }, { "xenon/event/e0204/E0204_02_SV.xma", 1 }, { "xenon/event/e0204/E0204_03_SV.xma", 1 }, { "xenon/event/e0204/E0204_04_SV.xma", 1 }, { "xenon/event/e0204/J0204_00_SV.xma", 1 }, { "xenon/event/e0204/J0204_01_SV.xma", 1 }, { "xenon/event/e0204/J0204_02_SV.xma", 1 }, { "xenon/event/e0204/J0204_03_SV.xma", 1 }, { "xenon/event/e0204/J0204_04_SV.xma", 1 }, { "xenon/event/e0205/E0205_00_BZ.xma", 1 }, { "xenon/event/e0205/E0205_01_BZ.xma", 1 }, { "xenon/event/e0205/E0205_02_BZ.xma", 1 }, { "xenon/event/e0205/J0205_00_BZ.xma", 1 }, { "xenon/event/e0205/J0205_01_BZ.xma", 1 }, { "xenon/event/e0205/J0205_02_BZ.xma", 1 }, { "xenon/event/e0206/E0000_32_SV.xma", 1 }, { "xenon/event/e0206/E0206_00_AM.xma", 1 }, { "xenon/event/e0206/E0206_01_SV.xma", 1 }, { "xenon/event/e0206/E0206_02_AM.xma", 1 }, { "xenon/event/e0206/E0206_04_AM.xma", 1 }, { "xenon/event/e0206/E0206_05_AM.xma", 1 }, { "xenon/event/e0206/E0206_06_SV.xma", 1 }, { "xenon/event/e0206/E0206_07A_SV.xma", 1 }, { "xenon/event/e0206/E0206_07B_SV.xma", 1 }, { "xenon/event/e0206/E0206_08_AM.xma", 1 }, { "xenon/event/e0206/E0206_09_SV.xma", 1 }, { "xenon/event/e0206/E0206_10_SV.xma", 1 }, { "xenon/event/e0206/E0206_11_AM.xma", 1 }, { "xenon/event/e0206/E0206_12_SV.xma", 1 }, { "xenon/event/e0206/E0206_13_AM.xma", 1 }, { "xenon/event/e0206/E0206_14_SV.xma", 1 }, { "xenon/event/e0206/E0206_15_AM.xma", 1 }, { "xenon/event/e0206/E0206_16_SV.xma", 1 }, { "xenon/event/e0206/E0206_17_AM.xma", 1 }, { "xenon/event/e0206/E0206_18_SV.xma", 1 }, { "xenon/event/e0206/J0000_32_SV.xma", 1 }, { "xenon/event/e0206/J0206_00_AM.xma", 1 }, { "xenon/event/e0206/J0206_01_SV.xma", 1 }, { "xenon/event/e0206/J0206_02_AM.xma", 1 }, { "xenon/event/e0206/J0206_03_AM.xma", 1 }, { "xenon/event/e0206/J0206_04_AM.xma", 1 }, { "xenon/event/e0206/J0206_05_AM.xma", 1 }, { "xenon/event/e0206/J0206_06_SV.xma", 1 }, { "xenon/event/e0206/J0206_07A_SV.xma", 1 }, { "xenon/event/e0206/J0206_07B_SV.xma", 1 }, { "xenon/event/e0206/J0206_08_AM.xma", 1 }, { "xenon/event/e0206/J0206_09_SV.xma", 1 }, { "xenon/event/e0206/J0206_10_SV.xma", 1 }, { "xenon/event/e0206/J0206_11_AM.xma", 1 }, { "xenon/event/e0206/J0206_12_SV.xma", 1 }, { "xenon/event/e0206/J0206_13_AM.xma", 1 }, { "xenon/event/e0206/J0206_14_SV.xma", 1 }, { "xenon/event/e0206/J0206_15_AM.xma", 1 }, { "xenon/event/e0206/J0206_16_SV.xma", 1 }, { "xenon/event/e0206/J0206_17_AM.xma", 1 }, { "xenon/event/e0206/J0206_18_SV.xma", 1 }, { "xenon/event/e0207/E0207_00_AM.xma", 1 }, { "xenon/event/e0207/E0207_01_AM.xma", 1 }, { "xenon/event/e0207/E0207_03_SV.xma", 1 }, { "xenon/event/e0207/E0207_04_AM.xma", 1 }, { "xenon/event/e0207/E0207_05_AM.xma", 1 }, { "xenon/event/e0207/E0207_06_SV.xma", 1 }, { "xenon/event/e0207/E0207_07_AM.xma", 1 }, { "xenon/event/e0207/E0207_08_SV.xma", 1 }, { "xenon/event/e0207/E0207_09_AM.xma", 1 }, { "xenon/event/e0207/E0207_10_SV.xma", 1 }, { "xenon/event/e0207/E0207_11_SV.xma", 1 }, { "xenon/event/e0207/J0207_00_AM.xma", 1 }, { "xenon/event/e0207/J0207_01_AM.xma", 1 }, { "xenon/event/e0207/J0207_03_SV.xma", 1 }, { "xenon/event/e0207/J0207_04_AM.xma", 1 }, { "xenon/event/e0207/J0207_05_AM.xma", 1 }, { "xenon/event/e0207/J0207_06_SV.xma", 1 }, { "xenon/event/e0207/J0207_07_AM.xma", 1 }, { "xenon/event/e0207/J0207_08_SV.xma", 1 }, { "xenon/event/e0207/J0207_09_AM.xma", 1 }, { "xenon/event/e0207/J0207_10_SV.xma", 1 }, { "xenon/event/e0207/J0207_11_SV.xma", 1 }, { "xenon/event/e0208/E0208_00_AM.xma", 1 }, { "xenon/event/e0208/E0208_01_AM.xma", 1 }, { "xenon/event/e0208/E0208_02_SV.xma", 1 }, { "xenon/event/e0208/J0208_00_AM.xma", 1 }, { "xenon/event/e0208/J0208_01_AM.xma", 1 }, { "xenon/event/e0210/E0210_00_AM.xma", 1 }, { "xenon/event/e0210/E0210_01_AM.xma", 1 }, { "xenon/event/e0210/E0210_02_SV.xma", 1 }, { "xenon/event/e0210/E0210_03_AM.xma", 1 }, { "xenon/event/e0210/E0210_04_AM.xma", 1 }, { "xenon/event/e0210/E0210_05_SV.xma", 1 }, { "xenon/event/e0210/E0210_06_SV.xma", 1 }, { "xenon/event/e0210/E0210_07_SV.xma", 1 }, { "xenon/event/e0210/E0210_08_AM.xma", 1 }, { "xenon/event/e0210/E0210_09_AM.xma", 1 }, { "xenon/event/e0210/E0210_10_AM.xma", 1 }, { "xenon/event/e0210/J0210_00_AM.xma", 1 }, { "xenon/event/e0210/J0210_01_AM.xma", 1 }, { "xenon/event/e0210/J0210_02_SV.xma", 1 }, { "xenon/event/e0210/J0210_03_AM.xma", 1 }, { "xenon/event/e0210/J0210_04_AM.xma", 1 }, { "xenon/event/e0210/J0210_05_SV.xma", 1 }, { "xenon/event/e0210/J0210_06_SV.xma", 1 }, { "xenon/event/e0210/J0210_07_SV.xma", 1 }, { "xenon/event/e0210/J0210_08_AM.xma", 1 }, { "xenon/event/e0210/J0210_09_AM.xma", 1 }, { "xenon/event/e0210/J0210_10_AM.xma", 1 }, { "xenon/event/e0211/E0211_00_BZ.xma", 1 }, { "xenon/event/e0211/E0211_02_BZ.xma", 1 }, { "xenon/event/e0211/E0211_03_SV.xma", 1 }, { "xenon/event/e0211/E0211_04_SV.xma", 1 }, { "xenon/event/e0211/E0211_05_SV.xma", 1 }, { "xenon/event/e0211/E0211_06_BZ.xma", 1 }, { "xenon/event/e0211/E0211_08_BZ.xma", 1 }, { "xenon/event/e0211/E0211_09_BZ.xma", 1 }, { "xenon/event/e0211/E0211_11_BZ.xma", 1 }, { "xenon/event/e0211/E0211_12_SV.xma", 1 }, { "xenon/event/e0211/E0211_13_SV.xma", 1 }, { "xenon/event/e0211/J0211_00_BZ.xma", 1 }, { "xenon/event/e0211/J0211_02_BZ.xma", 1 }, { "xenon/event/e0211/J0211_03_SV.xma", 1 }, { "xenon/event/e0211/J0211_04_SV.xma", 1 }, { "xenon/event/e0211/J0211_05_SV.xma", 1 }, { "xenon/event/e0211/J0211_06_BZ.xma", 1 }, { "xenon/event/e0211/J0211_07_SV.xma", 1 }, { "xenon/event/e0211/J0211_08_BZ.xma", 1 }, { "xenon/event/e0211/J0211_09_BZ.xma", 1 }, { "xenon/event/e0211/J0211_11_BZ.xma", 1 }, { "xenon/event/e0211/J0211_12_SV.xma", 1 }, { "xenon/event/e0211/J0211_13_SV.xma", 1 }, { "xenon/event/e0212/E0212_00_SV.xma", 1 }, { "xenon/event/e0212/E0212_01_SV.xma", 1 }, { "xenon/event/e0212/J0212_00_SV.xma", 1 }, { "xenon/event/e0212/J0212_01_SV.xma", 1 }, { "xenon/event/e0213/E0213_00_BZ.xma", 1 }, { "xenon/event/e0213/E0213_01_BZ.xma", 1 }, { "xenon/event/e0213/E0213_02_BZ.xma", 1 }, { "xenon/event/e0213/E0213_04_SV.xma", 1 }, { "xenon/event/e0213/J0213_00_BZ.xma", 1 }, { "xenon/event/e0213/J0213_01_BZ.xma", 1 }, { "xenon/event/e0213/J0213_02_BZ.xma", 1 }, { "xenon/event/e0213/J0213_04_SV.xma", 1 }, { "xenon/event/e0214/E0214_00_AM.xma", 1 }, { "xenon/event/e0214/E0214_01_AM.xma", 1 }, { "xenon/event/e0214/E0214_02_EL.xma", 1 }, { "xenon/event/e0214/E0214_03_COM.xma", 1 }, { "xenon/event/e0214/E0214_05_COM.xma", 1 }, { "xenon/event/e0214/E0214_06_AM.xma", 1 }, { "xenon/event/e0214/E0214_07_EL.xma", 1 }, { "xenon/event/e0214/J0214_00_AM.xma", 1 }, { "xenon/event/e0214/J0214_01_AM.xma", 1 }, { "xenon/event/e0214/J0214_02_EL.xma", 1 }, { "xenon/event/e0214/J0214_03_COM.xma", 1 }, { "xenon/event/e0214/J0214_05_COM.xma", 1 }, { "xenon/event/e0214/J0214_06_AM.xma", 1 }, { "xenon/event/e0214/J0214_07_EL.xma", 1 }, { "xenon/event/e0215/E0215_00_EL.xma", 1 }, { "xenon/event/e0215/E0215_01_AM.xma", 1 }, { "xenon/event/e0215/E0215_02_EL.xma", 1 }, { "xenon/event/e0215/E0215_03_AM.xma", 1 }, { "xenon/event/e0215/E0215_05_EL.xma", 1 }, { "xenon/event/e0215/E0215_08_AM.xma", 1 }, { "xenon/event/e0215/E0215_10_EL.xma", 1 }, { "xenon/event/e0215/E0215_12_AM.xma", 1 }, { "xenon/event/e0215/E0215_13_AM.xma", 1 }, { "xenon/event/e0215/E0215_15_AM.xma", 1 }, { "xenon/event/e0215/E0215_16_EL.xma", 1 }, { "xenon/event/e0215/E0215_17_AM.xma", 1 }, { "xenon/event/e0215/E0215_20_AM.xma", 1 }, { "xenon/event/e0215/E0215_21_EL.xma", 1 }, { "xenon/event/e0215/E0215_21_EL_ALT.xma", 1 }, { "xenon/event/e0215/E0215_22_AM.xma", 1 }, { "xenon/event/e0215/J0215_00_EL.xma", 1 }, { "xenon/event/e0215/J0215_01_AM.xma", 1 }, { "xenon/event/e0215/J0215_02_EL.xma", 1 }, { "xenon/event/e0215/J0215_03_AM.xma", 1 }, { "xenon/event/e0215/J0215_05_EL.xma", 1 }, { "xenon/event/e0215/J0215_08_AM.xma", 1 }, { "xenon/event/e0215/J0215_10_EL.xma", 1 }, { "xenon/event/e0215/J0215_12_AM.xma", 1 }, { "xenon/event/e0215/J0215_13_AM.xma", 1 }, { "xenon/event/e0215/J0215_15_AM.xma", 1 }, { "xenon/event/e0215/J0215_16_EL.xma", 1 }, { "xenon/event/e0215/J0215_17_AM.xma", 1 }, { "xenon/event/e0215/J0215_20_AM.xma", 1 }, { "xenon/event/e0215/J0215_21_EL.xma", 1 }, { "xenon/event/e0215/J0215_22_AM.xma", 1 }, { "xenon/event/e0216/E0216_00_EL.xma", 1 }, { "xenon/event/e0216/E0216_01_EG.xma", 1 }, { "xenon/event/e0216/J0216_00_EL.xma", 1 }, { "xenon/event/e0216/J0216_01_EG.xma", 1 }, { "xenon/event/e0217/E0217_00_SV.xma", 1 }, { "xenon/event/e0217/E0217_01_SV.xma", 1 }, { "xenon/event/e0217/E0217_02_SV.xma", 1 }, { "xenon/event/e0217/E0217_03_MF.xma", 1 }, { "xenon/event/e0217/E0217_04_MF.xma", 1 }, { "xenon/event/e0217/E0217_06_MF.xma", 1 }, { "xenon/event/e0217/J0217_00_SV.xma", 1 }, { "xenon/event/e0217/J0217_01_SV.xma", 1 }, { "xenon/event/e0217/J0217_02_SV.xma", 1 }, { "xenon/event/e0217/J0217_03_MF.xma", 1 }, { "xenon/event/e0217/J0217_04_MF.xma", 1 }, { "xenon/event/e0217/J0217_06_MF.xma", 1 }, { "xenon/event/e0221/E0221_00_SV.xma", 1 }, { "xenon/event/e0221/E0221_01_SV.xma", 1 }, { "xenon/event/e0221/E0221_02_SV.xma", 1 }, { "xenon/event/e0221/E0221_03_SR.xma", 1 }, { "xenon/event/e0221/E0221_04_SR.xma", 1 }, { "xenon/event/e0221/E0221_05_SR.xma", 1 }, { "xenon/event/e0221/E0221_06_SR.xma", 1 }, { "xenon/event/e0221/E0221_07_SR.xma", 1 }, { "xenon/event/e0221/E0221_09_SR.xma", 1 }, { "xenon/event/e0221/E0221_10_SR.xma", 1 }, { "xenon/event/e0221/E0221_12_SR.xma", 1 }, { "xenon/event/e0221/E0221_13_SR.xma", 1 }, { "xenon/event/e0221/E0221_14_SR.xma", 1 }, { "xenon/event/e0221/E0221_16_SR.xma", 1 }, { "xenon/event/e0221/E0221_17_SR.xma", 1 }, { "xenon/event/e0221/E0221_18_SR.xma", 1 }, { "xenon/event/e0221/J0221_00_SV.xma", 1 }, { "xenon/event/e0221/J0221_01_SV.xma", 1 }, { "xenon/event/e0221/J0221_02_SV.xma", 1 }, { "xenon/event/e0221/J0221_03_SR.xma", 1 }, { "xenon/event/e0221/J0221_04_SR.xma", 1 }, { "xenon/event/e0221/J0221_05_SR.xma", 1 }, { "xenon/event/e0221/J0221_06_SR.xma", 1 }, { "xenon/event/e0221/J0221_07_SR.xma", 1 }, { "xenon/event/e0221/J0221_09_SR.xma", 1 }, { "xenon/event/e0221/J0221_10_SR.xma", 1 }, { "xenon/event/e0221/J0221_12_SR.xma", 1 }, { "xenon/event/e0221/J0221_13_SR.xma", 1 }, { "xenon/event/e0221/J0221_14_SR.xma", 1 }, { "xenon/event/e0221/J0221_16_SR.xma", 1 }, { "xenon/event/e0221/J0221_17_SR.xma", 1 }, { "xenon/event/e0221/J0221_18_SR.xma", 1 }, { "xenon/event/e0222/E0222_00_EL.xma", 1 }, { "xenon/event/e0222/E0222_02_SV.xma", 1 }, { "xenon/event/e0222/J0222_00_EL.xma", 1 }, { "xenon/event/e0222/J0222_02_SV.xma", 1 }, { "xenon/event/e0223/E0223_00_SV.xma", 1 }, { "xenon/event/e0223/E0223_01_BZ.xma", 1 }, { "xenon/event/e0223/E0223_02_SV.xma", 1 }, { "xenon/event/e0223/E0223_03_BZ.xma", 1 }, { "xenon/event/e0223/E0223_04_NPC.xma", 1 }, { "xenon/event/e0223/E0223_05_SV.xma", 1 }, { "xenon/event/e0223/E0223_06_BZ.xma", 1 }, { "xenon/event/e0223/E0223_09_SV.xma", 1 }, { "xenon/event/e0223/J0223_00_SV.xma", 1 }, { "xenon/event/e0223/J0223_01_BZ.xma", 1 }, { "xenon/event/e0223/J0223_02_SV.xma", 1 }, { "xenon/event/e0223/J0223_03_BZ.xma", 1 }, { "xenon/event/e0223/J0223_04_NPC.xma", 1 }, { "xenon/event/e0223/J0223_05_SV.xma", 1 }, { "xenon/event/e0223/J0223_06_BZ.xma", 1 }, { "xenon/event/e0223/J0223_08_SV.xma", 1 }, { "xenon/event/e0223/J0223_09_SV.xma", 1 }, { "xenon/event/e0223/J0223_10_BZ.xma", 1 }, { "xenon/event/e0226/E0226_00A_SV.xma", 1 }, { "xenon/event/e0226/E0226_00B_SV.xma", 1 }, { "xenon/event/e0226/E0226_01_BZ.xma", 1 }, { "xenon/event/e0226/E0226_02_BZ.xma", 1 }, { "xenon/event/e0226/J0226_00A_SV.xma", 1 }, { "xenon/event/e0226/J0226_00B_SV.xma", 1 }, { "xenon/event/e0226/J0226_01_BZ.xma", 1 }, { "xenon/event/e0226/J0226_02_BZ.xma", 1 }, { "xenon/event/e0227/E0227_00_SV.xma", 1 }, { "xenon/event/e0227/E0227_01_SV.xma", 1 }, { "xenon/event/e0227/J0227_00_SV.xma", 1 }, { "xenon/event/e0227/J0227_01_SV.xma", 1 }, { "xenon/event/e0228/e0228.wmv", 1 }, { "xenon/event/e0301/E0301_00_EL.xma", 1 }, { "xenon/event/e0301/E0301_01_SN.xma", 1 }, { "xenon/event/e0301/E0301_02_EL.xma", 1 }, { "xenon/event/e0301/E0301_03_EL.xma", 1 }, { "xenon/event/e0301/E0301_04_MF.xma", 1 }, { "xenon/event/e0301/E0301_05_EL.xma", 1 }, { "xenon/event/e0301/E0301_06_SR.xma", 1 }, { "xenon/event/e0301/E0301_07_SR.xma", 1 }, { "xenon/event/e0301/E0301_08_EL.xma", 1 }, { "xenon/event/e0301/E0301_09_EL.xma", 1 }, { "xenon/event/e0301/E0301_10_MF.xma", 1 }, { "xenon/event/e0301/E0301_11_MF.xma", 1 }, { "xenon/event/e0301/E0301_12_MF.xma", 1 }, { "xenon/event/e0301/E0301_13_MF.xma", 1 }, { "xenon/event/e0301/E0301_14_MF.xma", 1 }, { "xenon/event/e0301/J0301_00_EL.xma", 1 }, { "xenon/event/e0301/J0301_01_SN.xma", 1 }, { "xenon/event/e0301/J0301_02_EL.xma", 1 }, { "xenon/event/e0301/J0301_03_EL.xma", 1 }, { "xenon/event/e0301/J0301_04_MF.xma", 1 }, { "xenon/event/e0301/J0301_05_EL.xma", 1 }, { "xenon/event/e0301/J0301_06_SR.xma", 1 }, { "xenon/event/e0301/J0301_07_SR.xma", 1 }, { "xenon/event/e0301/J0301_08_EL.xma", 1 }, { "xenon/event/e0301/J0301_09_EL.xma", 1 }, { "xenon/event/e0301/J0301_10_MF.xma", 1 }, { "xenon/event/e0301/J0301_11_MF.xma", 1 }, { "xenon/event/e0301/J0301_12_MF.xma", 1 }, { "xenon/event/e0301/J0301_13_MF.xma", 1 }, { "xenon/event/e0301/J0301_14_MF.xma", 1 }, { "xenon/event/e0303/e0303.wmv", 1 }, { "xenon/event/e0304/E0304_00_TL.xma", 1 }, { "xenon/event/e0304/E0304_01_AM.xma", 1 }, { "xenon/event/e0304/E0304_02_AM.xma", 1 }, { "xenon/event/e0304/E0304_03_KN.xma", 1 }, { "xenon/event/e0304/E0304_04_SV.xma", 1 }, { "xenon/event/e0304/E0304_05_AM.xma", 1 }, { "xenon/event/e0304/E0304_06_SV.xma", 1 }, { "xenon/event/e0304/E0304_07_SV.xma", 1 }, { "xenon/event/e0304/E0304_08_RG.xma", 1 }, { "xenon/event/e0304/E0304_09_EG.xma", 1 }, { "xenon/event/e0304/E0304_11_EG.xma", 1 }, { "xenon/event/e0304/E0304_12_EG.xma", 1 }, { "xenon/event/e0304/E0304_13_AM.xma", 1 }, { "xenon/event/e0304/E0304_14_TL.xma", 1 }, { "xenon/event/e0304/E0304_15_KN.xma", 1 }, { "xenon/event/e0304/E0304_16_EG.xma", 1 }, { "xenon/event/e0304/E0304_18_SV.xma", 1 }, { "xenon/event/e0304/E0304_19_SV.xma", 1 }, { "xenon/event/e0304/E0304_20_EG.xma", 1 }, { "xenon/event/e0304/E0304_22_SH.xma", 1 }, { "xenon/event/e0304/E0304_23_EG.xma", 1 }, { "xenon/event/e0304/E0304_24_KN.xma", 1 }, { "xenon/event/e0304/E0304_25_EG.xma", 1 }, { "xenon/event/e0304/E0304_26_EG.xma", 1 }, { "xenon/event/e0304/E0304_27_SV.xma", 1 }, { "xenon/event/e0304/E0304_28_SV.xma", 1 }, { "xenon/event/e0304/E0304_30A_SH.xma", 1 }, { "xenon/event/e0304/E0304_30B_SH.xma", 1 }, { "xenon/event/e0304/E0304_31_SH.xma", 1 }, { "xenon/event/e0304/E0304_32_AM.xma", 1 }, { "xenon/event/e0304/E0304_33_EL.xma", 1 }, { "xenon/event/e0304/E0304_34_SV.xma", 1 }, { "xenon/event/e0304/E0304_35_EL.xma", 1 }, { "xenon/event/e0304/E0304_36_EL.xma", 1 }, { "xenon/event/e0304/E0304_37_EL.xma", 1 }, { "xenon/event/e0304/E0304_38_SV.xma", 1 }, { "xenon/event/e0304/E0304_39_SV.xma", 1 }, { "xenon/event/e0304/E0304_40_SV.xma", 1 }, { "xenon/event/e0304/E0304_41_EL.xma", 1 }, { "xenon/event/e0304/E0304_42_SV.xma", 1 }, { "xenon/event/e0304/E0304_43_SV.xma", 1 }, { "xenon/event/e0304/E0304_44_EL.xma", 1 }, { "xenon/event/e0304/E0304_45_EG.xma", 1 }, { "xenon/event/e0304/E0304_46_TL.xma", 1 }, { "xenon/event/e0304/E0304_47_AM.xma", 1 }, { "xenon/event/e0304/E0304_48_AM.xma", 1 }, { "xenon/event/e0304/J0304_00_TL.xma", 1 }, { "xenon/event/e0304/J0304_01_AM.xma", 1 }, { "xenon/event/e0304/J0304_02_AM.xma", 1 }, { "xenon/event/e0304/J0304_03_KN.xma", 1 }, { "xenon/event/e0304/J0304_04_SV.xma", 1 }, { "xenon/event/e0304/J0304_05_AM.xma", 1 }, { "xenon/event/e0304/J0304_06_SV.xma", 1 }, { "xenon/event/e0304/J0304_07_SV.xma", 1 }, { "xenon/event/e0304/J0304_08_RG.xma", 1 }, { "xenon/event/e0304/J0304_09_EG.xma", 1 }, { "xenon/event/e0304/J0304_11_EG.xma", 1 }, { "xenon/event/e0304/J0304_12_EG.xma", 1 }, { "xenon/event/e0304/J0304_13_AM.xma", 1 }, { "xenon/event/e0304/J0304_14_TL.xma", 1 }, { "xenon/event/e0304/J0304_15_AM.xma", 1 }, { "xenon/event/e0304/J0304_15_KN.xma", 1 }, { "xenon/event/e0304/J0304_16_EG.xma", 1 }, { "xenon/event/e0304/J0304_17_EG.xma", 1 }, { "xenon/event/e0304/J0304_18_SV.xma", 1 }, { "xenon/event/e0304/J0304_19_SV.xma", 1 }, { "xenon/event/e0304/J0304_20_EG.xma", 1 }, { "xenon/event/e0304/J0304_22_SH.xma", 1 }, { "xenon/event/e0304/J0304_23_EG.xma", 1 }, { "xenon/event/e0304/J0304_24_KN.xma", 1 }, { "xenon/event/e0304/J0304_25_EG.xma", 1 }, { "xenon/event/e0304/J0304_26_EG.xma", 1 }, { "xenon/event/e0304/J0304_27_SV.xma", 1 }, { "xenon/event/e0304/J0304_28_SV.xma", 1 }, { "xenon/event/e0304/J0304_30A_SH.xma", 1 }, { "xenon/event/e0304/J0304_30B_SH.xma", 1 }, { "xenon/event/e0304/J0304_31_SH.xma", 1 }, { "xenon/event/e0304/J0304_32_AM.xma", 1 }, { "xenon/event/e0304/J0304_33_EL.xma", 1 }, { "xenon/event/e0304/J0304_34_SV.xma", 1 }, { "xenon/event/e0304/J0304_35_EL.xma", 1 }, { "xenon/event/e0304/J0304_36_EL.xma", 1 }, { "xenon/event/e0304/J0304_37_EL.xma", 1 }, { "xenon/event/e0304/J0304_38_SV.xma", 1 }, { "xenon/event/e0304/J0304_39_SV.xma", 1 }, { "xenon/event/e0304/J0304_40_SV.xma", 1 }, { "xenon/event/e0304/J0304_41_EL.xma", 1 }, { "xenon/event/e0304/J0304_42_SV.xma", 1 }, { "xenon/event/e0304/J0304_43_SV.xma", 1 }, { "xenon/event/e0304/J0304_44_EL.xma", 1 }, { "xenon/event/e0304/J0304_45_EG.xma", 1 }, { "xenon/event/e0304/J0304_46_TL.xma", 1 }, { "xenon/event/e0304/J0304_47_AM.xma", 1 }, { "xenon/event/e0304/J0304_48_AM.xma", 1 }, { "xenon/event/e0305/e0305.wmv", 1 }, { "xenon/event/e0306/e0306.wmv", 1 }, { "xenon/event/e0307/e0307.wmv", 1 }, { "xenon/event/e0308/e0308.wmv", 1 }, { "xenon/event/e0309/e0309.wmv", 1 }, { "xenon/event/eboss/e_bos04_e01_mf.xma", 1 }, { "xenon/event/eboss/e_bos04_e13_mf.xma", 1 }, { "xenon/event/eboss/e_bos05_e03_mf.xma", 1 }, { "xenon/event/eboss/e_bos05_e08_mf.xma", 1 }, { "xenon/event/eboss/e_bos06_e00_eg.xma", 1 }, { "xenon/event/eboss/e_bos06_e03_eg.xma", 1 }, { "xenon/event/eboss/e_bos07_e00_eg.xma", 1 }, { "xenon/event/eboss/e_bos08_e00_1_eg.xma", 1 }, { "xenon/event/eboss/e_bos08_e00_eg.xma", 1 }, { "xenon/event/eboss/e_bos08_e16_eg.xma", 1 }, { "xenon/event/eboss/e_bos09_e00_sn.xma", 1 }, { "xenon/event/eboss/e_bos09_e00_sv.xma", 1 }, { "xenon/event/eboss/e_bos10_e00_sd.xma", 1 }, { "xenon/event/eboss/e_bos10_e00_sv.xma", 1 }, { "xenon/event/eboss/e_bos11_e06_sd.xma", 1 }, { "xenon/event/eboss/e_bos11_e06_sn.xma", 1 }, { "xenon/event/eboss/e_bos11_e06_sv.xma", 1 }, { "xenon/event/eboss/j_bos04_e01_mf.xma", 1 }, { "xenon/event/eboss/j_bos04_e13_mf.xma", 1 }, { "xenon/event/eboss/j_bos05_e03_mf.xma", 1 }, { "xenon/event/eboss/j_bos05_e08_mf.xma", 1 }, { "xenon/event/eboss/j_bos06_e00_eg.xma", 1 }, { "xenon/event/eboss/j_bos06_e03_eg.xma", 1 }, { "xenon/event/eboss/j_bos07_e00_eg.xma", 1 }, { "xenon/event/eboss/j_bos08_e00_1_eg.xma", 1 }, { "xenon/event/eboss/j_bos08_e00_eg.xma", 1 }, { "xenon/event/eboss/j_bos08_e16_eg.xma", 1 }, { "xenon/event/eboss/j_bos09_e00_sn.xma", 1 }, { "xenon/event/eboss/j_bos09_e00_sv.xma", 1 }, { "xenon/event/eboss/j_bos10_e00_sd.xma", 1 }, { "xenon/event/eboss/j_bos10_e00_sv.xma", 1 }, { "xenon/event/eboss/j_bos11_e06_sd.xma", 1 }, { "xenon/event/eboss/j_bos11_e06_sn.xma", 1 }, { "xenon/event/eboss/j_bos11_e06_sv.xma", 1 }, { "xenon/sound/HD_SEGA.wmv", 1 }, { "xenon/sound/boss_cerberus.xma", 1 }, { "xenon/sound/boss_character.xma", 1 }, { "xenon/sound/boss_iblis.xma", 1 }, { "xenon/sound/boss_iblis3.xma", 1 }, { "xenon/sound/boss_mefires1.xma", 1 }, { "xenon/sound/boss_mefires3.xma", 1 }, { "xenon/sound/boss_solaris1.xma", 1 }, { "xenon/sound/boss_solaris2.xma", 1 }, { "xenon/sound/boss_wyvern.xma", 1 }, { "xenon/sound/end_elise.wmv", 1 }, { "xenon/sound/end_shadow.wmv", 1 }, { "xenon/sound/end_silver.wmv", 1 }, { "xenon/sound/end_sonic.wmv", 1 }, { "xenon/sound/event/e0001.xma", 1 }, { "xenon/sound/event/e0003.xma", 1 }, { "xenon/sound/event/e0006.xma", 1 }, { "xenon/sound/event/e0007.xma", 1 }, { "xenon/sound/event/e0009.xma", 1 }, { "xenon/sound/event/e0011.xma", 1 }, { "xenon/sound/event/e0012.xma", 1 }, { "xenon/sound/event/e0014.xma", 1 }, { "xenon/sound/event/e0016.xma", 1 }, { "xenon/sound/event/e0017.xma", 1 }, { "xenon/sound/event/e0018.xma", 1 }, { "xenon/sound/event/e0021.xma", 1 }, { "xenon/sound/event/e0022.xma", 1 }, { "xenon/sound/event/e0024.xma", 1 }, { "xenon/sound/event/e0026.xma", 1 }, { "xenon/sound/event/e0028.xma", 1 }, { "xenon/sound/event/e0029.xma", 1 }, { "xenon/sound/event/e0031.xma", 1 }, { "xenon/sound/event/e0103.xma", 1 }, { "xenon/sound/event/e0104.xma", 1 }, { "xenon/sound/event/e0106.xma", 1 }, { "xenon/sound/event/e0112.xma", 1 }, { "xenon/sound/event/e0114.xma", 1 }, { "xenon/sound/event/e0115.xma", 1 }, { "xenon/sound/event/e0122.xma", 1 }, { "xenon/sound/event/e0125.xma", 1 }, { "xenon/sound/event/e0126.xma", 1 }, { "xenon/sound/event/e0127.xma", 1 }, { "xenon/sound/event/e0129.xma", 1 }, { "xenon/sound/event/e0201.xma", 1 }, { "xenon/sound/event/e0202.xma", 1 }, { "xenon/sound/event/e0203.xma", 1 }, { "xenon/sound/event/e0208.xma", 1 }, { "xenon/sound/event/e0209.xma", 1 }, { "xenon/sound/event/e0210.xma", 1 }, { "xenon/sound/event/e0211.xma", 1 }, { "xenon/sound/event/e0214.xma", 1 }, { "xenon/sound/event/e0215.xma", 1 }, { "xenon/sound/event/e0221.xma", 1 }, { "xenon/sound/event/e0222.xma", 1 }, { "xenon/sound/event/e0226.xma", 1 }, { "xenon/sound/event/e0227.xma", 1 }, { "xenon/sound/event/e0300.xma", 1 }, { "xenon/sound/event/e0301.xma", 1 }, { "xenon/sound/event/e0304.xma", 1 }, { "xenon/sound/event/e0304_0.xma", 1 }, { "xenon/sound/event/e0304_11940.xma", 1 }, { "xenon/sound/event/e0304_3195.xma", 1 }, { "xenon/sound/event/e0304_9120.xma", 1 }, { "xenon/sound/extra.xma", 1 }, { "xenon/sound/menu.xma", 1 }, { "xenon/sound/result.xma", 1 }, { "xenon/sound/roundclear.xma", 1 }, { "xenon/sound/select.xma", 1 }, { "xenon/sound/shadow_theme.xma", 1 }, { "xenon/sound/silver_theme.xma", 1 }, { "xenon/sound/speed_up.xma", 1 }, { "xenon/sound/stg_aqa_a.xma", 1 }, { "xenon/sound/stg_aqa_b.xma", 1 }, { "xenon/sound/stg_csc_a.xma", 1 }, { "xenon/sound/stg_csc_b.xma", 1 }, { "xenon/sound/stg_csc_e.xma", 1 }, { "xenon/sound/stg_csc_f.xma", 1 }, { "xenon/sound/stg_dtd_a.xma", 1 }, { "xenon/sound/stg_dtd_b.xma", 1 }, { "xenon/sound/stg_end_a.xma", 1 }, { "xenon/sound/stg_end_b.xma", 1 }, { "xenon/sound/stg_end_c.xma", 1 }, { "xenon/sound/stg_end_d.xma", 1 }, { "xenon/sound/stg_end_e.xma", 1 }, { "xenon/sound/stg_end_f.xma", 1 }, { "xenon/sound/stg_end_g.xma", 1 }, { "xenon/sound/stg_flc_a.xma", 1 }, { "xenon/sound/stg_flc_b.xma", 1 }, { "xenon/sound/stg_kdv_a.xma", 1 }, { "xenon/sound/stg_kdv_b.xma", 1 }, { "xenon/sound/stg_kdv_c.xma", 1 }, { "xenon/sound/stg_kdv_d.xma", 1 }, { "xenon/sound/stg_rct_a.xma", 1 }, { "xenon/sound/stg_rct_b.xma", 1 }, { "xenon/sound/stg_tpj_a.xma", 1 }, { "xenon/sound/stg_tpj_b.xma", 1 }, { "xenon/sound/stg_tpj_c.xma", 1 }, { "xenon/sound/stg_twn_a.xma", 1 }, { "xenon/sound/stg_twn_b.xma", 1 }, { "xenon/sound/stg_twn_c.xma", 1 }, { "xenon/sound/stg_twn_shop.xma", 1 }, { "xenon/sound/stg_wap_a.xma", 1 }, { "xenon/sound/stg_wap_b.xma", 1 }, { "xenon/sound/stg_wvo_a.xma", 1 }, { "xenon/sound/stg_wvo_b.xma", 1 }, { "xenon/sound/sweetdream.xma", 1 }, { "xenon/sound/sweetsweetsweet.xma", 1 }, { "xenon/sound/theme_elise.xma", 1 }, { "xenon/sound/theme_shadow.xma", 1 }, { "xenon/sound/theme_silver.xma", 1 }, { "xenon/sound/theme_sonic.xma", 1 }, { "xenon/sound/title_loop.xma", 1 }, { "xenon/sound/title_loop_GBn.wmv", 1 }, { "xenon/sound/twn_accordion.xma", 1 }, { "xenon/sound/twn_clear.xma", 1 }, { "xenon/sound/twn_mission_comical.xma", 1 }, { "xenon/sound/twn_mission_fast.xma", 1 }, { "xenon/sound/twn_mission_slow.xma", 1 }, { "xenon/sound/voice/e/all01_a00_am.xma", 1 }, { "xenon/sound/voice/e/all01_a00_bz.xma", 1 }, { "xenon/sound/voice/e/all01_a00_kn.xma", 1 }, { "xenon/sound/voice/e/all01_a00_om.xma", 1 }, { "xenon/sound/voice/e/all01_a00_pr.xma", 1 }, { "xenon/sound/voice/e/all01_a00_rg.xma", 1 }, { "xenon/sound/voice/e/all01_a00_sd.xma", 1 }, { "xenon/sound/voice/e/all01_a00_sn.xma", 1 }, { "xenon/sound/voice/e/all01_a00_sv.xma", 1 }, { "xenon/sound/voice/e/all01_a00_tl.xma", 1 }, { "xenon/sound/voice/e/all01_a01_am.xma", 1 }, { "xenon/sound/voice/e/all01_a01_bz.xma", 1 }, { "xenon/sound/voice/e/all01_a01_kn.xma", 1 }, { "xenon/sound/voice/e/all01_a01_om.xma", 1 }, { "xenon/sound/voice/e/all01_a01_pr.xma", 1 }, { "xenon/sound/voice/e/all01_a01_rg.xma", 1 }, { "xenon/sound/voice/e/all01_a01_sd.xma", 1 }, { "xenon/sound/voice/e/all01_a01_sn.xma", 1 }, { "xenon/sound/voice/e/all01_a01_sv.xma", 1 }, { "xenon/sound/voice/e/all01_a01_tl.xma", 1 }, { "xenon/sound/voice/e/all01_a02_am.xma", 1 }, { "xenon/sound/voice/e/all01_a02_bz.xma", 1 }, { "xenon/sound/voice/e/all01_a02_kn.xma", 1 }, { "xenon/sound/voice/e/all01_a02_om.xma", 1 }, { "xenon/sound/voice/e/all01_a02_pr.xma", 1 }, { "xenon/sound/voice/e/all01_a02_rg.xma", 1 }, { "xenon/sound/voice/e/all01_a02_sd.xma", 1 }, { "xenon/sound/voice/e/all01_a02_sn.xma", 1 }, { "xenon/sound/voice/e/all01_a02_sv.xma", 1 }, { "xenon/sound/voice/e/all01_a02_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e00_am.xma", 1 }, { "xenon/sound/voice/e/all01_e00_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e00_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e00_om.xma", 1 }, { "xenon/sound/voice/e/all01_e00_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e00_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e00_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e00_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e01_am.xma", 1 }, { "xenon/sound/voice/e/all01_e01_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e01_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e01_om.xma", 1 }, { "xenon/sound/voice/e/all01_e01_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e01_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e01_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e01_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e01_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e02_am.xma", 1 }, { "xenon/sound/voice/e/all01_e02_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e02_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e02_om.xma", 1 }, { "xenon/sound/voice/e/all01_e02_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e02_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e02_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e03_am.xma", 1 }, { "xenon/sound/voice/e/all01_e03_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e03_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e03_om.xma", 1 }, { "xenon/sound/voice/e/all01_e03_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e03_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e03_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e03_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e04_am.xma", 1 }, { "xenon/sound/voice/e/all01_e04_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e04_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e04_om.xma", 1 }, { "xenon/sound/voice/e/all01_e04_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e04_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e04_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e04_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e04_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e05_am.xma", 1 }, { "xenon/sound/voice/e/all01_e05_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e05_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e05_om.xma", 1 }, { "xenon/sound/voice/e/all01_e05_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e05_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e05_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e05_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e05_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e05_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e06_am.xma", 1 }, { "xenon/sound/voice/e/all01_e06_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e06_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e06_om.xma", 1 }, { "xenon/sound/voice/e/all01_e06_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e06_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e06_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e06_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e06_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e06_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e07_am.xma", 1 }, { "xenon/sound/voice/e/all01_e07_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e07_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e07_om.xma", 1 }, { "xenon/sound/voice/e/all01_e07_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e07_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e07_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e07_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e07_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e07_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e08_am.xma", 1 }, { "xenon/sound/voice/e/all01_e08_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e08_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e08_om.xma", 1 }, { "xenon/sound/voice/e/all01_e08_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e08_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e08_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e08_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e08_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e08_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e09_am.xma", 1 }, { "xenon/sound/voice/e/all01_e09_bz.xma", 1 }, { "xenon/sound/voice/e/all01_e09_kn.xma", 1 }, { "xenon/sound/voice/e/all01_e09_om.xma", 1 }, { "xenon/sound/voice/e/all01_e09_pr.xma", 1 }, { "xenon/sound/voice/e/all01_e09_rg.xma", 1 }, { "xenon/sound/voice/e/all01_e09_sd.xma", 1 }, { "xenon/sound/voice/e/all01_e09_sn.xma", 1 }, { "xenon/sound/voice/e/all01_e09_sv.xma", 1 }, { "xenon/sound/voice/e/all01_e09_tl.xma", 1 }, { "xenon/sound/voice/e/all01_e10_sn.xma", 1 }, { "xenon/sound/voice/e/all01_v12_am.xma", 1 }, { "xenon/sound/voice/e/all01_v12_bz.xma", 1 }, { "xenon/sound/voice/e/all01_v12_kn.xma", 1 }, { "xenon/sound/voice/e/all01_v12_om.xma", 1 }, { "xenon/sound/voice/e/all01_v12_pr.xma", 1 }, { "xenon/sound/voice/e/all01_v12_rg.xma", 1 }, { "xenon/sound/voice/e/all01_v12_sd.xma", 1 }, { "xenon/sound/voice/e/all01_v12_sn.xma", 1 }, { "xenon/sound/voice/e/all01_v12_sv.xma", 1 }, { "xenon/sound/voice/e/all01_v12_tl.xma", 1 }, { "xenon/sound/voice/e/all01_v13_am.xma", 1 }, { "xenon/sound/voice/e/all01_v13_bz.xma", 1 }, { "xenon/sound/voice/e/all01_v13_kn.xma", 1 }, { "xenon/sound/voice/e/all01_v13_om.xma", 1 }, { "xenon/sound/voice/e/all01_v13_pr.xma", 1 }, { "xenon/sound/voice/e/all01_v13_rg.xma", 1 }, { "xenon/sound/voice/e/all01_v13_sd.xma", 1 }, { "xenon/sound/voice/e/all01_v13_sn.xma", 1 }, { "xenon/sound/voice/e/all01_v13_sv.xma", 1 }, { "xenon/sound/voice/e/all01_v13_tl.xma", 1 }, { "xenon/sound/voice/e/all01_v14_am.xma", 1 }, { "xenon/sound/voice/e/all01_v14_bz.xma", 1 }, { "xenon/sound/voice/e/all01_v14_kn.xma", 1 }, { "xenon/sound/voice/e/all01_v14_om.xma", 1 }, { "xenon/sound/voice/e/all01_v14_pr.xma", 1 }, { "xenon/sound/voice/e/all01_v14_rg.xma", 1 }, { "xenon/sound/voice/e/all01_v14_sd.xma", 1 }, { "xenon/sound/voice/e/all01_v14_sn.xma", 1 }, { "xenon/sound/voice/e/all01_v14_sv.xma", 1 }, { "xenon/sound/voice/e/all01_v14_tl.xma", 1 }, { "xenon/sound/voice/e/all01_v15_am.xma", 1 }, { "xenon/sound/voice/e/all01_v15_bz.xma", 1 }, { "xenon/sound/voice/e/all01_v15_kn.xma", 1 }, { "xenon/sound/voice/e/all01_v15_om.xma", 1 }, { "xenon/sound/voice/e/all01_v15_pr.xma", 1 }, { "xenon/sound/voice/e/all01_v15_rg.xma", 1 }, { "xenon/sound/voice/e/all01_v15_sd.xma", 1 }, { "xenon/sound/voice/e/all01_v15_sn.xma", 1 }, { "xenon/sound/voice/e/all01_v15_sv.xma", 1 }, { "xenon/sound/voice/e/all01_v15_tl.xma", 1 }, { "xenon/sound/voice/e/all01_v16_am.xma", 1 }, { "xenon/sound/voice/e/all01_v16_bz.xma", 1 }, { "xenon/sound/voice/e/all01_v16_kn.xma", 1 }, { "xenon/sound/voice/e/all01_v16_om.xma", 1 }, { "xenon/sound/voice/e/all01_v16_pr.xma", 1 }, { "xenon/sound/voice/e/all01_v16_rg.xma", 1 }, { "xenon/sound/voice/e/all01_v16_sd.xma", 1 }, { "xenon/sound/voice/e/all01_v16_sn.xma", 1 }, { "xenon/sound/voice/e/all01_v16_sv.xma", 1 }, { "xenon/sound/voice/e/all01_v16_tl.xma", 1 }, { "xenon/sound/voice/e/all01_w00_am.xma", 1 }, { "xenon/sound/voice/e/all01_w00_bz.xma", 1 }, { "xenon/sound/voice/e/all01_w00_kn.xma", 1 }, { "xenon/sound/voice/e/all01_w00_om.xma", 1 }, { "xenon/sound/voice/e/all01_w00_pr.xma", 1 }, { "xenon/sound/voice/e/all01_w00_rg.xma", 1 }, { "xenon/sound/voice/e/all01_w00_sd.xma", 1 }, { "xenon/sound/voice/e/all01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/all01_w00_sv.xma", 1 }, { "xenon/sound/voice/e/all01_w00_tl.xma", 1 }, { "xenon/sound/voice/e/all01_w01_am.xma", 1 }, { "xenon/sound/voice/e/all01_w01_bz.xma", 1 }, { "xenon/sound/voice/e/all01_w01_kn.xma", 1 }, { "xenon/sound/voice/e/all01_w01_om.xma", 1 }, { "xenon/sound/voice/e/all01_w01_pr.xma", 1 }, { "xenon/sound/voice/e/all01_w01_rg.xma", 1 }, { "xenon/sound/voice/e/all01_w01_sd.xma", 1 }, { "xenon/sound/voice/e/all01_w01_sn.xma", 1 }, { "xenon/sound/voice/e/all01_w01_sv.xma", 1 }, { "xenon/sound/voice/e/all01_w01_tl.xma", 1 }, { "xenon/sound/voice/e/all02_a00_am.xma", 1 }, { "xenon/sound/voice/e/all02_a00_bz.xma", 1 }, { "xenon/sound/voice/e/all02_a00_kn.xma", 1 }, { "xenon/sound/voice/e/all02_a00_om.xma", 1 }, { "xenon/sound/voice/e/all02_a00_rg.xma", 1 }, { "xenon/sound/voice/e/all02_a00_sd.xma", 1 }, { "xenon/sound/voice/e/all02_a00_sn.xma", 1 }, { "xenon/sound/voice/e/all02_a00_sv.xma", 1 }, { "xenon/sound/voice/e/all02_a00_tl.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_am.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_bz.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_kn.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_om.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_rg.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_sd.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_sn.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_sv.xma", 1 }, { "xenon/sound/voice/e/all02_a00ps_tl.xma", 1 }, { "xenon/sound/voice/e/all03_a00_am.xma", 1 }, { "xenon/sound/voice/e/all03_a00_bz.xma", 1 }, { "xenon/sound/voice/e/all03_a00_kn.xma", 1 }, { "xenon/sound/voice/e/all03_a00_om.xma", 1 }, { "xenon/sound/voice/e/all03_a00_pr.xma", 1 }, { "xenon/sound/voice/e/all03_a00_rg.xma", 1 }, { "xenon/sound/voice/e/all03_a00_sn.xma", 1 }, { "xenon/sound/voice/e/all03_a00_sv.xma", 1 }, { "xenon/sound/voice/e/all03_a00_tl.xma", 1 }, { "xenon/sound/voice/e/all03_a01_bz.xma", 1 }, { "xenon/sound/voice/e/all03_a01_sd.xma", 1 }, { "xenon/sound/voice/e/all03_a01_sn.xma", 1 }, { "xenon/sound/voice/e/all03_a01ps_bz.xma", 1 }, { "xenon/sound/voice/e/all03_a01ps_sd.xma", 1 }, { "xenon/sound/voice/e/all03_a01ps_sn.xma", 1 }, { "xenon/sound/voice/e/all03_h00_so.xma", 1 }, { "xenon/sound/voice/e/all03_h00ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h01_so.xma", 1 }, { "xenon/sound/voice/e/all03_h01ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h02_so.xma", 1 }, { "xenon/sound/voice/e/all03_h02ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h03_so.xma", 1 }, { "xenon/sound/voice/e/all03_h03ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h04_so.xma", 1 }, { "xenon/sound/voice/e/all03_h04ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h05_so.xma", 1 }, { "xenon/sound/voice/e/all03_h05ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h06_so.xma", 1 }, { "xenon/sound/voice/e/all03_h06ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h07_so.xma", 1 }, { "xenon/sound/voice/e/all03_h07ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h08_so.xma", 1 }, { "xenon/sound/voice/e/all03_h08ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h09_so.xma", 1 }, { "xenon/sound/voice/e/all03_h09ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h10_so.xma", 1 }, { "xenon/sound/voice/e/all03_h10ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h11_so.xma", 1 }, { "xenon/sound/voice/e/all03_h11ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h12_so.xma", 1 }, { "xenon/sound/voice/e/all03_h12ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h13_so.xma", 1 }, { "xenon/sound/voice/e/all03_h13ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h14_so.xma", 1 }, { "xenon/sound/voice/e/all03_h14ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h15_so.xma", 1 }, { "xenon/sound/voice/e/all03_h15ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h16_so.xma", 1 }, { "xenon/sound/voice/e/all03_h16ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h17_so.xma", 1 }, { "xenon/sound/voice/e/all03_h17ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h18_so.xma", 1 }, { "xenon/sound/voice/e/all03_h18ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h19_so.xma", 1 }, { "xenon/sound/voice/e/all03_h19ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h20_so.xma", 1 }, { "xenon/sound/voice/e/all03_h20ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h21_so.xma", 1 }, { "xenon/sound/voice/e/all03_h21ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h22_so.xma", 1 }, { "xenon/sound/voice/e/all03_h22ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h23_so.xma", 1 }, { "xenon/sound/voice/e/all03_h23ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h24_so.xma", 1 }, { "xenon/sound/voice/e/all03_h24ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h25_so.xma", 1 }, { "xenon/sound/voice/e/all03_h25ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h26_so.xma", 1 }, { "xenon/sound/voice/e/all03_h26ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h27_so.xma", 1 }, { "xenon/sound/voice/e/all03_h27ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h28_so.xma", 1 }, { "xenon/sound/voice/e/all03_h28ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h29_so.xma", 1 }, { "xenon/sound/voice/e/all03_h29ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h30_so.xma", 1 }, { "xenon/sound/voice/e/all03_h30ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h31_so.xma", 1 }, { "xenon/sound/voice/e/all03_h31ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h32_so.xma", 1 }, { "xenon/sound/voice/e/all03_h32ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h33_so.xma", 1 }, { "xenon/sound/voice/e/all03_h33ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h34_so.xma", 1 }, { "xenon/sound/voice/e/all03_h34ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h35_so.xma", 1 }, { "xenon/sound/voice/e/all03_h35ps_so.xma", 1 }, { "xenon/sound/voice/e/all03_h36_so.xma", 1 }, { "xenon/sound/voice/e/all03_h37_so.xma", 1 }, { "xenon/sound/voice/e/all03_h38_so.xma", 1 }, { "xenon/sound/voice/e/all04_a00_am.xma", 1 }, { "xenon/sound/voice/e/all04_a00_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a00_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a00_om.xma", 1 }, { "xenon/sound/voice/e/all04_a00_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a00_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a00_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a00_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a00_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a01_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a01_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a02_am.xma", 1 }, { "xenon/sound/voice/e/all04_a02_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a02_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a02_om.xma", 1 }, { "xenon/sound/voice/e/all04_a02_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a02_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a02_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a02_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a02_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a03_am.xma", 1 }, { "xenon/sound/voice/e/all04_a03_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a03_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a03_om.xma", 1 }, { "xenon/sound/voice/e/all04_a03_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a03_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a03_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a03_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a03_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a04_am.xma", 1 }, { "xenon/sound/voice/e/all04_a04_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a04_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a04_om.xma", 1 }, { "xenon/sound/voice/e/all04_a04_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a04_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a04_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a04_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a04_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a05_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a05_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a05_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a06_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a06_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a06_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a07_am.xma", 1 }, { "xenon/sound/voice/e/all04_a07_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a07_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a07_om.xma", 1 }, { "xenon/sound/voice/e/all04_a07_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a07_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a07_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a07_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a07_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a08_am.xma", 1 }, { "xenon/sound/voice/e/all04_a08_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a08_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a08_om.xma", 1 }, { "xenon/sound/voice/e/all04_a08_pr.xma", 1 }, { "xenon/sound/voice/e/all04_a08_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a08_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a08_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a08_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a08_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a09_am.xma", 1 }, { "xenon/sound/voice/e/all04_a09_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a09_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a09_om.xma", 1 }, { "xenon/sound/voice/e/all04_a09_pr.xma", 1 }, { "xenon/sound/voice/e/all04_a09_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a09_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a09_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a09_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a09_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a10_am.xma", 1 }, { "xenon/sound/voice/e/all04_a10_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a10_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a10_om.xma", 1 }, { "xenon/sound/voice/e/all04_a10_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a10_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a10_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a10_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a10_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a11_am.xma", 1 }, { "xenon/sound/voice/e/all04_a11_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a11_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a11_om.xma", 1 }, { "xenon/sound/voice/e/all04_a11_pr.xma", 1 }, { "xenon/sound/voice/e/all04_a11_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a11_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a11_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a11_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a11_tl.xma", 1 }, { "xenon/sound/voice/e/all04_a12_am.xma", 1 }, { "xenon/sound/voice/e/all04_a12_bz.xma", 1 }, { "xenon/sound/voice/e/all04_a12_kn.xma", 1 }, { "xenon/sound/voice/e/all04_a12_om.xma", 1 }, { "xenon/sound/voice/e/all04_a12_pr.xma", 1 }, { "xenon/sound/voice/e/all04_a12_rg.xma", 1 }, { "xenon/sound/voice/e/all04_a12_sd.xma", 1 }, { "xenon/sound/voice/e/all04_a12_sn.xma", 1 }, { "xenon/sound/voice/e/all04_a12_sv.xma", 1 }, { "xenon/sound/voice/e/all04_a12_tl.xma", 1 }, { "xenon/sound/voice/e/all04_h00_so.xma", 1 }, { "xenon/sound/voice/e/all04_h01_so.xma", 1 }, { "xenon/sound/voice/e/all05_a00_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a00_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a00_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a01_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a01_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a01_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a02_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a02_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a03_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a04_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a04_om.xma", 1 }, { "xenon/sound/voice/e/all05_a04_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a04_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a04_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a04_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a05_am.xma", 1 }, { "xenon/sound/voice/e/all05_a05_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a05_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a06_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a06_om.xma", 1 }, { "xenon/sound/voice/e/all05_a06_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a06_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a06_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a06_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a07_am.xma", 1 }, { "xenon/sound/voice/e/all05_a07_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a07_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a08_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a08_om.xma", 1 }, { "xenon/sound/voice/e/all05_a08_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a08_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a08_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a08_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a09_am.xma", 1 }, { "xenon/sound/voice/e/all05_a09_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a09_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a10_am.xma", 1 }, { "xenon/sound/voice/e/all05_a10_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a10_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a10_om.xma", 1 }, { "xenon/sound/voice/e/all05_a10_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a10_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a10_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a10_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a10_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a11_am.xma", 1 }, { "xenon/sound/voice/e/all05_a11_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a11_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a11_om.xma", 1 }, { "xenon/sound/voice/e/all05_a11_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a11_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a11_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a11_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a11_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a12_am.xma", 1 }, { "xenon/sound/voice/e/all05_a12_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a12_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a12_om.xma", 1 }, { "xenon/sound/voice/e/all05_a12_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a12_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a12_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a12_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a12_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a13_am.xma", 1 }, { "xenon/sound/voice/e/all05_a13_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a13_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a13_om.xma", 1 }, { "xenon/sound/voice/e/all05_a13_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a13_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a13_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a13_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a13_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a14_am.xma", 1 }, { "xenon/sound/voice/e/all05_a14_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a14_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a14_om.xma", 1 }, { "xenon/sound/voice/e/all05_a14_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a14_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a14_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a14_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a14_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a15_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a15_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a15_om.xma", 1 }, { "xenon/sound/voice/e/all05_a15_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a15_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a15_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a16_am.xma", 1 }, { "xenon/sound/voice/e/all05_a16_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a16_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a17_am.xma", 1 }, { "xenon/sound/voice/e/all05_a17_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a17_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a17_om.xma", 1 }, { "xenon/sound/voice/e/all05_a17_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a17_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a17_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a17_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a17_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a18_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a18_om.xma", 1 }, { "xenon/sound/voice/e/all05_a18_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a18_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a18_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a18_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a19_am.xma", 1 }, { "xenon/sound/voice/e/all05_a19_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a19_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a20_am.xma", 1 }, { "xenon/sound/voice/e/all05_a20_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a20_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a20_om.xma", 1 }, { "xenon/sound/voice/e/all05_a20_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a20_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a20_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a20_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a20_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a21_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a21_om.xma", 1 }, { "xenon/sound/voice/e/all05_a21_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a21_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a21_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a21_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a22_am.xma", 1 }, { "xenon/sound/voice/e/all05_a22_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a22_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a23_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a23_om.xma", 1 }, { "xenon/sound/voice/e/all05_a23_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a23_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a23_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a23_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a24_am.xma", 1 }, { "xenon/sound/voice/e/all05_a24_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a24_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a25_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a25_om.xma", 1 }, { "xenon/sound/voice/e/all05_a25_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a25_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a25_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a25_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a26_am.xma", 1 }, { "xenon/sound/voice/e/all05_a26_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a26_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a27_am.xma", 1 }, { "xenon/sound/voice/e/all05_a27_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a27_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a27_om.xma", 1 }, { "xenon/sound/voice/e/all05_a27_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a27_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a27_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a27_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a27_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a28_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a28_om.xma", 1 }, { "xenon/sound/voice/e/all05_a28_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a28_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a28_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a28_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a29_am.xma", 1 }, { "xenon/sound/voice/e/all05_a29_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a29_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a30_am.xma", 1 }, { "xenon/sound/voice/e/all05_a30_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a30_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a30_om.xma", 1 }, { "xenon/sound/voice/e/all05_a30_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a30_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a30_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a30_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a30_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a31_am.xma", 1 }, { "xenon/sound/voice/e/all05_a31_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a31_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a31_om.xma", 1 }, { "xenon/sound/voice/e/all05_a31_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a31_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a31_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a31_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a31_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a32_am.xma", 1 }, { "xenon/sound/voice/e/all05_a32_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a32_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a32_om.xma", 1 }, { "xenon/sound/voice/e/all05_a32_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a32_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a32_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a32_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a32_tl.xma", 1 }, { "xenon/sound/voice/e/all05_a33_am.xma", 1 }, { "xenon/sound/voice/e/all05_a33_bz.xma", 1 }, { "xenon/sound/voice/e/all05_a33_kn.xma", 1 }, { "xenon/sound/voice/e/all05_a33_om.xma", 1 }, { "xenon/sound/voice/e/all05_a33_pr.xma", 1 }, { "xenon/sound/voice/e/all05_a33_rg.xma", 1 }, { "xenon/sound/voice/e/all05_a33_sd.xma", 1 }, { "xenon/sound/voice/e/all05_a33_sn.xma", 1 }, { "xenon/sound/voice/e/all05_a33_sv.xma", 1 }, { "xenon/sound/voice/e/all05_a33_tl.xma", 1 }, { "xenon/sound/voice/e/all05_e00_am.xma", 1 }, { "xenon/sound/voice/e/all05_e00_bz.xma", 1 }, { "xenon/sound/voice/e/all05_e00_kn.xma", 1 }, { "xenon/sound/voice/e/all05_e00_om.xma", 1 }, { "xenon/sound/voice/e/all05_e00_rg.xma", 1 }, { "xenon/sound/voice/e/all05_e00_sd.xma", 1 }, { "xenon/sound/voice/e/all05_e00_sn.xma", 1 }, { "xenon/sound/voice/e/all05_e00_sv.xma", 1 }, { "xenon/sound/voice/e/all05_e00_tl.xma", 1 }, { "xenon/sound/voice/e/all05_e01_am.xma", 1 }, { "xenon/sound/voice/e/all05_e01_bz.xma", 1 }, { "xenon/sound/voice/e/all05_e01_kn.xma", 1 }, { "xenon/sound/voice/e/all05_e01_om.xma", 1 }, { "xenon/sound/voice/e/all05_e01_rg.xma", 1 }, { "xenon/sound/voice/e/all05_e01_sd.xma", 1 }, { "xenon/sound/voice/e/all05_e01_sn.xma", 1 }, { "xenon/sound/voice/e/all05_e01_sv.xma", 1 }, { "xenon/sound/voice/e/all05_e01_tl.xma", 1 }, { "xenon/sound/voice/e/all05_e02_am.xma", 1 }, { "xenon/sound/voice/e/all05_e02_bz.xma", 1 }, { "xenon/sound/voice/e/all05_e02_kn.xma", 1 }, { "xenon/sound/voice/e/all05_e02_om.xma", 1 }, { "xenon/sound/voice/e/all05_e02_rg.xma", 1 }, { "xenon/sound/voice/e/all05_e02_sd.xma", 1 }, { "xenon/sound/voice/e/all05_e02_sn.xma", 1 }, { "xenon/sound/voice/e/all05_e02_sv.xma", 1 }, { "xenon/sound/voice/e/all05_e02_tl.xma", 1 }, { "xenon/sound/voice/e/all05_e03_am.xma", 1 }, { "xenon/sound/voice/e/all05_e03_bz.xma", 1 }, { "xenon/sound/voice/e/all05_e03_kn.xma", 1 }, { "xenon/sound/voice/e/all05_e03_om.xma", 1 }, { "xenon/sound/voice/e/all05_e03_rg.xma", 1 }, { "xenon/sound/voice/e/all05_e03_sd.xma", 1 }, { "xenon/sound/voice/e/all05_e03_sn.xma", 1 }, { "xenon/sound/voice/e/all05_e03_sv.xma", 1 }, { "xenon/sound/voice/e/all05_e03_tl.xma", 1 }, { "xenon/sound/voice/e/all06_a00_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a00_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a01_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a01_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a02_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a02_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a03_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a03_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a03ps_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a03ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a04_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a04_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a04ps_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a04ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a05_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a05_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a05ps_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a05ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a06_rg.xma", 1 }, { "xenon/sound/voice/e/all06_a06_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a07_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a07ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a08_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a09_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a09ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a11_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a11ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a12_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a12ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a13_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a14_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a14ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a15_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a15ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a16_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a16ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a17_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a18_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a19_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a19ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a20_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a20ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a21_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a21ps_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a22_sd.xma", 1 }, { "xenon/sound/voice/e/all06_a22ps_sd.xma", 1 }, { "xenon/sound/voice/e/all07_a00_am.xma", 1 }, { "xenon/sound/voice/e/all07_a00_bz.xma", 1 }, { "xenon/sound/voice/e/all07_a00_kn.xma", 1 }, { "xenon/sound/voice/e/all07_a00_om.xma", 1 }, { "xenon/sound/voice/e/all07_a00_rg.xma", 1 }, { "xenon/sound/voice/e/all07_a00_sd.xma", 1 }, { "xenon/sound/voice/e/all07_a00_sn.xma", 1 }, { "xenon/sound/voice/e/all07_a00_sv.xma", 1 }, { "xenon/sound/voice/e/all07_a00_tl.xma", 1 }, { "xenon/sound/voice/e/all07_a01_am.xma", 1 }, { "xenon/sound/voice/e/all07_a01_bz.xma", 1 }, { "xenon/sound/voice/e/all07_a01_kn.xma", 1 }, { "xenon/sound/voice/e/all07_a01_om.xma", 1 }, { "xenon/sound/voice/e/all07_a01_rg.xma", 1 }, { "xenon/sound/voice/e/all07_a01_sd.xma", 1 }, { "xenon/sound/voice/e/all07_a01_sn.xma", 1 }, { "xenon/sound/voice/e/all07_a01_sv.xma", 1 }, { "xenon/sound/voice/e/all07_a01_tl.xma", 1 }, { "xenon/sound/voice/e/all07_a02_am.xma", 1 }, { "xenon/sound/voice/e/all07_a02_bz.xma", 1 }, { "xenon/sound/voice/e/all07_a02_kn.xma", 1 }, { "xenon/sound/voice/e/all07_a02_om.xma", 1 }, { "xenon/sound/voice/e/all07_a02_rg.xma", 1 }, { "xenon/sound/voice/e/all07_a02_sd.xma", 1 }, { "xenon/sound/voice/e/all07_a02_sn.xma", 1 }, { "xenon/sound/voice/e/all07_a02_sv.xma", 1 }, { "xenon/sound/voice/e/all07_a02_tl.xma", 1 }, { "xenon/sound/voice/e/all07_a03_am.xma", 1 }, { "xenon/sound/voice/e/all07_a03_bz.xma", 1 }, { "xenon/sound/voice/e/all07_a03_kn.xma", 1 }, { "xenon/sound/voice/e/all07_a03_om.xma", 1 }, { "xenon/sound/voice/e/all07_a03_rg.xma", 1 }, { "xenon/sound/voice/e/all07_a03_sd.xma", 1 }, { "xenon/sound/voice/e/all07_a03_sn.xma", 1 }, { "xenon/sound/voice/e/all07_a03_sv.xma", 1 }, { "xenon/sound/voice/e/all07_a03_tl.xma", 1 }, { "xenon/sound/voice/e/all07_a04_am.xma", 1 }, { "xenon/sound/voice/e/all07_a04_bz.xma", 1 }, { "xenon/sound/voice/e/all07_a04_kn.xma", 1 }, { "xenon/sound/voice/e/all07_a04_rg.xma", 1 }, { "xenon/sound/voice/e/all07_a04_sd.xma", 1 }, { "xenon/sound/voice/e/all07_a04_sn.xma", 1 }, { "xenon/sound/voice/e/all07_a04_sv.xma", 1 }, { "xenon/sound/voice/e/all07_a04_tl.xma", 1 }, { "xenon/sound/voice/e/all08_e00_1_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e00_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e00_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e01_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e02_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e03_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e04_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e05_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e06_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e07_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e07_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e08_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e09_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e09_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e10_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e10_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e11_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e11_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e12_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e12_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e13_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e13_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e14_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e14_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e15_sd.xma", 1 }, { "xenon/sound/voice/e/all08_e15_sn.xma", 1 }, { "xenon/sound/voice/e/all08_e16_eg.xma", 1 }, { "xenon/sound/voice/e/all08_e17_eg.xma", 1 }, { "xenon/sound/voice/e/all08_e18_eg.xma", 1 }, { "xenon/sound/voice/e/all08_e19_eg.xma", 1 }, { "xenon/sound/voice/e/all08_e20_eg.xma", 1 }, { "xenon/sound/voice/e/all08_e21_eg.xma", 1 }, { "xenon/sound/voice/e/all08_h00_so.xma", 1 }, { "xenon/sound/voice/e/all08_h01_so.xma", 1 }, { "xenon/sound/voice/e/all08_h02_so.xma", 1 }, { "xenon/sound/voice/e/aqa00_e00_1_so.xma", 1 }, { "xenon/sound/voice/e/aqa00_e00_so.xma", 1 }, { "xenon/sound/voice/e/aqa00_e01_so.xma", 1 }, { "xenon/sound/voice/e/aqa01_a00_sn.xma", 1 }, { "xenon/sound/voice/e/aqa01_a00_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_a01_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_a02_kn.xma", 1 }, { "xenon/sound/voice/e/aqa01_a02_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_a03_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_a04_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_a05_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_a06_kn.xma", 1 }, { "xenon/sound/voice/e/aqa01_a06_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_a07_sn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e00_kn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/aqa01_e00_sv.xma", 1 }, { "xenon/sound/voice/e/aqa01_e00_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_e01_kn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e01_sv.xma", 1 }, { "xenon/sound/voice/e/aqa01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e03_kn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/aqa01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e03_sv.xma", 1 }, { "xenon/sound/voice/e/aqa01_e03_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_e04_sd.xma", 1 }, { "xenon/sound/voice/e/aqa01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e04_sv.xma", 1 }, { "xenon/sound/voice/e/aqa01_e05_sn.xma", 1 }, { "xenon/sound/voice/e/aqa01_e06_ec.xma", 1 }, { "xenon/sound/voice/e/aqa01_e07_ec.xma", 1 }, { "xenon/sound/voice/e/aqa01_e08_ec.xma", 1 }, { "xenon/sound/voice/e/aqa01_e09_ec.xma", 1 }, { "xenon/sound/voice/e/aqa01_w00_sd.xma", 1 }, { "xenon/sound/voice/e/aqa01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/aqa01_w00_sv.xma", 1 }, { "xenon/sound/voice/e/aqa01_w00_tl.xma", 1 }, { "xenon/sound/voice/e/aqa01_w01_kn.xma", 1 }, { "xenon/sound/voice/e/aqa01_w01_sd.xma", 1 }, { "xenon/sound/voice/e/aqa01_w01_sv.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_am.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_bz.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_kn.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_om.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_rg.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_sv.xma", 1 }, { "xenon/sound/voice/e/bat01_e00_tl.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_am.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_bz.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_kn.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_om.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_rg.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_sd.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_sn.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_sv.xma", 1 }, { "xenon/sound/voice/e/bat01_e01_tl.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_am.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_bz.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_kn.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_om.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_rg.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_sv.xma", 1 }, { "xenon/sound/voice/e/bat01_e02_tl.xma", 1 }, { "xenon/sound/voice/e/bat01_h00_so.xma", 1 }, { "xenon/sound/voice/e/bat01_h01_so.xma", 1 }, { "xenon/sound/voice/e/bat01_h02_so.xma", 1 }, { "xenon/sound/voice/e/bos01_a00_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a01_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a02_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a03_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a04_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a04ps_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a05_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a06_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a07_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_a08_bz.xma", 1 }, { "xenon/sound/voice/e/bos01_e00_sv.xma", 1 }, { "xenon/sound/voice/e/bos01_e01_sv.xma", 1 }, { "xenon/sound/voice/e/bos01_e02_bz.xma", 1 }, { "xenon/sound/voice/e/bos02_a00_sd.xma", 1 }, { "xenon/sound/voice/e/bos02_a00_sn.xma", 1 }, { "xenon/sound/voice/e/bos02_a01_sd.xma", 1 }, { "xenon/sound/voice/e/bos02_a01_sn.xma", 1 }, { "xenon/sound/voice/e/bos02_a02_sd.xma", 1 }, { "xenon/sound/voice/e/bos02_a02_sn.xma", 1 }, { "xenon/sound/voice/e/bos02_a03_rg.xma", 1 }, { "xenon/sound/voice/e/bos02_a03_tl.xma", 1 }, { "xenon/sound/voice/e/bos02_a04_rg.xma", 1 }, { "xenon/sound/voice/e/bos02_a04_tl.xma", 1 }, { "xenon/sound/voice/e/bos02_a05_kn.xma", 1 }, { "xenon/sound/voice/e/bos02_a05_rg.xma", 1 }, { "xenon/sound/voice/e/bos02_e00_sd.xma", 1 }, { "xenon/sound/voice/e/bos02_e00_sn.xma", 1 }, { "xenon/sound/voice/e/bos02_e01_sd.xma", 1 }, { "xenon/sound/voice/e/bos02_e01_sn.xma", 1 }, { "xenon/sound/voice/e/bos03_a00_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a01_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a02_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a03_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a05_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a06_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a07_1_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a07_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a08_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a09_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_a10_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_e00_sv.xma", 1 }, { "xenon/sound/voice/e/bos03_e01_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_e02_bz.xma", 1 }, { "xenon/sound/voice/e/bos03_e03_sv.xma", 1 }, { "xenon/sound/voice/e/bos03_e04_sv.xma", 1 }, { "xenon/sound/voice/e/bos04_a00_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a01_1_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a01_1ps_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a01_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a01ps_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a02_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a03_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a04_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a05_1ps_om.xma", 1 }, { "xenon/sound/voice/e/bos04_a05_om.xma", 1 }, { "xenon/sound/voice/e/bos04_a05ps_om.xma", 1 }, { "xenon/sound/voice/e/bos04_a06_1ps_om.xma", 1 }, { "xenon/sound/voice/e/bos04_a06_om.xma", 1 }, { "xenon/sound/voice/e/bos04_a06ps_om.xma", 1 }, { "xenon/sound/voice/e/bos04_a07_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_a08_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_e00_om.xma", 1 }, { "xenon/sound/voice/e/bos04_e00_sd.xma", 1 }, { "xenon/sound/voice/e/bos04_e01_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e02_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e03_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e04_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e05_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e06_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e07_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e08_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e09_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e10_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e11_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e12_mf.xma", 1 }, { "xenon/sound/voice/e/bos04_e13_mf.xma", 1 }, { "xenon/sound/voice/e/bos05_a00_rg.xma", 1 }, { "xenon/sound/voice/e/bos05_a01_rg.xma", 1 }, { "xenon/sound/voice/e/bos05_a02_rg.xma", 1 }, { "xenon/sound/voice/e/bos05_a03_om.xma", 1 }, { "xenon/sound/voice/e/bos05_a04_om.xma", 1 }, { "xenon/sound/voice/e/bos05_e00_sd.xma", 1 }, { "xenon/sound/voice/e/bos05_e01_om.xma", 1 }, { "xenon/sound/voice/e/bos05_e02_rg.xma", 1 }, { "xenon/sound/voice/e/bos05_e03_mf.xma", 1 }, { "xenon/sound/voice/e/bos05_e04_mf.xma", 1 }, { "xenon/sound/voice/e/bos05_e05_mf.xma", 1 }, { "xenon/sound/voice/e/bos05_e06_mf.xma", 1 }, { "xenon/sound/voice/e/bos05_e07_mf.xma", 1 }, { "xenon/sound/voice/e/bos05_e08_mf.xma", 1 }, { "xenon/sound/voice/e/bos06_a00_sd.xma", 1 }, { "xenon/sound/voice/e/bos06_a00_sn.xma", 1 }, { "xenon/sound/voice/e/bos06_a01_rg.xma", 1 }, { "xenon/sound/voice/e/bos06_a01_tl.xma", 1 }, { "xenon/sound/voice/e/bos06_a02_rg.xma", 1 }, { "xenon/sound/voice/e/bos06_a02_tl.xma", 1 }, { "xenon/sound/voice/e/bos06_a03_1_rg.xma", 1 }, { "xenon/sound/voice/e/bos06_a03_1_tl.xma", 1 }, { "xenon/sound/voice/e/bos06_a03_rg.xma", 1 }, { "xenon/sound/voice/e/bos06_a03_tl.xma", 1 }, { "xenon/sound/voice/e/bos06_a04_rg.xma", 1 }, { "xenon/sound/voice/e/bos06_a04_tl.xma", 1 }, { "xenon/sound/voice/e/bos06_a05_rg.xma", 1 }, { "xenon/sound/voice/e/bos06_a05_tl.xma", 1 }, { "xenon/sound/voice/e/bos06_e00_eg.xma", 1 }, { "xenon/sound/voice/e/bos06_e00_sd.xma", 1 }, { "xenon/sound/voice/e/bos06_e00_sn.xma", 1 }, { "xenon/sound/voice/e/bos06_e01_sd.xma", 1 }, { "xenon/sound/voice/e/bos06_e01_sn.xma", 1 }, { "xenon/sound/voice/e/bos06_e02_eg.xma", 1 }, { "xenon/sound/voice/e/bos06_e03_eg.xma", 1 }, { "xenon/sound/voice/e/bos06_e04_sd.xma", 1 }, { "xenon/sound/voice/e/bos06_e04_sn.xma", 1 }, { "xenon/sound/voice/e/bos07_a00_bz.xma", 1 }, { "xenon/sound/voice/e/bos07_a00_sn.xma", 1 }, { "xenon/sound/voice/e/bos07_a01_bz.xma", 1 }, { "xenon/sound/voice/e/bos07_a01_sn.xma", 1 }, { "xenon/sound/voice/e/bos07_a02_1_sn.xma", 1 }, { "xenon/sound/voice/e/bos07_a02_bz.xma", 1 }, { "xenon/sound/voice/e/bos07_a02_sn.xma", 1 }, { "xenon/sound/voice/e/bos07_a03_bz.xma", 1 }, { "xenon/sound/voice/e/bos07_a04_bz.xma", 1 }, { "xenon/sound/voice/e/bos07_a05_bz.xma", 1 }, { "xenon/sound/voice/e/bos07_a05_pr.xma", 1 }, { "xenon/sound/voice/e/bos07_a06_bz.xma", 1 }, { "xenon/sound/voice/e/bos07_a06_pr.xma", 1 }, { "xenon/sound/voice/e/bos07_e00_sn.xma", 1 }, { "xenon/sound/voice/e/bos07_e00_sv.xma", 1 }, { "xenon/sound/voice/e/bos07_e01_pr.xma", 1 }, { "xenon/sound/voice/e/bos07_e02_pr.xma", 1 }, { "xenon/sound/voice/e/bos07_e03_sn.xma", 1 }, { "xenon/sound/voice/e/bos07_e03_sv.xma", 1 }, { "xenon/sound/voice/e/bos07_e04_eg.xma", 1 }, { "xenon/sound/voice/e/bos07_e05_eg.xma", 1 }, { "xenon/sound/voice/e/bos07_e07_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_a00_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_a01_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_a02_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_a03_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_a04_pr.xma", 1 }, { "xenon/sound/voice/e/bos08_a05_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_a06_pr.xma", 1 }, { "xenon/sound/voice/e/bos08_a07_pr.xma", 1 }, { "xenon/sound/voice/e/bos08_e00_1_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e00_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e00_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_e01_pr.xma", 1 }, { "xenon/sound/voice/e/bos08_e02_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e03_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e04_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e05_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e06_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e07_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e08_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e09_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e10_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e11_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e12_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e13_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e13_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_e14_sn.xma", 1 }, { "xenon/sound/voice/e/bos08_e15_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e16_eg.xma", 1 }, { "xenon/sound/voice/e/bos08_e17_eg.xma", 1 }, { "xenon/sound/voice/e/bos09_a00_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_a00_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_a01_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_a01_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_a02_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_a02_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e00_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e00_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e01_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e01_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e02_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e02_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e03_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e03_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e04_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e05_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e05_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e06_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e06_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e07_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e07_sv.xma", 1 }, { "xenon/sound/voice/e/bos09_e08_sn.xma", 1 }, { "xenon/sound/voice/e/bos09_e08_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_a00_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_a00_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_a01_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_a01_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_a02_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e00_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e00_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_e01_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e02_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e03_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e04_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_e05_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e05_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_e06_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e06_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_e07_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e08_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e08_sv.xma", 1 }, { "xenon/sound/voice/e/bos10_e09_sd.xma", 1 }, { "xenon/sound/voice/e/bos10_e09_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_a06_pr.xma", 1 }, { "xenon/sound/voice/e/bos11_a07_am.xma", 1 }, { "xenon/sound/voice/e/bos11_a07_om.xma", 1 }, { "xenon/sound/voice/e/bos11_a08_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_a09_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_a10_om.xma", 1 }, { "xenon/sound/voice/e/bos11_a11_am.xma", 1 }, { "xenon/sound/voice/e/bos11_a12_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_a13_1_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_a13_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_a15_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_a16_pr.xma", 1 }, { "xenon/sound/voice/e/bos11_a17_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_a18_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_a19_am.xma", 1 }, { "xenon/sound/voice/e/bos11_a20_tl.xma", 1 }, { "xenon/sound/voice/e/bos11_a20ps_tl.xma", 1 }, { "xenon/sound/voice/e/bos11_a21_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_a21ps_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_a22_om.xma", 1 }, { "xenon/sound/voice/e/bos11_a23_rg.xma", 1 }, { "xenon/sound/voice/e/bos11_e00_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_e00_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_e01_1_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_e01_2_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_e01_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_e01_rg.xma", 1 }, { "xenon/sound/voice/e/bos11_e02_am.xma", 1 }, { "xenon/sound/voice/e/bos11_e02_tl.xma", 1 }, { "xenon/sound/voice/e/bos11_e03_am.xma", 1 }, { "xenon/sound/voice/e/bos11_e03_rg.xma", 1 }, { "xenon/sound/voice/e/bos11_e03ps_rg.xma", 1 }, { "xenon/sound/voice/e/bos11_e04_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_e04_pr.xma", 1 }, { "xenon/sound/voice/e/bos11_e04ps_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_e05_eg.xma", 1 }, { "xenon/sound/voice/e/bos11_e05_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_e05ps_kn.xma", 1 }, { "xenon/sound/voice/e/bos11_e06_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e06_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e06_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e07_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e07_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e07_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e07ps_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e07ps_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e07ps_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e08_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e08_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e08_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e09_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e09_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e09_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e10_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e10_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e10_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e11_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e11_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e11_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e12_sd.xma", 1 }, { "xenon/sound/voice/e/bos11_e12_sn.xma", 1 }, { "xenon/sound/voice/e/bos11_e12_sv.xma", 1 }, { "xenon/sound/voice/e/bos11_e14_tl.xma", 1 }, { "xenon/sound/voice/e/csc01_a01_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_a02_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_a02_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_a03_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_a04_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_a05_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_a06_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_a06_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_a07_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_a07_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_a08_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_a09_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_a10_bz.xma", 1 }, { "xenon/sound/voice/e/csc01_a11_bz.xma", 1 }, { "xenon/sound/voice/e/csc01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_e01_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e01_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_e04_bz.xma", 1 }, { "xenon/sound/voice/e/csc01_e04_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_e04_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_e05_bz.xma", 1 }, { "xenon/sound/voice/e/csc01_e05_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e05_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_e05_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_e06_bz.xma", 1 }, { "xenon/sound/voice/e/csc01_e06_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e06_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_e06_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_e07_bz.xma", 1 }, { "xenon/sound/voice/e/csc01_e07_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_e08_sv.xma", 1 }, { "xenon/sound/voice/e/csc01_e09_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_e09_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_w00_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_w01_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_w02_bz.xma", 1 }, { "xenon/sound/voice/e/csc01_w02_sn.xma", 1 }, { "xenon/sound/voice/e/csc01_w03_sd.xma", 1 }, { "xenon/sound/voice/e/csc01_w03_sv.xma", 1 }, { "xenon/sound/voice/e/dtd01_a00_pr.xma", 1 }, { "xenon/sound/voice/e/dtd01_a00_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_a01_pr.xma", 1 }, { "xenon/sound/voice/e/dtd01_a01_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_a02_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_a03_am.xma", 1 }, { "xenon/sound/voice/e/dtd01_a03_pr.xma", 1 }, { "xenon/sound/voice/e/dtd01_a03_sv.xma", 1 }, { "xenon/sound/voice/e/dtd01_a04_sv.xma", 1 }, { "xenon/sound/voice/e/dtd01_a05_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_e00_am.xma", 1 }, { "xenon/sound/voice/e/dtd01_e00_pr.xma", 1 }, { "xenon/sound/voice/e/dtd01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_e01_pr.xma", 1 }, { "xenon/sound/voice/e/dtd01_e02_pr.xma", 1 }, { "xenon/sound/voice/e/dtd01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/dtd01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_e03_sv.xma", 1 }, { "xenon/sound/voice/e/dtd01_e04_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_e05_am.xma", 1 }, { "xenon/sound/voice/e/dtd01_e05_om.xma", 1 }, { "xenon/sound/voice/e/dtd01_e06_am.xma", 1 }, { "xenon/sound/voice/e/dtd01_e06_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_h00_so.xma", 1 }, { "xenon/sound/voice/e/dtd01_w00_pr.xma", 1 }, { "xenon/sound/voice/e/dtd01_w00_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/dtd01_w01_am.xma", 1 }, { "xenon/sound/voice/e/dtd01_w01_om.xma", 1 }, { "xenon/sound/voice/e/dtd01_w01_rg.xma", 1 }, { "xenon/sound/voice/e/dtd01_w01_sd.xma", 1 }, { "xenon/sound/voice/e/dtd01_w01_sv.xma", 1 }, { "xenon/sound/voice/e/end01_e00_am.xma", 1 }, { "xenon/sound/voice/e/end01_e00_kn.xma", 1 }, { "xenon/sound/voice/e/end01_e00_om.xma", 1 }, { "xenon/sound/voice/e/end01_e00_rg.xma", 1 }, { "xenon/sound/voice/e/end01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/end01_e00_sv.xma", 1 }, { "xenon/sound/voice/e/end01_e00_tl.xma", 1 }, { "xenon/sound/voice/e/end01_e01_am.xma", 1 }, { "xenon/sound/voice/e/end01_e01_kn.xma", 1 }, { "xenon/sound/voice/e/end01_e01_om.xma", 1 }, { "xenon/sound/voice/e/end01_e01_rg.xma", 1 }, { "xenon/sound/voice/e/end01_e01_sd.xma", 1 }, { "xenon/sound/voice/e/end01_e01_sv.xma", 1 }, { "xenon/sound/voice/e/end01_e01_tl.xma", 1 }, { "xenon/sound/voice/e/end01_e02_am.xma", 1 }, { "xenon/sound/voice/e/end01_e02_kn.xma", 1 }, { "xenon/sound/voice/e/end01_e02_rg.xma", 1 }, { "xenon/sound/voice/e/end01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/end01_e02_sv.xma", 1 }, { "xenon/sound/voice/e/end01_e02_tl.xma", 1 }, { "xenon/sound/voice/e/end01_e03_am.xma", 1 }, { "xenon/sound/voice/e/end01_e03_kn.xma", 1 }, { "xenon/sound/voice/e/end01_e03_om.xma", 1 }, { "xenon/sound/voice/e/end01_e03_rg.xma", 1 }, { "xenon/sound/voice/e/end01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/end01_e03_sv.xma", 1 }, { "xenon/sound/voice/e/end01_e03_tl.xma", 1 }, { "xenon/sound/voice/e/end01_e04_am.xma", 1 }, { "xenon/sound/voice/e/end01_e04_kn.xma", 1 }, { "xenon/sound/voice/e/end01_e04_om.xma", 1 }, { "xenon/sound/voice/e/end01_e04_rg.xma", 1 }, { "xenon/sound/voice/e/end01_e04_sd.xma", 1 }, { "xenon/sound/voice/e/end01_e04_sv.xma", 1 }, { "xenon/sound/voice/e/end01_e04_tl.xma", 1 }, { "xenon/sound/voice/e/end01_e05_am.xma", 1 }, { "xenon/sound/voice/e/end01_e05_kn.xma", 1 }, { "xenon/sound/voice/e/end01_e05_om.xma", 1 }, { "xenon/sound/voice/e/end01_e05_rg.xma", 1 }, { "xenon/sound/voice/e/end01_e05_sd.xma", 1 }, { "xenon/sound/voice/e/end01_e05_sv.xma", 1 }, { "xenon/sound/voice/e/end01_e05_tl.xma", 1 }, { "xenon/sound/voice/e/end01_h00_1_so.xma", 1 }, { "xenon/sound/voice/e/end01_h00_so.xma", 1 }, { "xenon/sound/voice/e/flc01_a00_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_a00_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_a00_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_a01_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_a01_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_a01_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_a02_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_a02_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_a03_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_a03_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_a03_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_a040_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_a04_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_a04_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_a04_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_a05_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_a06_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_e01_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_e01_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_e01_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_e02_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_e03_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_e04_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_e04_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_e05_sv.xma", 1 }, { "xenon/sound/voice/e/flc01_h00_so.xma", 1 }, { "xenon/sound/voice/e/flc01_h01_so.xma", 1 }, { "xenon/sound/voice/e/flc01_h02_so.xma", 1 }, { "xenon/sound/voice/e/flc01_h03_so.xma", 1 }, { "xenon/sound/voice/e/flc01_h04_so.xma", 1 }, { "xenon/sound/voice/e/flc01_w00_bz.xma", 1 }, { "xenon/sound/voice/e/flc01_w00_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_w01_kn.xma", 1 }, { "xenon/sound/voice/e/flc01_w01_rg.xma", 1 }, { "xenon/sound/voice/e/flc01_w01_sd.xma", 1 }, { "xenon/sound/voice/e/flc01_w01_sn.xma", 1 }, { "xenon/sound/voice/e/flc01_w01_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a00_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_a00_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a01_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a02_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a03_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_a03_sn.xma", 1 }, { "xenon/sound/voice/e/kdv01_a03_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a04_rg.xma", 1 }, { "xenon/sound/voice/e/kdv01_a05_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a06_rg.xma", 1 }, { "xenon/sound/voice/e/kdv01_a06_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a07_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_a08_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_e01_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/kdv01_e02_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_e04_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_e05_sn.xma", 1 }, { "xenon/sound/voice/e/kdv01_e05_sv.xma", 1 }, { "xenon/sound/voice/e/kdv01_e06_sn.xma", 1 }, { "xenon/sound/voice/e/kdv01_e07_rg.xma", 1 }, { "xenon/sound/voice/e/kdv01_e08_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_e09_rg.xma", 1 }, { "xenon/sound/voice/e/kdv01_e09_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_e09_sn.xma", 1 }, { "xenon/sound/voice/e/kdv01_e10_rg.xma", 1 }, { "xenon/sound/voice/e/kdv01_e10_sn.xma", 1 }, { "xenon/sound/voice/e/kdv01_e11_rg.xma", 1 }, { "xenon/sound/voice/e/kdv01_h00_so.xma", 1 }, { "xenon/sound/voice/e/kdv01_h01_so.xma", 1 }, { "xenon/sound/voice/e/kdv01_h02_so.xma", 1 }, { "xenon/sound/voice/e/kdv01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/kdv01_w01_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_w02_rg.xma", 1 }, { "xenon/sound/voice/e/kdv01_w02_sd.xma", 1 }, { "xenon/sound/voice/e/kdv01_w02_sv.xma", 1 }, { "xenon/sound/voice/e/mis01_e00_am.xma", 1 }, { "xenon/sound/voice/e/mis01_e00_pr.xma", 1 }, { "xenon/sound/voice/e/mis01_e01_am.xma", 1 }, { "xenon/sound/voice/e/mis01_e01_pr.xma", 1 }, { "xenon/sound/voice/e/mis01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/mis01_e02_tl.xma", 1 }, { "xenon/sound/voice/e/mis01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/mis01_e03_tl.xma", 1 }, { "xenon/sound/voice/e/mis01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/mis01_e05_sn.xma", 1 }, { "xenon/sound/voice/e/mis01_e06_kn.xma", 1 }, { "xenon/sound/voice/e/mis01_e06_sn.xma", 1 }, { "xenon/sound/voice/e/mis01_e06_tl.xma", 1 }, { "xenon/sound/voice/e/mis01_e07_kn.xma", 1 }, { "xenon/sound/voice/e/mis01_e07_sn.xma", 1 }, { "xenon/sound/voice/e/mis01_e07_tl.xma", 1 }, { "xenon/sound/voice/e/mis01_e08_am.xma", 1 }, { "xenon/sound/voice/e/mis01_e08_sv.xma", 1 }, { "xenon/sound/voice/e/mis01_e09_am.xma", 1 }, { "xenon/sound/voice/e/mis01_e09_bz.xma", 1 }, { "xenon/sound/voice/e/mis01_e09_sv.xma", 1 }, { "xenon/sound/voice/e/mis01_e10_bz.xma", 1 }, { "xenon/sound/voice/e/mis01_e11_rg.xma", 1 }, { "xenon/sound/voice/e/mis01_e13_rg.xma", 1 }, { "xenon/sound/voice/e/mis01_e14_om.xma", 1 }, { "xenon/sound/voice/e/mis01_e15_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a00_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a00_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_a00_sv.xma", 1 }, { "xenon/sound/voice/e/rct01_a01_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a01_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_a02_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a03_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a04_sv.xma", 1 }, { "xenon/sound/voice/e/rct01_a05_sv.xma", 1 }, { "xenon/sound/voice/e/rct01_a06_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a06_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_a07_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a07_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_a07_sv.xma", 1 }, { "xenon/sound/voice/e/rct01_a08_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_a09_sv.xma", 1 }, { "xenon/sound/voice/e/rct01_a10_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a10_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_a11_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_a12_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_eg.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_hu.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_hu_a.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_hu_b.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_hu_c.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_hu_d.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_hu_e.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_hu_f.xma", 1 }, { "xenon/sound/voice/e/rct01_e01_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/rct01_e05_eg.xma", 1 }, { "xenon/sound/voice/e/rct01_e06_eg.xma", 1 }, { "xenon/sound/voice/e/rct01_e07_eg.xma", 1 }, { "xenon/sound/voice/e/rct01_e08_eg.xma", 1 }, { "xenon/sound/voice/e/rct01_e09_eg.xma", 1 }, { "xenon/sound/voice/e/rct01_e10_eg.xma", 1 }, { "xenon/sound/voice/e/rct01_h00_so.xma", 1 }, { "xenon/sound/voice/e/rct01_h01_so.xma", 1 }, { "xenon/sound/voice/e/rct01_h02_so.xma", 1 }, { "xenon/sound/voice/e/rct01_h03_so.xma", 1 }, { "xenon/sound/voice/e/rct01_ho1_so.xma", 1 }, { "xenon/sound/voice/e/rct01_ho2_so.xma", 1 }, { "xenon/sound/voice/e/rct01_ho3_so.xma", 1 }, { "xenon/sound/voice/e/rct01_hoo_so.xma", 1 }, { "xenon/sound/voice/e/rct01_w00_sd.xma", 1 }, { "xenon/sound/voice/e/rct01_w00_sv.xma", 1 }, { "xenon/sound/voice/e/rct01_w01_sd.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_am.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_bz.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_kn.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_om.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_pr.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_rg.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_sd.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_sv.xma", 1 }, { "xenon/sound/voice/e/sys01_e00_tl.xma", 1 }, { "xenon/sound/voice/e/tpj01_a00_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a01_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_a01_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a02_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a03_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_a03_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a04_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_a04_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_a04_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a04_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a05_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_a05_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_a05_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a05_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a06_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_a06_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a07_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a08_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a09_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a10_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a10_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a11_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_a11_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a12_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_a12_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a12_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_a13_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_a13_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_a13_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_e01_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_e02_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_e03_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_e05_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_e06_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_e07_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_e08_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_e09_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_e10_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_e11_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_e12_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_e13_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_e14_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_e15_sv.xma", 1 }, { "xenon/sound/voice/e/tpj01_e16_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_e17_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_e18_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_e19_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_e20_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_h00_so.xma", 1 }, { "xenon/sound/voice/e/tpj01_h01_so.xma", 1 }, { "xenon/sound/voice/e/tpj01_h02_so.xma", 1 }, { "xenon/sound/voice/e/tpj01_h03_so.xma", 1 }, { "xenon/sound/voice/e/tpj01_h04_so.xma", 1 }, { "xenon/sound/voice/e/tpj01_w00_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_w01_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_w01_sn.xma", 1 }, { "xenon/sound/voice/e/tpj01_w02_pr.xma", 1 }, { "xenon/sound/voice/e/tpj01_w02_rg.xma", 1 }, { "xenon/sound/voice/e/tpj01_w02_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_am.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_om.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a00_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_am.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_om.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a01_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_am.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_om.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a02_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_am.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_om.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a03_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_am.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_om.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a04_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_am.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_om.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a05_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_am.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_om.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_a06_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_e00_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e01_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e01ps_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e02_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_e04_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e05_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_e05_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e06_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e07_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e07ps_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e08_pr.xma", 1 }, { "xenon/sound/voice/e/twn01_e09_pr.xma", 1 }, { "xenon/sound/voice/e/twn01_e10_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_e11_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_e12_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_e13_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e14_an.xma", 1 }, { "xenon/sound/voice/e/twn01_e15_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e16_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e17_an.xma", 1 }, { "xenon/sound/voice/e/twn01_e18_kn.xma", 1 }, { "xenon/sound/voice/e/twn01_e18_sn.xma", 1 }, { "xenon/sound/voice/e/twn01_e18_tl.xma", 1 }, { "xenon/sound/voice/e/twn01_e19_1_an.xma", 1 }, { "xenon/sound/voice/e/twn01_e19_an.xma", 1 }, { "xenon/sound/voice/e/twn01_e20_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e21_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e22_am.xma", 1 }, { "xenon/sound/voice/e/twn01_e23_am.xma", 1 }, { "xenon/sound/voice/e/twn01_e24_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e25_am.xma", 1 }, { "xenon/sound/voice/e/twn01_e26_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e27_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e28_am.xma", 1 }, { "xenon/sound/voice/e/twn01_e29_am.xma", 1 }, { "xenon/sound/voice/e/twn01_e30_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_e31_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e32_bz.xma", 1 }, { "xenon/sound/voice/e/twn01_e33_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e34_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e35_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e36_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e37_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e38_an.xma", 1 }, { "xenon/sound/voice/e/twn01_e39_sv.xma", 1 }, { "xenon/sound/voice/e/twn01_e40_an.xma", 1 }, { "xenon/sound/voice/e/twn01_e41_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e42_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_e43_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_e44_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_e44ps_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_e45_1_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e45_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e46_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_e47_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e48_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e49_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_e50_1_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e50_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e51_sd.xma", 1 }, { "xenon/sound/voice/e/twn01_e52_gn.xma", 1 }, { "xenon/sound/voice/e/twn01_e53_rg.xma", 1 }, { "xenon/sound/voice/e/twn01_h00_so.xma", 1 }, { "xenon/sound/voice/e/twn01_h01_so.xma", 1 }, { "xenon/sound/voice/e/twn01_h02_so.xma", 1 }, { "xenon/sound/voice/e/twn01_h03_so.xma", 1 }, { "xenon/sound/voice/e/twn01_h04_so.xma", 1 }, { "xenon/sound/voice/e/twn01_h05_so.xma", 1 }, { "xenon/sound/voice/e/twn01_h06_so.xma", 1 }, { "xenon/sound/voice/e/twn01_h07_so.xma", 1 }, { "xenon/sound/voice/e/wap01_a00_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_a00_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_a00_tl.xma", 1 }, { "xenon/sound/voice/e/wap01_a01_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_a01_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_a02_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_a03_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_a03_tl.xma", 1 }, { "xenon/sound/voice/e/wap01_a03ps_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_a03ps_tl.xma", 1 }, { "xenon/sound/voice/e/wap01_a04_sv.xma", 1 }, { "xenon/sound/voice/e/wap01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_e01_bz.xma", 1 }, { "xenon/sound/voice/e/wap01_e01_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_e01_sv.xma", 1 }, { "xenon/sound/voice/e/wap01_e01_tl.xma", 1 }, { "xenon/sound/voice/e/wap01_e02_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_e03_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_e04_sv.xma", 1 }, { "xenon/sound/voice/e/wap01_e05_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_e06_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_e07_bz.xma", 1 }, { "xenon/sound/voice/e/wap01_e08_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_e09_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_e09_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_e10_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_e10_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_e11_1_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_e11_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_e11_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_h00_so.xma", 1 }, { "xenon/sound/voice/e/wap01_h01_so.xma", 1 }, { "xenon/sound/voice/e/wap01_w00_bz.xma", 1 }, { "xenon/sound/voice/e/wap01_w00_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_w01_rg.xma", 1 }, { "xenon/sound/voice/e/wap01_w01_sd.xma", 1 }, { "xenon/sound/voice/e/wap01_w01_sn.xma", 1 }, { "xenon/sound/voice/e/wap01_w01_sv.xma", 1 }, { "xenon/sound/voice/e/wap01_w01_tl.xma", 1 }, { "xenon/sound/voice/e/wvo01_a00_tl.xma", 1 }, { "xenon/sound/voice/e/wvo01_a01_sd.xma", 1 }, { "xenon/sound/voice/e/wvo01_e00_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e01_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e02_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e03_om.xma", 1 }, { "xenon/sound/voice/e/wvo01_e03_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e03_tl.xma", 1 }, { "xenon/sound/voice/e/wvo01_e04_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e05_mf.xma", 1 }, { "xenon/sound/voice/e/wvo01_e05_tl.xma", 1 }, { "xenon/sound/voice/e/wvo01_e06_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e07_bz.xma", 1 }, { "xenon/sound/voice/e/wvo01_e07_sd.xma", 1 }, { "xenon/sound/voice/e/wvo01_e07_tl.xma", 1 }, { "xenon/sound/voice/e/wvo01_e08_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e08_tl.xma", 1 }, { "xenon/sound/voice/e/wvo01_e09_sd.xma", 1 }, { "xenon/sound/voice/e/wvo01_e09_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e10_eg.xma", 1 }, { "xenon/sound/voice/e/wvo01_e10_tl.xma", 1 }, { "xenon/sound/voice/e/wvo01_e11_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e12_bz.xma", 1 }, { "xenon/sound/voice/e/wvo01_e12_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_e13_sn.xma", 1 }, { "xenon/sound/voice/e/wvo01_h00_so.xma", 1 }, { "xenon/sound/voice/e/wvo01_h00ps_so.xma", 1 }, { "xenon/sound/voice/e/wvo01_w00_bz.xma", 1 }, { "xenon/sound/voice/e/wvo01_w00_om.xma", 1 }, { "xenon/sound/voice/e/wvo01_w00_rg.xma", 1 }, { "xenon/sound/voice/e/wvo01_w00_sd.xma", 1 }, { "xenon/sound/voice/e/wvo01_w00_tl.xma", 1 }, { "xenon/sound/voice/j/all01_a00_am.xma", 1 }, { "xenon/sound/voice/j/all01_a00_bz.xma", 1 }, { "xenon/sound/voice/j/all01_a00_kn.xma", 1 }, { "xenon/sound/voice/j/all01_a00_om.xma", 1 }, { "xenon/sound/voice/j/all01_a00_pr.xma", 1 }, { "xenon/sound/voice/j/all01_a00_rg.xma", 1 }, { "xenon/sound/voice/j/all01_a00_sd.xma", 1 }, { "xenon/sound/voice/j/all01_a00_sn.xma", 1 }, { "xenon/sound/voice/j/all01_a00_sv.xma", 1 }, { "xenon/sound/voice/j/all01_a00_tl.xma", 1 }, { "xenon/sound/voice/j/all01_a01_am.xma", 1 }, { "xenon/sound/voice/j/all01_a01_bz.xma", 1 }, { "xenon/sound/voice/j/all01_a01_kn.xma", 1 }, { "xenon/sound/voice/j/all01_a01_om.xma", 1 }, { "xenon/sound/voice/j/all01_a01_pr.xma", 1 }, { "xenon/sound/voice/j/all01_a01_rg.xma", 1 }, { "xenon/sound/voice/j/all01_a01_sd.xma", 1 }, { "xenon/sound/voice/j/all01_a01_sn.xma", 1 }, { "xenon/sound/voice/j/all01_a01_sv.xma", 1 }, { "xenon/sound/voice/j/all01_a01_tl.xma", 1 }, { "xenon/sound/voice/j/all01_a02_am.xma", 1 }, { "xenon/sound/voice/j/all01_a02_bz.xma", 1 }, { "xenon/sound/voice/j/all01_a02_kn.xma", 1 }, { "xenon/sound/voice/j/all01_a02_om.xma", 1 }, { "xenon/sound/voice/j/all01_a02_pr.xma", 1 }, { "xenon/sound/voice/j/all01_a02_rg.xma", 1 }, { "xenon/sound/voice/j/all01_a02_sd.xma", 1 }, { "xenon/sound/voice/j/all01_a02_sn.xma", 1 }, { "xenon/sound/voice/j/all01_a02_sv.xma", 1 }, { "xenon/sound/voice/j/all01_a02_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e00_am.xma", 1 }, { "xenon/sound/voice/j/all01_e00_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e00_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e00_om.xma", 1 }, { "xenon/sound/voice/j/all01_e00_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e00_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e00_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e00_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e01_am.xma", 1 }, { "xenon/sound/voice/j/all01_e01_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e01_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e01_om.xma", 1 }, { "xenon/sound/voice/j/all01_e01_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e01_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e01_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e01_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e01_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e02_am.xma", 1 }, { "xenon/sound/voice/j/all01_e02_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e02_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e02_om.xma", 1 }, { "xenon/sound/voice/j/all01_e02_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e02_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e02_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e03_am.xma", 1 }, { "xenon/sound/voice/j/all01_e03_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e03_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e03_om.xma", 1 }, { "xenon/sound/voice/j/all01_e03_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e03_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e03_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e03_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e04_am.xma", 1 }, { "xenon/sound/voice/j/all01_e04_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e04_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e04_om.xma", 1 }, { "xenon/sound/voice/j/all01_e04_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e04_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e04_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e04_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e04_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e05_am.xma", 1 }, { "xenon/sound/voice/j/all01_e05_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e05_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e05_om.xma", 1 }, { "xenon/sound/voice/j/all01_e05_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e05_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e05_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e05_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e05_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e05_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e06_am.xma", 1 }, { "xenon/sound/voice/j/all01_e06_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e06_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e06_om.xma", 1 }, { "xenon/sound/voice/j/all01_e06_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e06_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e06_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e06_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e06_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e06_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e07_am.xma", 1 }, { "xenon/sound/voice/j/all01_e07_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e07_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e07_om.xma", 1 }, { "xenon/sound/voice/j/all01_e07_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e07_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e07_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e07_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e07_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e07_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e08_am.xma", 1 }, { "xenon/sound/voice/j/all01_e08_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e08_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e08_om.xma", 1 }, { "xenon/sound/voice/j/all01_e08_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e08_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e08_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e08_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e08_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e08_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e09_am.xma", 1 }, { "xenon/sound/voice/j/all01_e09_bz.xma", 1 }, { "xenon/sound/voice/j/all01_e09_kn.xma", 1 }, { "xenon/sound/voice/j/all01_e09_om.xma", 1 }, { "xenon/sound/voice/j/all01_e09_pr.xma", 1 }, { "xenon/sound/voice/j/all01_e09_rg.xma", 1 }, { "xenon/sound/voice/j/all01_e09_sd.xma", 1 }, { "xenon/sound/voice/j/all01_e09_sn.xma", 1 }, { "xenon/sound/voice/j/all01_e09_sv.xma", 1 }, { "xenon/sound/voice/j/all01_e09_tl.xma", 1 }, { "xenon/sound/voice/j/all01_e10_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v09_am.xma", 1 }, { "xenon/sound/voice/j/all01_v09_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v09_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v09_om.xma", 1 }, { "xenon/sound/voice/j/all01_v09_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v09_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v09_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v09_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v09_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v09_tl.xma", 1 }, { "xenon/sound/voice/j/all01_v10_am.xma", 1 }, { "xenon/sound/voice/j/all01_v10_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v10_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v10_om.xma", 1 }, { "xenon/sound/voice/j/all01_v10_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v10_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v10_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v10_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v10_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v10_tl.xma", 1 }, { "xenon/sound/voice/j/all01_v11_am.xma", 1 }, { "xenon/sound/voice/j/all01_v11_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v11_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v11_om.xma", 1 }, { "xenon/sound/voice/j/all01_v11_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v11_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v11_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v11_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v11_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v11_tl.xma", 1 }, { "xenon/sound/voice/j/all01_v12_am.xma", 1 }, { "xenon/sound/voice/j/all01_v12_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v12_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v12_om.xma", 1 }, { "xenon/sound/voice/j/all01_v12_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v12_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v12_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v12_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v12_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v12_tl.xma", 1 }, { "xenon/sound/voice/j/all01_v13_am.xma", 1 }, { "xenon/sound/voice/j/all01_v13_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v13_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v13_om.xma", 1 }, { "xenon/sound/voice/j/all01_v13_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v13_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v13_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v13_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v13_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v13_tl.xma", 1 }, { "xenon/sound/voice/j/all01_v14_am.xma", 1 }, { "xenon/sound/voice/j/all01_v14_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v14_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v14_om.xma", 1 }, { "xenon/sound/voice/j/all01_v14_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v14_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v14_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v14_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v14_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v14_tl.xma", 1 }, { "xenon/sound/voice/j/all01_v15_am.xma", 1 }, { "xenon/sound/voice/j/all01_v15_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v15_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v15_om.xma", 1 }, { "xenon/sound/voice/j/all01_v15_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v15_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v15_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v15_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v15_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v15_tl.xma", 1 }, { "xenon/sound/voice/j/all01_v16_am.xma", 1 }, { "xenon/sound/voice/j/all01_v16_bz.xma", 1 }, { "xenon/sound/voice/j/all01_v16_kn.xma", 1 }, { "xenon/sound/voice/j/all01_v16_om.xma", 1 }, { "xenon/sound/voice/j/all01_v16_pr.xma", 1 }, { "xenon/sound/voice/j/all01_v16_rg.xma", 1 }, { "xenon/sound/voice/j/all01_v16_sd.xma", 1 }, { "xenon/sound/voice/j/all01_v16_sn.xma", 1 }, { "xenon/sound/voice/j/all01_v16_sv.xma", 1 }, { "xenon/sound/voice/j/all01_v16_tl.xma", 1 }, { "xenon/sound/voice/j/all01_w00_am.xma", 1 }, { "xenon/sound/voice/j/all01_w00_bz.xma", 1 }, { "xenon/sound/voice/j/all01_w00_kn.xma", 1 }, { "xenon/sound/voice/j/all01_w00_om.xma", 1 }, { "xenon/sound/voice/j/all01_w00_pr.xma", 1 }, { "xenon/sound/voice/j/all01_w00_rg.xma", 1 }, { "xenon/sound/voice/j/all01_w00_sd.xma", 1 }, { "xenon/sound/voice/j/all01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/all01_w00_sv.xma", 1 }, { "xenon/sound/voice/j/all01_w00_tl.xma", 1 }, { "xenon/sound/voice/j/all01_w01_am.xma", 1 }, { "xenon/sound/voice/j/all01_w01_bz.xma", 1 }, { "xenon/sound/voice/j/all01_w01_kn.xma", 1 }, { "xenon/sound/voice/j/all01_w01_om.xma", 1 }, { "xenon/sound/voice/j/all01_w01_pr.xma", 1 }, { "xenon/sound/voice/j/all01_w01_rg.xma", 1 }, { "xenon/sound/voice/j/all01_w01_sd.xma", 1 }, { "xenon/sound/voice/j/all01_w01_sn.xma", 1 }, { "xenon/sound/voice/j/all01_w01_sv.xma", 1 }, { "xenon/sound/voice/j/all01_w01_tl.xma", 1 }, { "xenon/sound/voice/j/all02_a00_am.xma", 1 }, { "xenon/sound/voice/j/all02_a00_bz.xma", 1 }, { "xenon/sound/voice/j/all02_a00_kn.xma", 1 }, { "xenon/sound/voice/j/all02_a00_om.xma", 1 }, { "xenon/sound/voice/j/all02_a00_rg.xma", 1 }, { "xenon/sound/voice/j/all02_a00_sd.xma", 1 }, { "xenon/sound/voice/j/all02_a00_sn.xma", 1 }, { "xenon/sound/voice/j/all02_a00_sv.xma", 1 }, { "xenon/sound/voice/j/all02_a00_tl.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_am.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_bz.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_kn.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_om.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_rg.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_sd.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_sn.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_sv.xma", 1 }, { "xenon/sound/voice/j/all02_a00ps_tl.xma", 1 }, { "xenon/sound/voice/j/all03_a00_am.xma", 1 }, { "xenon/sound/voice/j/all03_a00_bz.xma", 1 }, { "xenon/sound/voice/j/all03_a00_kn.xma", 1 }, { "xenon/sound/voice/j/all03_a00_om.xma", 1 }, { "xenon/sound/voice/j/all03_a00_pr.xma", 1 }, { "xenon/sound/voice/j/all03_a00_rg.xma", 1 }, { "xenon/sound/voice/j/all03_a00_sd.xma", 1 }, { "xenon/sound/voice/j/all03_a00_sn.xma", 1 }, { "xenon/sound/voice/j/all03_a00_sv.xma", 1 }, { "xenon/sound/voice/j/all03_a00_tl.xma", 1 }, { "xenon/sound/voice/j/all03_a01_bz.xma", 1 }, { "xenon/sound/voice/j/all03_a01_sd.xma", 1 }, { "xenon/sound/voice/j/all03_a01_sn.xma", 1 }, { "xenon/sound/voice/j/all03_h00_so.xma", 1 }, { "xenon/sound/voice/j/all03_h00ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h01_so.xma", 1 }, { "xenon/sound/voice/j/all03_h01ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h02_so.xma", 1 }, { "xenon/sound/voice/j/all03_h02ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h03_so.xma", 1 }, { "xenon/sound/voice/j/all03_h03ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h04_so.xma", 1 }, { "xenon/sound/voice/j/all03_h04ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h05_so.xma", 1 }, { "xenon/sound/voice/j/all03_h05ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h06_so.xma", 1 }, { "xenon/sound/voice/j/all03_h06ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h07_so.xma", 1 }, { "xenon/sound/voice/j/all03_h07ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h08_so.xma", 1 }, { "xenon/sound/voice/j/all03_h08ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h09_so.xma", 1 }, { "xenon/sound/voice/j/all03_h09ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h10_so.xma", 1 }, { "xenon/sound/voice/j/all03_h10ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h11_so.xma", 1 }, { "xenon/sound/voice/j/all03_h11ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h12_so.xma", 1 }, { "xenon/sound/voice/j/all03_h12ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h13_so.xma", 1 }, { "xenon/sound/voice/j/all03_h13ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h14_so.xma", 1 }, { "xenon/sound/voice/j/all03_h14ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h15_so.xma", 1 }, { "xenon/sound/voice/j/all03_h15ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h16_so.xma", 1 }, { "xenon/sound/voice/j/all03_h16ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h17_so.xma", 1 }, { "xenon/sound/voice/j/all03_h17ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h18_so.xma", 1 }, { "xenon/sound/voice/j/all03_h18ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h19_so.xma", 1 }, { "xenon/sound/voice/j/all03_h19ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h20_so.xma", 1 }, { "xenon/sound/voice/j/all03_h20ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h21_so.xma", 1 }, { "xenon/sound/voice/j/all03_h21ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h22_so.xma", 1 }, { "xenon/sound/voice/j/all03_h22ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h23_so.xma", 1 }, { "xenon/sound/voice/j/all03_h23ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h24_so.xma", 1 }, { "xenon/sound/voice/j/all03_h24ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h25_so.xma", 1 }, { "xenon/sound/voice/j/all03_h25ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h26_so.xma", 1 }, { "xenon/sound/voice/j/all03_h26ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h27_so.xma", 1 }, { "xenon/sound/voice/j/all03_h27ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h28_so.xma", 1 }, { "xenon/sound/voice/j/all03_h28ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h29_so.xma", 1 }, { "xenon/sound/voice/j/all03_h29ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h30_so.xma", 1 }, { "xenon/sound/voice/j/all03_h30ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h31_so.xma", 1 }, { "xenon/sound/voice/j/all03_h31ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h32_so.xma", 1 }, { "xenon/sound/voice/j/all03_h32ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h33_so.xma", 1 }, { "xenon/sound/voice/j/all03_h33ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h34_so.xma", 1 }, { "xenon/sound/voice/j/all03_h34ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h35_so.xma", 1 }, { "xenon/sound/voice/j/all03_h35ps_so.xma", 1 }, { "xenon/sound/voice/j/all03_h36_so.xma", 1 }, { "xenon/sound/voice/j/all03_h37_so.xma", 1 }, { "xenon/sound/voice/j/all03_h38_so.xma", 1 }, { "xenon/sound/voice/j/all04_a00_am.xma", 1 }, { "xenon/sound/voice/j/all04_a00_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a00_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a00_om.xma", 1 }, { "xenon/sound/voice/j/all04_a00_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a00_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a00_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a00_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a00_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a01_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a01_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a02_am.xma", 1 }, { "xenon/sound/voice/j/all04_a02_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a02_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a02_om.xma", 1 }, { "xenon/sound/voice/j/all04_a02_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a02_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a02_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a02_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a02_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a03_am.xma", 1 }, { "xenon/sound/voice/j/all04_a03_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a03_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a03_om.xma", 1 }, { "xenon/sound/voice/j/all04_a03_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a03_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a03_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a03_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a03_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a04_am.xma", 1 }, { "xenon/sound/voice/j/all04_a04_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a04_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a04_om.xma", 1 }, { "xenon/sound/voice/j/all04_a04_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a04_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a04_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a04_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a04_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a05_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a05_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a05_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a06_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a06_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a06_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a07_am.xma", 1 }, { "xenon/sound/voice/j/all04_a07_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a07_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a07_om.xma", 1 }, { "xenon/sound/voice/j/all04_a07_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a07_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a07_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a07_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a07_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a08_am.xma", 1 }, { "xenon/sound/voice/j/all04_a08_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a08_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a08_om.xma", 1 }, { "xenon/sound/voice/j/all04_a08_pr.xma", 1 }, { "xenon/sound/voice/j/all04_a08_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a08_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a08_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a08_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a08_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a09_am.xma", 1 }, { "xenon/sound/voice/j/all04_a09_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a09_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a09_om.xma", 1 }, { "xenon/sound/voice/j/all04_a09_pr.xma", 1 }, { "xenon/sound/voice/j/all04_a09_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a09_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a09_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a09_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a09_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a10_am.xma", 1 }, { "xenon/sound/voice/j/all04_a10_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a10_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a10_om.xma", 1 }, { "xenon/sound/voice/j/all04_a10_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a10_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a10_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a10_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a10_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a11_am.xma", 1 }, { "xenon/sound/voice/j/all04_a11_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a11_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a11_om.xma", 1 }, { "xenon/sound/voice/j/all04_a11_pr.xma", 1 }, { "xenon/sound/voice/j/all04_a11_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a11_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a11_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a11_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a11_tl.xma", 1 }, { "xenon/sound/voice/j/all04_a12_am.xma", 1 }, { "xenon/sound/voice/j/all04_a12_bz.xma", 1 }, { "xenon/sound/voice/j/all04_a12_kn.xma", 1 }, { "xenon/sound/voice/j/all04_a12_om.xma", 1 }, { "xenon/sound/voice/j/all04_a12_pr.xma", 1 }, { "xenon/sound/voice/j/all04_a12_rg.xma", 1 }, { "xenon/sound/voice/j/all04_a12_sd.xma", 1 }, { "xenon/sound/voice/j/all04_a12_sn.xma", 1 }, { "xenon/sound/voice/j/all04_a12_sv.xma", 1 }, { "xenon/sound/voice/j/all04_a12_tl.xma", 1 }, { "xenon/sound/voice/j/all04_h00_so.xma", 1 }, { "xenon/sound/voice/j/all04_h01_so.xma", 1 }, { "xenon/sound/voice/j/all05_a00_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a00_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a00_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a01_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a01_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a01_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a02_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a02_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a03_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a04_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a04_om.xma", 1 }, { "xenon/sound/voice/j/all05_a04_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a04_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a04_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a04_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a05_am.xma", 1 }, { "xenon/sound/voice/j/all05_a05_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a05_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a06_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a06_om.xma", 1 }, { "xenon/sound/voice/j/all05_a06_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a06_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a06_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a06_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a07_am.xma", 1 }, { "xenon/sound/voice/j/all05_a07_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a07_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a08_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a08_om.xma", 1 }, { "xenon/sound/voice/j/all05_a08_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a08_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a08_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a08_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a09_am.xma", 1 }, { "xenon/sound/voice/j/all05_a09_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a09_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a10_am.xma", 1 }, { "xenon/sound/voice/j/all05_a10_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a10_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a10_om.xma", 1 }, { "xenon/sound/voice/j/all05_a10_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a10_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a10_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a10_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a10_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a11_am.xma", 1 }, { "xenon/sound/voice/j/all05_a11_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a11_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a11_om.xma", 1 }, { "xenon/sound/voice/j/all05_a11_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a11_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a11_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a11_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a11_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a12_am.xma", 1 }, { "xenon/sound/voice/j/all05_a12_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a12_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a12_om.xma", 1 }, { "xenon/sound/voice/j/all05_a12_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a12_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a12_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a12_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a12_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a13_am.xma", 1 }, { "xenon/sound/voice/j/all05_a13_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a13_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a13_om.xma", 1 }, { "xenon/sound/voice/j/all05_a13_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a13_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a13_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a13_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a13_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a14_am.xma", 1 }, { "xenon/sound/voice/j/all05_a14_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a14_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a14_om.xma", 1 }, { "xenon/sound/voice/j/all05_a14_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a14_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a14_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a14_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a14_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a15_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a15_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a15_om.xma", 1 }, { "xenon/sound/voice/j/all05_a15_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a15_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a15_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a16_am.xma", 1 }, { "xenon/sound/voice/j/all05_a16_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a16_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a17_am.xma", 1 }, { "xenon/sound/voice/j/all05_a17_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a17_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a17_om.xma", 1 }, { "xenon/sound/voice/j/all05_a17_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a17_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a17_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a17_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a17_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a18_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a18_om.xma", 1 }, { "xenon/sound/voice/j/all05_a18_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a18_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a18_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a18_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a19_am.xma", 1 }, { "xenon/sound/voice/j/all05_a19_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a19_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a20_am.xma", 1 }, { "xenon/sound/voice/j/all05_a20_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a20_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a20_om.xma", 1 }, { "xenon/sound/voice/j/all05_a20_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a20_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a20_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a20_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a20_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a21_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a21_om.xma", 1 }, { "xenon/sound/voice/j/all05_a21_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a21_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a21_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a21_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a22_am.xma", 1 }, { "xenon/sound/voice/j/all05_a22_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a22_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a23_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a23_om.xma", 1 }, { "xenon/sound/voice/j/all05_a23_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a23_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a23_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a23_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a24_am.xma", 1 }, { "xenon/sound/voice/j/all05_a24_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a24_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a25_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a25_om.xma", 1 }, { "xenon/sound/voice/j/all05_a25_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a25_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a25_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a25_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a26_am.xma", 1 }, { "xenon/sound/voice/j/all05_a26_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a26_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a27_am.xma", 1 }, { "xenon/sound/voice/j/all05_a27_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a27_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a27_om.xma", 1 }, { "xenon/sound/voice/j/all05_a27_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a27_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a27_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a27_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a27_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a28_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a28_om.xma", 1 }, { "xenon/sound/voice/j/all05_a28_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a28_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a28_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a28_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a29_am.xma", 1 }, { "xenon/sound/voice/j/all05_a29_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a29_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a30_am.xma", 1 }, { "xenon/sound/voice/j/all05_a30_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a30_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a30_om.xma", 1 }, { "xenon/sound/voice/j/all05_a30_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a30_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a30_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a30_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a30_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a31_am.xma", 1 }, { "xenon/sound/voice/j/all05_a31_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a31_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a31_om.xma", 1 }, { "xenon/sound/voice/j/all05_a31_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a31_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a31_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a31_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a31_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a32_am.xma", 1 }, { "xenon/sound/voice/j/all05_a32_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a32_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a32_om.xma", 1 }, { "xenon/sound/voice/j/all05_a32_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a32_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a32_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a32_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a32_tl.xma", 1 }, { "xenon/sound/voice/j/all05_a33_am.xma", 1 }, { "xenon/sound/voice/j/all05_a33_bz.xma", 1 }, { "xenon/sound/voice/j/all05_a33_kn.xma", 1 }, { "xenon/sound/voice/j/all05_a33_om.xma", 1 }, { "xenon/sound/voice/j/all05_a33_pr.xma", 1 }, { "xenon/sound/voice/j/all05_a33_rg.xma", 1 }, { "xenon/sound/voice/j/all05_a33_sd.xma", 1 }, { "xenon/sound/voice/j/all05_a33_sn.xma", 1 }, { "xenon/sound/voice/j/all05_a33_sv.xma", 1 }, { "xenon/sound/voice/j/all05_a33_tl.xma", 1 }, { "xenon/sound/voice/j/all05_e00_am.xma", 1 }, { "xenon/sound/voice/j/all05_e00_bz.xma", 1 }, { "xenon/sound/voice/j/all05_e00_kn.xma", 1 }, { "xenon/sound/voice/j/all05_e00_om.xma", 1 }, { "xenon/sound/voice/j/all05_e00_rg.xma", 1 }, { "xenon/sound/voice/j/all05_e00_sd.xma", 1 }, { "xenon/sound/voice/j/all05_e00_sn.xma", 1 }, { "xenon/sound/voice/j/all05_e00_sv.xma", 1 }, { "xenon/sound/voice/j/all05_e00_tl.xma", 1 }, { "xenon/sound/voice/j/all05_e01_am.xma", 1 }, { "xenon/sound/voice/j/all05_e01_bz.xma", 1 }, { "xenon/sound/voice/j/all05_e01_kn.xma", 1 }, { "xenon/sound/voice/j/all05_e01_om.xma", 1 }, { "xenon/sound/voice/j/all05_e01_rg.xma", 1 }, { "xenon/sound/voice/j/all05_e01_sd.xma", 1 }, { "xenon/sound/voice/j/all05_e01_sn.xma", 1 }, { "xenon/sound/voice/j/all05_e01_sv.xma", 1 }, { "xenon/sound/voice/j/all05_e01_tl.xma", 1 }, { "xenon/sound/voice/j/all05_e02_am.xma", 1 }, { "xenon/sound/voice/j/all05_e02_bz.xma", 1 }, { "xenon/sound/voice/j/all05_e02_kn.xma", 1 }, { "xenon/sound/voice/j/all05_e02_om.xma", 1 }, { "xenon/sound/voice/j/all05_e02_rg.xma", 1 }, { "xenon/sound/voice/j/all05_e02_sd.xma", 1 }, { "xenon/sound/voice/j/all05_e02_sn.xma", 1 }, { "xenon/sound/voice/j/all05_e02_sv.xma", 1 }, { "xenon/sound/voice/j/all05_e02_tl.xma", 1 }, { "xenon/sound/voice/j/all05_e03_am.xma", 1 }, { "xenon/sound/voice/j/all05_e03_bz.xma", 1 }, { "xenon/sound/voice/j/all05_e03_kn.xma", 1 }, { "xenon/sound/voice/j/all05_e03_om.xma", 1 }, { "xenon/sound/voice/j/all05_e03_rg.xma", 1 }, { "xenon/sound/voice/j/all05_e03_sd.xma", 1 }, { "xenon/sound/voice/j/all05_e03_sn.xma", 1 }, { "xenon/sound/voice/j/all05_e03_sv.xma", 1 }, { "xenon/sound/voice/j/all05_e03_tl.xma", 1 }, { "xenon/sound/voice/j/all06_a00_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a00_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a01_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a01_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a02_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a02_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a03_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a03_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a03ps_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a03ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a04_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a04_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a04ps_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a04ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a05_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a05_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a05ps_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a05ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a06_rg.xma", 1 }, { "xenon/sound/voice/j/all06_a06_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a07_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a07ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a08_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a09_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a09ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a11_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a12_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a12ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a13_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a14_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a14ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a15_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a15ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a16_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a16ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a17_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a18_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a19_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a19ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a20_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a20ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a21_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a21ps_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a22_sd.xma", 1 }, { "xenon/sound/voice/j/all06_a22ps_sd.xma", 1 }, { "xenon/sound/voice/j/all07_a00_am.xma", 1 }, { "xenon/sound/voice/j/all07_a00_bz.xma", 1 }, { "xenon/sound/voice/j/all07_a00_kn.xma", 1 }, { "xenon/sound/voice/j/all07_a00_om.xma", 1 }, { "xenon/sound/voice/j/all07_a00_rg.xma", 1 }, { "xenon/sound/voice/j/all07_a00_sd.xma", 1 }, { "xenon/sound/voice/j/all07_a00_sn.xma", 1 }, { "xenon/sound/voice/j/all07_a00_sv.xma", 1 }, { "xenon/sound/voice/j/all07_a00_tl.xma", 1 }, { "xenon/sound/voice/j/all07_a01_am.xma", 1 }, { "xenon/sound/voice/j/all07_a01_bz.xma", 1 }, { "xenon/sound/voice/j/all07_a01_kn.xma", 1 }, { "xenon/sound/voice/j/all07_a01_om.xma", 1 }, { "xenon/sound/voice/j/all07_a01_rg.xma", 1 }, { "xenon/sound/voice/j/all07_a01_sd.xma", 1 }, { "xenon/sound/voice/j/all07_a01_sn.xma", 1 }, { "xenon/sound/voice/j/all07_a01_sv.xma", 1 }, { "xenon/sound/voice/j/all07_a01_tl.xma", 1 }, { "xenon/sound/voice/j/all07_a02_am.xma", 1 }, { "xenon/sound/voice/j/all07_a02_bz.xma", 1 }, { "xenon/sound/voice/j/all07_a02_kn.xma", 1 }, { "xenon/sound/voice/j/all07_a02_om.xma", 1 }, { "xenon/sound/voice/j/all07_a02_rg.xma", 1 }, { "xenon/sound/voice/j/all07_a02_sd.xma", 1 }, { "xenon/sound/voice/j/all07_a02_sn.xma", 1 }, { "xenon/sound/voice/j/all07_a02_sv.xma", 1 }, { "xenon/sound/voice/j/all07_a02_tl.xma", 1 }, { "xenon/sound/voice/j/all07_a03_am.xma", 1 }, { "xenon/sound/voice/j/all07_a03_bz.xma", 1 }, { "xenon/sound/voice/j/all07_a03_kn.xma", 1 }, { "xenon/sound/voice/j/all07_a03_om.xma", 1 }, { "xenon/sound/voice/j/all07_a03_rg.xma", 1 }, { "xenon/sound/voice/j/all07_a03_sd.xma", 1 }, { "xenon/sound/voice/j/all07_a03_sn.xma", 1 }, { "xenon/sound/voice/j/all07_a03_sv.xma", 1 }, { "xenon/sound/voice/j/all07_a03_tl.xma", 1 }, { "xenon/sound/voice/j/all07_a04_am.xma", 1 }, { "xenon/sound/voice/j/all07_a04_bz.xma", 1 }, { "xenon/sound/voice/j/all07_a04_kn.xma", 1 }, { "xenon/sound/voice/j/all07_a04_rg.xma", 1 }, { "xenon/sound/voice/j/all07_a04_sd.xma", 1 }, { "xenon/sound/voice/j/all07_a04_sn.xma", 1 }, { "xenon/sound/voice/j/all07_a04_sv.xma", 1 }, { "xenon/sound/voice/j/all07_a04_tl.xma", 1 }, { "xenon/sound/voice/j/all08_e00_1_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e00_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e00_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e01_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e02_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e03_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e04_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e05_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e06_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e07_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e07_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e08_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e09_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e09_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e10_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e10_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e11_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e11_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e12_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e12_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e13_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e13_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e14_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e14_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e15_sd.xma", 1 }, { "xenon/sound/voice/j/all08_e15_sn.xma", 1 }, { "xenon/sound/voice/j/all08_e16_eg.xma", 1 }, { "xenon/sound/voice/j/all08_e17_eg.xma", 1 }, { "xenon/sound/voice/j/all08_e18_eg.xma", 1 }, { "xenon/sound/voice/j/all08_e19_eg.xma", 1 }, { "xenon/sound/voice/j/all08_e20_eg.xma", 1 }, { "xenon/sound/voice/j/all08_e21_eg.xma", 1 }, { "xenon/sound/voice/j/all08_h00_so.xma", 1 }, { "xenon/sound/voice/j/all08_h01_so.xma", 1 }, { "xenon/sound/voice/j/all08_h02_so.xma", 1 }, { "xenon/sound/voice/j/aqa00_e00_1_so.xma", 1 }, { "xenon/sound/voice/j/aqa00_e00_so.xma", 1 }, { "xenon/sound/voice/j/aqa00_e01_so.xma", 1 }, { "xenon/sound/voice/j/aqa01_a00_sn.xma", 1 }, { "xenon/sound/voice/j/aqa01_a00_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_a01_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_a02_kn.xma", 1 }, { "xenon/sound/voice/j/aqa01_a02_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_a03_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_a04_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_a05_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_a06_kn.xma", 1 }, { "xenon/sound/voice/j/aqa01_a06_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_a07_sn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e00_kn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/aqa01_e00_sv.xma", 1 }, { "xenon/sound/voice/j/aqa01_e00_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_e01_kn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e01_sv.xma", 1 }, { "xenon/sound/voice/j/aqa01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e03_kn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/aqa01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e03_sv.xma", 1 }, { "xenon/sound/voice/j/aqa01_e03_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_e04_sd.xma", 1 }, { "xenon/sound/voice/j/aqa01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e04_sv.xma", 1 }, { "xenon/sound/voice/j/aqa01_e05_sn.xma", 1 }, { "xenon/sound/voice/j/aqa01_e06_ec.xma", 1 }, { "xenon/sound/voice/j/aqa01_e07_ec.xma", 1 }, { "xenon/sound/voice/j/aqa01_e08_ec.xma", 1 }, { "xenon/sound/voice/j/aqa01_e09_ec.xma", 1 }, { "xenon/sound/voice/j/aqa01_w00_sd.xma", 1 }, { "xenon/sound/voice/j/aqa01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/aqa01_w00_sv.xma", 1 }, { "xenon/sound/voice/j/aqa01_w00_tl.xma", 1 }, { "xenon/sound/voice/j/aqa01_w01_kn.xma", 1 }, { "xenon/sound/voice/j/aqa01_w01_sd.xma", 1 }, { "xenon/sound/voice/j/aqa01_w01_sv.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_am.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_bz.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_kn.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_om.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_rg.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_sv.xma", 1 }, { "xenon/sound/voice/j/bat01_e00_tl.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_am.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_bz.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_kn.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_om.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_rg.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_sd.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_sn.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_sv.xma", 1 }, { "xenon/sound/voice/j/bat01_e01_tl.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_am.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_bz.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_kn.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_om.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_rg.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_sv.xma", 1 }, { "xenon/sound/voice/j/bat01_e02_tl.xma", 1 }, { "xenon/sound/voice/j/bat01_h00_so.xma", 1 }, { "xenon/sound/voice/j/bat01_h01_so.xma", 1 }, { "xenon/sound/voice/j/bat01_h02_so.xma", 1 }, { "xenon/sound/voice/j/bos01_a00_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a01_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a02_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a03_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a04_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a04ps_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a05_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a06_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a07_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_a08_bz.xma", 1 }, { "xenon/sound/voice/j/bos01_e00_sv.xma", 1 }, { "xenon/sound/voice/j/bos01_e01_sv.xma", 1 }, { "xenon/sound/voice/j/bos01_e02_bz.xma", 1 }, { "xenon/sound/voice/j/bos02_a00_sd.xma", 1 }, { "xenon/sound/voice/j/bos02_a00_sn.xma", 1 }, { "xenon/sound/voice/j/bos02_a01_sd.xma", 1 }, { "xenon/sound/voice/j/bos02_a01_sn.xma", 1 }, { "xenon/sound/voice/j/bos02_a02_sd.xma", 1 }, { "xenon/sound/voice/j/bos02_a02_sn.xma", 1 }, { "xenon/sound/voice/j/bos02_a03_rg.xma", 1 }, { "xenon/sound/voice/j/bos02_a03_tl.xma", 1 }, { "xenon/sound/voice/j/bos02_a04_rg.xma", 1 }, { "xenon/sound/voice/j/bos02_a04_tl.xma", 1 }, { "xenon/sound/voice/j/bos02_a05_kn.xma", 1 }, { "xenon/sound/voice/j/bos02_a05_rg.xma", 1 }, { "xenon/sound/voice/j/bos02_e00_sd.xma", 1 }, { "xenon/sound/voice/j/bos02_e00_sn.xma", 1 }, { "xenon/sound/voice/j/bos02_e01_sd.xma", 1 }, { "xenon/sound/voice/j/bos02_e01_sn.xma", 1 }, { "xenon/sound/voice/j/bos03_a00_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a01_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a02_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a03_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a05_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a06_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a07_1_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a07_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a08_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a09_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_a10_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_e00_sv.xma", 1 }, { "xenon/sound/voice/j/bos03_e01_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_e02_bz.xma", 1 }, { "xenon/sound/voice/j/bos03_e03_sv.xma", 1 }, { "xenon/sound/voice/j/bos03_e04_sv.xma", 1 }, { "xenon/sound/voice/j/bos04_a00_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a01_1_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a01_1ps_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a01_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a01ps_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a02_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a03_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a04_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a05_1_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a05_1ps_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a05_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a05ps_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a06_1_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a06_1ps_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a06_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a06ps_om.xma", 1 }, { "xenon/sound/voice/j/bos04_a07_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_a08_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_e00_om.xma", 1 }, { "xenon/sound/voice/j/bos04_e00_sd.xma", 1 }, { "xenon/sound/voice/j/bos04_e01_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e02_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e03_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e04_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e05_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e06_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e07_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e08_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e09_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e10_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e11_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e12_mf.xma", 1 }, { "xenon/sound/voice/j/bos04_e13_mf.xma", 1 }, { "xenon/sound/voice/j/bos05_a00_rg.xma", 1 }, { "xenon/sound/voice/j/bos05_a01_rg.xma", 1 }, { "xenon/sound/voice/j/bos05_a02_rg.xma", 1 }, { "xenon/sound/voice/j/bos05_a03_om.xma", 1 }, { "xenon/sound/voice/j/bos05_a04_om.xma", 1 }, { "xenon/sound/voice/j/bos05_e00_sd.xma", 1 }, { "xenon/sound/voice/j/bos05_e01_om.xma", 1 }, { "xenon/sound/voice/j/bos05_e02_rg.xma", 1 }, { "xenon/sound/voice/j/bos05_e03_mf.xma", 1 }, { "xenon/sound/voice/j/bos05_e04_mf.xma", 1 }, { "xenon/sound/voice/j/bos05_e05_mf.xma", 1 }, { "xenon/sound/voice/j/bos05_e06_mf.xma", 1 }, { "xenon/sound/voice/j/bos05_e07_mf.xma", 1 }, { "xenon/sound/voice/j/bos05_e08_mf.xma", 1 }, { "xenon/sound/voice/j/bos06_a00_sd.xma", 1 }, { "xenon/sound/voice/j/bos06_a00_sn.xma", 1 }, { "xenon/sound/voice/j/bos06_a01_rg.xma", 1 }, { "xenon/sound/voice/j/bos06_a01_tl.xma", 1 }, { "xenon/sound/voice/j/bos06_a02_rg.xma", 1 }, { "xenon/sound/voice/j/bos06_a02_tl.xma", 1 }, { "xenon/sound/voice/j/bos06_a03_1_rg.xma", 1 }, { "xenon/sound/voice/j/bos06_a03_1_tl.xma", 1 }, { "xenon/sound/voice/j/bos06_a03_rg.xma", 1 }, { "xenon/sound/voice/j/bos06_a03_tl.xma", 1 }, { "xenon/sound/voice/j/bos06_a04_rg.xma", 1 }, { "xenon/sound/voice/j/bos06_a04_tl.xma", 1 }, { "xenon/sound/voice/j/bos06_a05_rg.xma", 1 }, { "xenon/sound/voice/j/bos06_a05_tl.xma", 1 }, { "xenon/sound/voice/j/bos06_e00_eg.xma", 1 }, { "xenon/sound/voice/j/bos06_e00_sd.xma", 1 }, { "xenon/sound/voice/j/bos06_e00_sn.xma", 1 }, { "xenon/sound/voice/j/bos06_e01_sd.xma", 1 }, { "xenon/sound/voice/j/bos06_e01_sn.xma", 1 }, { "xenon/sound/voice/j/bos06_e02_eg.xma", 1 }, { "xenon/sound/voice/j/bos06_e03_eg.xma", 1 }, { "xenon/sound/voice/j/bos06_e04_sd.xma", 1 }, { "xenon/sound/voice/j/bos06_e04_sn.xma", 1 }, { "xenon/sound/voice/j/bos07_a00_bz.xma", 1 }, { "xenon/sound/voice/j/bos07_a00_sn.xma", 1 }, { "xenon/sound/voice/j/bos07_a01_bz.xma", 1 }, { "xenon/sound/voice/j/bos07_a01_sn.xma", 1 }, { "xenon/sound/voice/j/bos07_a02_1_sn.xma", 1 }, { "xenon/sound/voice/j/bos07_a02_bz.xma", 1 }, { "xenon/sound/voice/j/bos07_a02_sn.xma", 1 }, { "xenon/sound/voice/j/bos07_a03_bz.xma", 1 }, { "xenon/sound/voice/j/bos07_a04_bz.xma", 1 }, { "xenon/sound/voice/j/bos07_a05_bz.xma", 1 }, { "xenon/sound/voice/j/bos07_a05_pr.xma", 1 }, { "xenon/sound/voice/j/bos07_a06_bz.xma", 1 }, { "xenon/sound/voice/j/bos07_a06_pr.xma", 1 }, { "xenon/sound/voice/j/bos07_e00_sn.xma", 1 }, { "xenon/sound/voice/j/bos07_e00_sv.xma", 1 }, { "xenon/sound/voice/j/bos07_e01_pr.xma", 1 }, { "xenon/sound/voice/j/bos07_e02_pr.xma", 1 }, { "xenon/sound/voice/j/bos07_e03_sn.xma", 1 }, { "xenon/sound/voice/j/bos07_e03_sv.xma", 1 }, { "xenon/sound/voice/j/bos07_e04_eg.xma", 1 }, { "xenon/sound/voice/j/bos07_e05_eg.xma", 1 }, { "xenon/sound/voice/j/bos07_e07_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_a00_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_a01_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_a02_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_a03_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_a04_pr.xma", 1 }, { "xenon/sound/voice/j/bos08_a05_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_a06_pr.xma", 1 }, { "xenon/sound/voice/j/bos08_a07_pr.xma", 1 }, { "xenon/sound/voice/j/bos08_e00_1_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e00_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e00_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_e01_pr.xma", 1 }, { "xenon/sound/voice/j/bos08_e02_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e03_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e04_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e05_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e06_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e07_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e08_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e09_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e10_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e11_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e12_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e13_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e13_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_e14_sn.xma", 1 }, { "xenon/sound/voice/j/bos08_e15_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e16_eg.xma", 1 }, { "xenon/sound/voice/j/bos08_e17_eg.xma", 1 }, { "xenon/sound/voice/j/bos09_a00_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_a00_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_a01_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_a01_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_a02_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_a02_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e00_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e00_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e01_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e01_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e02_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e02_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e03_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e03_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e04_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e05_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e05_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e06_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e06_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e07_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e07_sv.xma", 1 }, { "xenon/sound/voice/j/bos09_e08_sn.xma", 1 }, { "xenon/sound/voice/j/bos09_e08_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_a00_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_a00_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_a01_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_a01_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_a02_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e00_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e00_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_e01_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e02_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e03_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e04_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_e05_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e05_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_e06_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e06_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_e07_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e08_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e08_sv.xma", 1 }, { "xenon/sound/voice/j/bos10_e09_sd.xma", 1 }, { "xenon/sound/voice/j/bos10_e09_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_a06_pr.xma", 1 }, { "xenon/sound/voice/j/bos11_a07_am.xma", 1 }, { "xenon/sound/voice/j/bos11_a07_om.xma", 1 }, { "xenon/sound/voice/j/bos11_a08_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_a09_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_a10_om.xma", 1 }, { "xenon/sound/voice/j/bos11_a11_am.xma", 1 }, { "xenon/sound/voice/j/bos11_a12_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_a13_1_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_a13_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_a14_tl.xma", 1 }, { "xenon/sound/voice/j/bos11_a15_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_a16_pr.xma", 1 }, { "xenon/sound/voice/j/bos11_a17_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_a18_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_a19_am.xma", 1 }, { "xenon/sound/voice/j/bos11_a20_tl.xma", 1 }, { "xenon/sound/voice/j/bos11_a21_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_a21ps_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_a22_om.xma", 1 }, { "xenon/sound/voice/j/bos11_a23_rg.xma", 1 }, { "xenon/sound/voice/j/bos11_e00_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_e00_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_e01_1_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_e01_2_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_e01_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_e01_rg.xma", 1 }, { "xenon/sound/voice/j/bos11_e02_am.xma", 1 }, { "xenon/sound/voice/j/bos11_e02_tl.xma", 1 }, { "xenon/sound/voice/j/bos11_e03_am.xma", 1 }, { "xenon/sound/voice/j/bos11_e03_rg.xma", 1 }, { "xenon/sound/voice/j/bos11_e03ps_rg.xma", 1 }, { "xenon/sound/voice/j/bos11_e04_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_e04_pr.xma", 1 }, { "xenon/sound/voice/j/bos11_e04ps_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_e05_eg.xma", 1 }, { "xenon/sound/voice/j/bos11_e05_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_e05ps_kn.xma", 1 }, { "xenon/sound/voice/j/bos11_e06_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e06_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e06_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_e07_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e07_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e07_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_e07ps_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e07ps_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e07ps_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_e08_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e08_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e08_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_e09_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e09_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e09_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_e10_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e10_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e10_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_e11_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e11_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e11_sv.xma", 1 }, { "xenon/sound/voice/j/bos11_e12_sd.xma", 1 }, { "xenon/sound/voice/j/bos11_e12_sn.xma", 1 }, { "xenon/sound/voice/j/bos11_e12_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_a01_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_a02_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_a02_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_a03_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_a04_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_a05_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_a06_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_a06_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_a07_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_a07_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_a08_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_a09_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_a10_bz.xma", 1 }, { "xenon/sound/voice/j/csc01_a11_bz.xma", 1 }, { "xenon/sound/voice/j/csc01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_e01_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e01_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_e04_bz.xma", 1 }, { "xenon/sound/voice/j/csc01_e04_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_e04_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_e05_bz.xma", 1 }, { "xenon/sound/voice/j/csc01_e05_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e05_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_e05_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_e06_bz.xma", 1 }, { "xenon/sound/voice/j/csc01_e06_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e06_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_e06_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_e07_bz.xma", 1 }, { "xenon/sound/voice/j/csc01_e07_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_e08_sv.xma", 1 }, { "xenon/sound/voice/j/csc01_e09_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_e09_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_w00_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_w01_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_w02_bz.xma", 1 }, { "xenon/sound/voice/j/csc01_w02_sn.xma", 1 }, { "xenon/sound/voice/j/csc01_w03_sd.xma", 1 }, { "xenon/sound/voice/j/csc01_w03_sv.xma", 1 }, { "xenon/sound/voice/j/dtd01_a00_pr.xma", 1 }, { "xenon/sound/voice/j/dtd01_a00_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_a01_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_a02_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_a03_am.xma", 1 }, { "xenon/sound/voice/j/dtd01_a03_pr.xma", 1 }, { "xenon/sound/voice/j/dtd01_a03_sv.xma", 1 }, { "xenon/sound/voice/j/dtd01_a04_sv.xma", 1 }, { "xenon/sound/voice/j/dtd01_a05_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_e00_am.xma", 1 }, { "xenon/sound/voice/j/dtd01_e00_pr.xma", 1 }, { "xenon/sound/voice/j/dtd01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_e01_pr.xma", 1 }, { "xenon/sound/voice/j/dtd01_e02_pr.xma", 1 }, { "xenon/sound/voice/j/dtd01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/dtd01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_e03_sv.xma", 1 }, { "xenon/sound/voice/j/dtd01_e04_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_e05_am.xma", 1 }, { "xenon/sound/voice/j/dtd01_e05_om.xma", 1 }, { "xenon/sound/voice/j/dtd01_e06_am.xma", 1 }, { "xenon/sound/voice/j/dtd01_e06_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_h00_so.xma", 1 }, { "xenon/sound/voice/j/dtd01_w00_pr.xma", 1 }, { "xenon/sound/voice/j/dtd01_w00_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/dtd01_w01_am.xma", 1 }, { "xenon/sound/voice/j/dtd01_w01_om.xma", 1 }, { "xenon/sound/voice/j/dtd01_w01_rg.xma", 1 }, { "xenon/sound/voice/j/dtd01_w01_sd.xma", 1 }, { "xenon/sound/voice/j/dtd01_w01_sv.xma", 1 }, { "xenon/sound/voice/j/end01_e00_am.xma", 1 }, { "xenon/sound/voice/j/end01_e00_kn.xma", 1 }, { "xenon/sound/voice/j/end01_e00_om.xma", 1 }, { "xenon/sound/voice/j/end01_e00_rg.xma", 1 }, { "xenon/sound/voice/j/end01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/end01_e00_sv.xma", 1 }, { "xenon/sound/voice/j/end01_e00_tl.xma", 1 }, { "xenon/sound/voice/j/end01_e01_am.xma", 1 }, { "xenon/sound/voice/j/end01_e01_kn.xma", 1 }, { "xenon/sound/voice/j/end01_e01_om.xma", 1 }, { "xenon/sound/voice/j/end01_e01_rg.xma", 1 }, { "xenon/sound/voice/j/end01_e01_sd.xma", 1 }, { "xenon/sound/voice/j/end01_e01_sv.xma", 1 }, { "xenon/sound/voice/j/end01_e01_tl.xma", 1 }, { "xenon/sound/voice/j/end01_e02_am.xma", 1 }, { "xenon/sound/voice/j/end01_e02_kn.xma", 1 }, { "xenon/sound/voice/j/end01_e02_rg.xma", 1 }, { "xenon/sound/voice/j/end01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/end01_e02_sv.xma", 1 }, { "xenon/sound/voice/j/end01_e02_tl.xma", 1 }, { "xenon/sound/voice/j/end01_e03_am.xma", 1 }, { "xenon/sound/voice/j/end01_e03_kn.xma", 1 }, { "xenon/sound/voice/j/end01_e03_om.xma", 1 }, { "xenon/sound/voice/j/end01_e03_rg.xma", 1 }, { "xenon/sound/voice/j/end01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/end01_e03_sv.xma", 1 }, { "xenon/sound/voice/j/end01_e03_tl.xma", 1 }, { "xenon/sound/voice/j/end01_e04_am.xma", 1 }, { "xenon/sound/voice/j/end01_e04_kn.xma", 1 }, { "xenon/sound/voice/j/end01_e04_om.xma", 1 }, { "xenon/sound/voice/j/end01_e04_rg.xma", 1 }, { "xenon/sound/voice/j/end01_e04_sd.xma", 1 }, { "xenon/sound/voice/j/end01_e04_sv.xma", 1 }, { "xenon/sound/voice/j/end01_e04_tl.xma", 1 }, { "xenon/sound/voice/j/end01_e05_am.xma", 1 }, { "xenon/sound/voice/j/end01_e05_kn.xma", 1 }, { "xenon/sound/voice/j/end01_e05_om.xma", 1 }, { "xenon/sound/voice/j/end01_e05_rg.xma", 1 }, { "xenon/sound/voice/j/end01_e05_sd.xma", 1 }, { "xenon/sound/voice/j/end01_e05_sv.xma", 1 }, { "xenon/sound/voice/j/end01_e05_tl.xma", 1 }, { "xenon/sound/voice/j/end01_h00_so.xma", 1 }, { "xenon/sound/voice/j/end01_h01_so.xma", 1 }, { "xenon/sound/voice/j/flc01_a00_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_a00_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_a00_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_a01_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_a01_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_a01_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_a02_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_a02_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_a03_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_a03_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_a03_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_a04_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_a04_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_a04_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_a05_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_a06_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_e01_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_e01_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_e01_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_e02_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_e03_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_e04_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_e04_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_e05_sv.xma", 1 }, { "xenon/sound/voice/j/flc01_h00_so.xma", 1 }, { "xenon/sound/voice/j/flc01_h01_so.xma", 1 }, { "xenon/sound/voice/j/flc01_h02_so.xma", 1 }, { "xenon/sound/voice/j/flc01_h03_so.xma", 1 }, { "xenon/sound/voice/j/flc01_h04_so.xma", 1 }, { "xenon/sound/voice/j/flc01_w00_bz.xma", 1 }, { "xenon/sound/voice/j/flc01_w00_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_w01_kn.xma", 1 }, { "xenon/sound/voice/j/flc01_w01_rg.xma", 1 }, { "xenon/sound/voice/j/flc01_w01_sd.xma", 1 }, { "xenon/sound/voice/j/flc01_w01_sn.xma", 1 }, { "xenon/sound/voice/j/flc01_w01_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a00_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_a00_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a01_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a02_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a03_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_a03_sn.xma", 1 }, { "xenon/sound/voice/j/kdv01_a03_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a04_rg.xma", 1 }, { "xenon/sound/voice/j/kdv01_a05_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a06_rg.xma", 1 }, { "xenon/sound/voice/j/kdv01_a06_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a07_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_a08_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_e01_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/kdv01_e02_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_e04_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_e05_sn.xma", 1 }, { "xenon/sound/voice/j/kdv01_e05_sv.xma", 1 }, { "xenon/sound/voice/j/kdv01_e06_sn.xma", 1 }, { "xenon/sound/voice/j/kdv01_e07_rg.xma", 1 }, { "xenon/sound/voice/j/kdv01_e08_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_e09_rg.xma", 1 }, { "xenon/sound/voice/j/kdv01_e09_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_e09_sn.xma", 1 }, { "xenon/sound/voice/j/kdv01_e10_rg.xma", 1 }, { "xenon/sound/voice/j/kdv01_e10_sn.xma", 1 }, { "xenon/sound/voice/j/kdv01_e11_rg.xma", 1 }, { "xenon/sound/voice/j/kdv01_h00_so.xma", 1 }, { "xenon/sound/voice/j/kdv01_h01_so.xma", 1 }, { "xenon/sound/voice/j/kdv01_h02_so.xma", 1 }, { "xenon/sound/voice/j/kdv01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/kdv01_w01_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_w02_rg.xma", 1 }, { "xenon/sound/voice/j/kdv01_w02_sd.xma", 1 }, { "xenon/sound/voice/j/kdv01_w02_sv.xma", 1 }, { "xenon/sound/voice/j/mis01_e00_am.xma", 1 }, { "xenon/sound/voice/j/mis01_e00_pr.xma", 1 }, { "xenon/sound/voice/j/mis01_e01_am.xma", 1 }, { "xenon/sound/voice/j/mis01_e01_pr.xma", 1 }, { "xenon/sound/voice/j/mis01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/mis01_e02_tl.xma", 1 }, { "xenon/sound/voice/j/mis01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/mis01_e03_tl.xma", 1 }, { "xenon/sound/voice/j/mis01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/mis01_e05_sn.xma", 1 }, { "xenon/sound/voice/j/mis01_e06_kn.xma", 1 }, { "xenon/sound/voice/j/mis01_e06_sn.xma", 1 }, { "xenon/sound/voice/j/mis01_e06_tl.xma", 1 }, { "xenon/sound/voice/j/mis01_e07_kn.xma", 1 }, { "xenon/sound/voice/j/mis01_e07_sn.xma", 1 }, { "xenon/sound/voice/j/mis01_e07_tl.xma", 1 }, { "xenon/sound/voice/j/mis01_e08_am.xma", 1 }, { "xenon/sound/voice/j/mis01_e08_sv.xma", 1 }, { "xenon/sound/voice/j/mis01_e09_am.xma", 1 }, { "xenon/sound/voice/j/mis01_e09_bz.xma", 1 }, { "xenon/sound/voice/j/mis01_e09_sv.xma", 1 }, { "xenon/sound/voice/j/mis01_e10_bz.xma", 1 }, { "xenon/sound/voice/j/mis01_e11_rg.xma", 1 }, { "xenon/sound/voice/j/mis01_e12_sd.xma", 1 }, { "xenon/sound/voice/j/mis01_e13_rg.xma", 1 }, { "xenon/sound/voice/j/mis01_e14_om.xma", 1 }, { "xenon/sound/voice/j/mis01_e15_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a00_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a00_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_a00_sv.xma", 1 }, { "xenon/sound/voice/j/rct01_a01_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a01_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_a02_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a03_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a04_sv.xma", 1 }, { "xenon/sound/voice/j/rct01_a05_sv.xma", 1 }, { "xenon/sound/voice/j/rct01_a06_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a06_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_a07_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a07_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_a07_sv.xma", 1 }, { "xenon/sound/voice/j/rct01_a08_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_a09_sv.xma", 1 }, { "xenon/sound/voice/j/rct01_a10_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a10_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_a11_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_a12_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_eg.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_hu.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_hu_a.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_hu_b.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_hu_c.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_hu_d.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_hu_e.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_hu_f.xma", 1 }, { "xenon/sound/voice/j/rct01_e01_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_e05_eg.xma", 1 }, { "xenon/sound/voice/j/rct01_e06_eg.xma", 1 }, { "xenon/sound/voice/j/rct01_e07_eg.xma", 1 }, { "xenon/sound/voice/j/rct01_e08_eg.xma", 1 }, { "xenon/sound/voice/j/rct01_e09_eg.xma", 1 }, { "xenon/sound/voice/j/rct01_e10_eg.xma", 1 }, { "xenon/sound/voice/j/rct01_h00_so.xma", 1 }, { "xenon/sound/voice/j/rct01_h01_so.xma", 1 }, { "xenon/sound/voice/j/rct01_h02_so.xma", 1 }, { "xenon/sound/voice/j/rct01_h03_so.xma", 1 }, { "xenon/sound/voice/j/rct01_w00_sd.xma", 1 }, { "xenon/sound/voice/j/rct01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/rct01_w00_sv.xma", 1 }, { "xenon/sound/voice/j/rct01_w01_sd.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_am.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_bz.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_kn.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_om.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_pr.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_rg.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_sd.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_sv.xma", 1 }, { "xenon/sound/voice/j/sys01_e00_tl.xma", 1 }, { "xenon/sound/voice/j/tpj01_a00_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a01_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_a01_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a02_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a03_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_a03_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a04_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_a04_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_a04_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a04_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a05_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_a05_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_a05_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a05_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a06_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_a06_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a07_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a08_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a09_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a10_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a11_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_a11_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a12_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_a12_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a12_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_a13_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_a13_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_a13_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_e01_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_e02_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_e03_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_e05_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_e06_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_e07_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_e08_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_e09_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_e10_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_e11_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_e12_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_e13_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_e14_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_e15_sv.xma", 1 }, { "xenon/sound/voice/j/tpj01_e16_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_e17_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_e18_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_e19_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_e20_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_h00_so.xma", 1 }, { "xenon/sound/voice/j/tpj01_h01_so.xma", 1 }, { "xenon/sound/voice/j/tpj01_h02_so.xma", 1 }, { "xenon/sound/voice/j/tpj01_h03_so.xma", 1 }, { "xenon/sound/voice/j/tpj01_h04_so.xma", 1 }, { "xenon/sound/voice/j/tpj01_w00_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_w01_pr.xma", 1 }, { "xenon/sound/voice/j/tpj01_w01_sn.xma", 1 }, { "xenon/sound/voice/j/tpj01_w02_rg.xma", 1 }, { "xenon/sound/voice/j/tpj01_w02_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_am.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_om.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a00_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_am.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_om.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a01_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_am.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_om.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a02_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_am.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_om.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a03_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_am.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_om.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a04_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_am.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_om.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a05_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_am.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_om.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_a06_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_e00_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e01_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e01ps_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e02_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_e04_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e05_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_e05_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e06_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e07_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e07ps_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e08_pr.xma", 1 }, { "xenon/sound/voice/j/twn01_e09_pr.xma", 1 }, { "xenon/sound/voice/j/twn01_e10_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_e11_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_e12_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_e13_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e14_an.xma", 1 }, { "xenon/sound/voice/j/twn01_e15_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e16_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e17_an.xma", 1 }, { "xenon/sound/voice/j/twn01_e18_kn.xma", 1 }, { "xenon/sound/voice/j/twn01_e18_sn.xma", 1 }, { "xenon/sound/voice/j/twn01_e18_tl.xma", 1 }, { "xenon/sound/voice/j/twn01_e19_1_an.xma", 1 }, { "xenon/sound/voice/j/twn01_e19_an.xma", 1 }, { "xenon/sound/voice/j/twn01_e20_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e21_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e22_am.xma", 1 }, { "xenon/sound/voice/j/twn01_e23_am.xma", 1 }, { "xenon/sound/voice/j/twn01_e24_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e25_am.xma", 1 }, { "xenon/sound/voice/j/twn01_e26_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e27_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e28_am.xma", 1 }, { "xenon/sound/voice/j/twn01_e29_am.xma", 1 }, { "xenon/sound/voice/j/twn01_e30_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_e31_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e32_bz.xma", 1 }, { "xenon/sound/voice/j/twn01_e33_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e34_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e35_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e36_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e37_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e38_an.xma", 1 }, { "xenon/sound/voice/j/twn01_e39_sv.xma", 1 }, { "xenon/sound/voice/j/twn01_e40_an.xma", 1 }, { "xenon/sound/voice/j/twn01_e41_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e42_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_e43_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_e44_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_e44ps_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_e45_1_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e45_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e46_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_e47_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e48_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e49_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_e50_1_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e50_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e51_sd.xma", 1 }, { "xenon/sound/voice/j/twn01_e52_gn.xma", 1 }, { "xenon/sound/voice/j/twn01_e53_rg.xma", 1 }, { "xenon/sound/voice/j/twn01_h00_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h01_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h02_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h03_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h04_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h05_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h06_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h07_1_so.xma", 1 }, { "xenon/sound/voice/j/twn01_h07_so.xma", 1 }, { "xenon/sound/voice/j/wap01_a00_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_a00_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_a00_tl.xma", 1 }, { "xenon/sound/voice/j/wap01_a01_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_a01_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_a02_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_a03_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_a03_tl.xma", 1 }, { "xenon/sound/voice/j/wap01_a03ps_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_a03ps_tl.xma", 1 }, { "xenon/sound/voice/j/wap01_a04_sv.xma", 1 }, { "xenon/sound/voice/j/wap01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_e01_bz.xma", 1 }, { "xenon/sound/voice/j/wap01_e01_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_e01_sv.xma", 1 }, { "xenon/sound/voice/j/wap01_e01_tl.xma", 1 }, { "xenon/sound/voice/j/wap01_e02_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_e03_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_e04_sv.xma", 1 }, { "xenon/sound/voice/j/wap01_e05_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_e06_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_e07_bz.xma", 1 }, { "xenon/sound/voice/j/wap01_e08_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_e09_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_e09_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_e10_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_e10_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_e11_1_eg.xma", 1 }, { "xenon/sound/voice/j/wap01_e11_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_e11_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_h00_so.xma", 1 }, { "xenon/sound/voice/j/wap01_h01_so.xma", 1 }, { "xenon/sound/voice/j/wap01_w00_bz.xma", 1 }, { "xenon/sound/voice/j/wap01_w00_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_w01_rg.xma", 1 }, { "xenon/sound/voice/j/wap01_w01_sd.xma", 1 }, { "xenon/sound/voice/j/wap01_w01_sn.xma", 1 }, { "xenon/sound/voice/j/wap01_w01_sv.xma", 1 }, { "xenon/sound/voice/j/wap01_w01_tl.xma", 1 }, { "xenon/sound/voice/j/wvo01_a00_tl.xma", 1 }, { "xenon/sound/voice/j/wvo01_a01_sd.xma", 1 }, { "xenon/sound/voice/j/wvo01_e00_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e01_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e02_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e03_om.xma", 1 }, { "xenon/sound/voice/j/wvo01_e03_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e03_tl.xma", 1 }, { "xenon/sound/voice/j/wvo01_e04_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e05_mf.xma", 1 }, { "xenon/sound/voice/j/wvo01_e05_tl.xma", 1 }, { "xenon/sound/voice/j/wvo01_e06_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e07_bz.xma", 1 }, { "xenon/sound/voice/j/wvo01_e07_sd.xma", 1 }, { "xenon/sound/voice/j/wvo01_e07_tl.xma", 1 }, { "xenon/sound/voice/j/wvo01_e08_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e08_tl.xma", 1 }, { "xenon/sound/voice/j/wvo01_e09_sd.xma", 1 }, { "xenon/sound/voice/j/wvo01_e09_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e10_eg.xma", 1 }, { "xenon/sound/voice/j/wvo01_e10_tl.xma", 1 }, { "xenon/sound/voice/j/wvo01_e11_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e12_bz.xma", 1 }, { "xenon/sound/voice/j/wvo01_e12_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_e13_sn.xma", 1 }, { "xenon/sound/voice/j/wvo01_h00_so.xma", 1 }, { "xenon/sound/voice/j/wvo01_h00ps_so.xma", 1 }, { "xenon/sound/voice/j/wvo01_w00_bz.xma", 1 }, { "xenon/sound/voice/j/wvo01_w00_om.xma", 1 }, { "xenon/sound/voice/j/wvo01_w00_rg.xma", 1 }, { "xenon/sound/voice/j/wvo01_w00_sd.xma", 1 }, { "xenon/sound/voice/j/wvo01_w00_tl.xma", 1 }, }; const size_t GameFilesSize = std::size(GameFiles); ================================================ FILE: MarathonRecomp/install/hashes/game.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t GameHashes[]; extern const std::pair GameFiles[]; extern const size_t GameFilesSize; ================================================ FILE: MarathonRecomp/install/hashes/mission_shadow.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t MissionShadowHashes[]; extern const std::pair MissionShadowFiles[]; extern const size_t MissionShadowFilesSize; const uint64_t MissionShadowHashes[] = { 14477816605833199221ULL, }; const std::pair MissionShadowFiles[] = { { "download.arc", 1 }, }; const size_t MissionShadowFilesSize = std::size(MissionShadowFiles); ================================================ FILE: MarathonRecomp/install/hashes/mission_shadow.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t MissionShadowHashes[]; extern const std::pair MissionShadowFiles[]; extern const size_t MissionShadowFilesSize; ================================================ FILE: MarathonRecomp/install/hashes/mission_silver.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t MissionSilverHashes[]; extern const std::pair MissionSilverFiles[]; extern const size_t MissionSilverFilesSize; const uint64_t MissionSilverHashes[] = { 7936137245680593367ULL, }; const std::pair MissionSilverFiles[] = { { "download.arc", 1 }, }; const size_t MissionSilverFilesSize = std::size(MissionSilverFiles); ================================================ FILE: MarathonRecomp/install/hashes/mission_silver.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t MissionSilverHashes[]; extern const std::pair MissionSilverFiles[]; extern const size_t MissionSilverFilesSize; ================================================ FILE: MarathonRecomp/install/hashes/mission_sonic.cpp ================================================ // File automatically generated by fshasher #include extern const uint64_t MissionSonicHashes[]; extern const std::pair MissionSonicFiles[]; extern const size_t MissionSonicFilesSize; const uint64_t MissionSonicHashes[] = { 11577193563383386051ULL, }; const std::pair MissionSonicFiles[] = { { "download.arc", 1 }, }; const size_t MissionSonicFilesSize = std::size(MissionSonicFiles); ================================================ FILE: MarathonRecomp/install/hashes/mission_sonic.h ================================================ // File automatically generated by fshasher #pragma once #include extern const uint64_t MissionSonicHashes[]; extern const std::pair MissionSonicFiles[]; extern const size_t MissionSonicFilesSize; ================================================ FILE: MarathonRecomp/install/installer.cpp ================================================ #include "installer.h" #include #include "directory_file_system.h" #include "iso_file_system.h" #include "xcontent_file_system.h" #include "hashes/episode_sonic.h" #include "hashes/episode_shadow.h" #include "hashes/episode_silver.h" #include "hashes/episode_amigo.h" #include "hashes/mission_sonic.h" #include "hashes/mission_shadow.h" #include "hashes/mission_silver.h" #include "hashes/game.h" static const std::string GameDirectory = "game"; static const std::string DLCDirectory = "dlc"; static const std::string EpisodeSonicDirectory = DLCDirectory + "/Additional Episode - Sonic Boss Attack"; static const std::string EpisodeShadowDirectory = DLCDirectory + "/Additional Episode - Shadow Boss Attack"; static const std::string EpisodeSilverDirectory = DLCDirectory + "/Additional Episode - Silver Boss Attack"; static const std::string EpisodeAmigoDirectory = DLCDirectory + "/Additional Episode - Team Attack Amigo"; static const std::string MissionSonicDirectory = DLCDirectory + "/Additional Mission Pack - Sonic Very Hard"; static const std::string MissionShadowDirectory = DLCDirectory + "/Additional Mission Pack - Shadow Very Hard"; static const std::string MissionSilverDirectory = DLCDirectory + "/Additional Mission Pack - Silver Very Hard"; static const std::string GameExecutableFile = "default.xex"; static const std::string DLCValidationFile = "download.arc"; static const std::string ISOExtension = ".iso"; static std::string fromU8(const std::u8string &str) { return std::string(str.begin(), str.end()); } static std::string fromPath(const std::filesystem::path &path) { return fromU8(path.u8string()); } static std::string toLower(std::string str) { std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); }); return str; }; static std::unique_ptr createFileSystemFromPath(const std::filesystem::path &path) { if (XContentFileSystem::check(path)) { return XContentFileSystem::create(path); } else if (toLower(fromPath(path.extension())) == ISOExtension) { return ISOFileSystem::create(path); } else if (std::filesystem::is_directory(path)) { return DirectoryFileSystem::create(path); } else { return nullptr; } } static bool checkFile(const FilePair &pair, const uint64_t *fileHashes, const std::filesystem::path &targetDirectory, std::vector &fileData, Journal &journal, const std::function &progressCallback, bool checkSizeOnly) { const std::string fileName(pair.first); const uint32_t hashCount = pair.second; const std::filesystem::path filePath = targetDirectory / fileName; if (!std::filesystem::exists(filePath)) { journal.lastResult = Journal::Result::FileMissing; journal.lastErrorMessage = fmt::format("File \"{}\" does not exist.", fileName); return false; } std::error_code ec; size_t fileSize = std::filesystem::file_size(filePath, ec); if (ec) { journal.lastResult = Journal::Result::FileReadFailed; journal.lastErrorMessage = fmt::format("Failed to read file size for \"{}\".", fileName); return false; } if (checkSizeOnly) { journal.progressTotal += fileSize; } else { std::ifstream fileStream(filePath, std::ios::binary); if (fileStream.is_open()) { fileData.resize(fileSize); fileStream.read((char *)(fileData.data()), fileSize); } if (!fileStream.is_open() || fileStream.bad()) { journal.lastResult = Journal::Result::FileReadFailed; journal.lastErrorMessage = fmt::format("Failed to read file \"{}\".", fileName); return false; } uint64_t fileHash = XXH3_64bits(fileData.data(), fileSize); bool fileHashFound = false; for (uint32_t i = 0; i < hashCount && !fileHashFound; i++) { fileHashFound = fileHash == fileHashes[i]; } if (!fileHashFound) { journal.lastResult = Journal::Result::FileHashFailed; journal.lastErrorMessage = fmt::format("File \"{}\" did not match any of the known hashes.", fileName); return false; } journal.progressCounter += fileSize; } if (!progressCallback()) { journal.lastResult = Journal::Result::Cancelled; journal.lastErrorMessage = "Check was cancelled."; return false; } return true; } static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, bool skipHashChecks, std::vector &fileData, Journal &journal, const std::function &progressCallback) { const std::string filename(pair.first); const uint32_t hashCount = pair.second; if (!sourceVfs.exists(filename)) { journal.lastResult = Journal::Result::FileMissing; journal.lastErrorMessage = fmt::format("File \"{}\" does not exist in \"{}\".", filename, sourceVfs.getName()); return false; } if (!sourceVfs.load(filename, fileData)) { journal.lastResult = Journal::Result::FileReadFailed; journal.lastErrorMessage = fmt::format("Failed to read file \"{}\" from \"{}\".", filename, sourceVfs.getName()); return false; } if (!skipHashChecks) { uint64_t fileHash = XXH3_64bits(fileData.data(), fileData.size()); bool fileHashFound = false; for (uint32_t i = 0; i < hashCount && !fileHashFound; i++) { fileHashFound = fileHash == fileHashes[i]; } if (!fileHashFound) { journal.lastResult = Journal::Result::FileHashFailed; journal.lastErrorMessage = fmt::format("File \"{}\" from \"{}\" did not match any of the known hashes.", filename, sourceVfs.getName()); return false; } } std::filesystem::path targetPath = targetDirectory / std::filesystem::path(std::u8string_view((const char8_t *)(pair.first))); std::filesystem::path parentPath = targetPath.parent_path(); if (!std::filesystem::exists(parentPath)) { std::error_code ec; std::filesystem::create_directories(parentPath, ec); } while (!parentPath.empty()) { journal.createdDirectories.insert(parentPath); if (parentPath != targetDirectory) { parentPath = parentPath.parent_path(); } else { parentPath = std::filesystem::path(); } } std::ofstream outStream(targetPath, std::ios::binary); if (!outStream.is_open()) { journal.lastResult = Journal::Result::FileCreationFailed; journal.lastErrorMessage = fmt::format("Failed to create file at \"{}\".", fromPath(targetPath)); return false; } journal.createdFiles.push_back(targetPath); outStream.write((const char *)(fileData.data()), fileData.size()); if (outStream.bad()) { journal.lastResult = Journal::Result::FileWriteFailed; journal.lastErrorMessage = fmt::format("Failed to create file at \"{}\".", fromPath(targetPath)); return false; } journal.progressCounter += fileData.size(); if (!progressCallback()) { journal.lastResult = Journal::Result::Cancelled; journal.lastErrorMessage = "Installation was cancelled."; return false; } return true; } static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem &sourceVfs, Journal &journal) { std::string name; std::ifstream dlcFile(sourcePath); dlcFile.seekg(0x412, std::ios::beg); char ch; while (dlcFile.get(ch) && ch != '\0') { // If we're reading an invalid file, don't keep reading // past the maximum length of a valid DLC file name if (name.length() > 41) { break; } name += ch; // DLC file names have one character proceeded by null, // so we skip every other byte to read a continuous string dlcFile.seekg(1, std::ios::cur); } dlcFile.close(); if (name == "Additional Episode \"Sonic Boss Attack\"") { return DLC::EpisodeSonic; } else if (name == "Additional Episode \"Shadow Boss Attack\"") { return DLC::EpisodeShadow; } else if (name == "Additional Episode \"Silver Boss Attack\"") { return DLC::EpisodeSilver; } else if (name == "Additional Episode \"Team Attack Amigo\"") { return DLC::EpisodeAmigo; } else if (name == "Additional Mission Pack \"Sonic/Very Hard") { return DLC::MissionSonic; } else if (name == "Additional Mission Pack \"Shadow/Very Har") { return DLC::MissionShadow; } else if (name == "Additional Mission Pack \"Silver/Very Har") { return DLC::MissionSilver; } journal.lastResult = Journal::Result::UnknownDLCType; journal.lastErrorMessage = fmt::format("DLC type for \"{}\" is unknown.", name); return DLC::Unknown; } static bool fillDLCSource(DLC dlc, Installer::DLCSource &dlcSource) { switch (dlc) { case DLC::EpisodeSonic: dlcSource.filePairs = { EpisodeSonicFiles, EpisodeSonicFilesSize }; dlcSource.fileHashes = EpisodeSonicHashes; dlcSource.targetSubDirectory = EpisodeSonicDirectory; return true; case DLC::EpisodeShadow: dlcSource.filePairs = { EpisodeShadowFiles, EpisodeShadowFilesSize }; dlcSource.fileHashes = EpisodeShadowHashes; dlcSource.targetSubDirectory = EpisodeShadowDirectory; return true; case DLC::EpisodeSilver: dlcSource.filePairs = { EpisodeSilverFiles, EpisodeSilverFilesSize }; dlcSource.fileHashes = EpisodeSilverHashes; dlcSource.targetSubDirectory = EpisodeSilverDirectory; return true; case DLC::EpisodeAmigo: dlcSource.filePairs = { EpisodeAmigoFiles, EpisodeAmigoFilesSize }; dlcSource.fileHashes = EpisodeAmigoHashes; dlcSource.targetSubDirectory = EpisodeAmigoDirectory; return true; case DLC::MissionSonic: dlcSource.filePairs = { MissionSonicFiles, MissionSonicFilesSize }; dlcSource.fileHashes = MissionSonicHashes; dlcSource.targetSubDirectory = MissionSonicDirectory; return true; case DLC::MissionShadow: dlcSource.filePairs = { MissionShadowFiles, MissionShadowFilesSize }; dlcSource.fileHashes = MissionShadowHashes; dlcSource.targetSubDirectory = MissionShadowDirectory; return true; case DLC::MissionSilver: dlcSource.filePairs = { MissionSilverFiles, MissionSilverFilesSize }; dlcSource.fileHashes = MissionSilverHashes; dlcSource.targetSubDirectory = MissionSilverDirectory; return true; default: return false; } } bool Installer::checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath) { modulePath = baseDirectory / GameDirectory / GameExecutableFile; if (!std::filesystem::exists(modulePath)) return false; return true; } bool Installer::checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc) { switch (dlc) { case DLC::EpisodeSonic: return std::filesystem::exists(baseDirectory / EpisodeSonicDirectory / DLCValidationFile); case DLC::EpisodeShadow: return std::filesystem::exists(baseDirectory / EpisodeShadowDirectory / DLCValidationFile); case DLC::EpisodeSilver: return std::filesystem::exists(baseDirectory / EpisodeSilverDirectory / DLCValidationFile); case DLC::EpisodeAmigo: return std::filesystem::exists(baseDirectory / EpisodeAmigoDirectory / DLCValidationFile); case DLC::MissionSonic: return std::filesystem::exists(baseDirectory / MissionSonicDirectory / DLCValidationFile); case DLC::MissionShadow: return std::filesystem::exists(baseDirectory / MissionShadowDirectory / DLCValidationFile); case DLC::MissionSilver: return std::filesystem::exists(baseDirectory / MissionSilverDirectory / DLCValidationFile); default: return false; } } bool Installer::checkAllDLC(const std::filesystem::path& baseDirectory) { bool result = true; for (int i = 1; i < (int)DLC::Count; i++) { if (!checkDLCInstall(baseDirectory, (DLC)i)) result = false; } return result; } bool Installer::checkInstallIntegrity(const std::filesystem::path &baseDirectory, Journal &journal, const std::function &progressCallback) { // Run the file checks twice: once to fill out the progress counter and the file sizes, and another pass to do the hash integrity checks. for (uint32_t checkPass = 0; checkPass < 2; checkPass++) { bool checkSizeOnly = (checkPass == 0); if (!checkFiles({ GameFiles, GameFilesSize }, GameHashes, baseDirectory / GameDirectory, journal, progressCallback, checkSizeOnly)) { return false; } for (int i = 1; i < (int)DLC::Count; i++) { if (checkDLCInstall(baseDirectory, (DLC)i)) { Installer::DLCSource dlcSource; fillDLCSource((DLC)i, dlcSource); if (!checkFiles(dlcSource.filePairs, dlcSource.fileHashes, baseDirectory / dlcSource.targetSubDirectory, journal, progressCallback, checkSizeOnly)) { return false; } } } } return true; } bool Installer::computeTotalSize(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize) { for (FilePair pair : filePairs) { const std::string filename(pair.first); if (!sourceVfs.exists(filename)) { journal.lastResult = Journal::Result::FileMissing; journal.lastErrorMessage = fmt::format("File \"{}\" does not exist in \"{}\".", filename, sourceVfs.getName()); return false; } totalSize += sourceVfs.getSize(filename); } return true; } bool Installer::checkFiles(std::span filePairs, const uint64_t *fileHashes, const std::filesystem::path &targetDirectory, Journal &journal, const std::function &progressCallback, bool checkSizeOnly) { FilePair validationPair = {}; uint32_t validationHashIndex = 0; uint32_t hashIndex = 0; uint32_t hashCount = 0; std::vector fileData; for (FilePair pair : filePairs) { hashIndex = hashCount; hashCount += pair.second; if (!checkFile(pair, &fileHashes[hashIndex], targetDirectory, fileData, journal, progressCallback, checkSizeOnly)) { return false; } } return true; } bool Installer::copyFiles(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function &progressCallback) { std::error_code ec; if (!std::filesystem::exists(targetDirectory) && !std::filesystem::create_directories(targetDirectory, ec)) { journal.lastResult = Journal::Result::DirectoryCreationFailed; journal.lastErrorMessage = "Unable to create directory at \"" + fromPath(targetDirectory) + "\"."; return false; } uint32_t hashIndex = 0; uint32_t hashCount = 0; std::vector fileData; for (FilePair pair : filePairs) { hashIndex = hashCount; hashCount += pair.second; if (!copyFile(pair, &fileHashes[hashIndex], sourceVfs, targetDirectory, skipHashChecks, fileData, journal, progressCallback)) { return false; } } return true; } bool Installer::parseContent(const std::filesystem::path &sourcePath, std::unique_ptr &targetVfs, Journal &journal) { targetVfs = createFileSystemFromPath(sourcePath); if (targetVfs != nullptr) { return true; } else { journal.lastResult = Journal::Result::VirtualFileSystemFailed; journal.lastErrorMessage = "Unable to open \"" + fromPath(sourcePath) + "\"."; return false; } } bool Installer::parseSources(const Input &input, Journal &journal, Sources &sources) { journal = Journal(); sources = Sources(); // Parse the contents of the base game. if (!input.gameSource.empty()) { if (!parseContent(input.gameSource, sources.game, journal)) { return false; } if (!computeTotalSize({ GameFiles, GameFilesSize }, GameHashes, *sources.game, journal, sources.totalSize)) { return false; } } // Parse the contents of the DLC Packs. for (const auto &path : input.dlcSources) { sources.dlc.emplace_back(); DLCSource &dlcSource = sources.dlc.back(); if (!parseContent(path, dlcSource.sourceVfs, journal)) { return false; } DLC dlc = detectDLC(path, *dlcSource.sourceVfs, journal); if (!fillDLCSource(dlc, dlcSource)) { return false; } if (!computeTotalSize(dlcSource.filePairs, dlcSource.fileHashes, *dlcSource.sourceVfs, journal, sources.totalSize)) { return false; } } // Add the total size in bytes as the journal progress. journal.progressTotal += sources.totalSize; return true; } bool Installer::install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, std::chrono::seconds endWaitTime, const std::function &progressCallback) { // Install files in reverse order of importance. In case of a process crash or power outage, this will increase the likelihood of the installation // missing critical files required for the game to run. These files are used as the way to detect if the game is installed. // Install the DLC. if (!sources.dlc.empty()) { journal.createdDirectories.insert(targetDirectory / DLCDirectory); } for (const DLCSource &dlcSource : sources.dlc) { if (!copyFiles(dlcSource.filePairs, dlcSource.fileHashes, *dlcSource.sourceVfs, targetDirectory / dlcSource.targetSubDirectory, DLCValidationFile, skipHashChecks, journal, progressCallback)) { return false; } } // If no game was specified, we're finished. This means the user was only installing the DLC. if ((sources.game == nullptr)) { return true; } // Install the base game. if (!copyFiles({ GameFiles, GameFilesSize }, GameHashes, *sources.game, targetDirectory / GameDirectory, GameExecutableFile, skipHashChecks, journal, progressCallback)) { return false; } for (uint32_t i = 0; i < 2; i++) { if (!progressCallback()) { journal.lastResult = Journal::Result::Cancelled; journal.lastErrorMessage = "Installation was cancelled."; return false; } if (i == 0) { // Wait the specified amount of time to allow the consumer of the callbacks to animate, halt or cancel the installation for a while after it's finished. std::this_thread::sleep_for(endWaitTime); } } return true; } void Installer::rollback(Journal &journal) { std::error_code ec; for (const auto &path : journal.createdFiles) { std::filesystem::remove(path, ec); } for (auto it = journal.createdDirectories.rbegin(); it != journal.createdDirectories.rend(); it++) { std::filesystem::remove(*it, ec); } } bool Installer::parseGame(const std::filesystem::path &sourcePath) { std::unique_ptr sourceVfs = createFileSystemFromPath(sourcePath); if (sourceVfs == nullptr) { return false; } return sourceVfs->exists(GameExecutableFile); } DLC Installer::parseDLC(const std::filesystem::path &sourcePath) { Journal journal; std::unique_ptr sourceVfs = createFileSystemFromPath(sourcePath); if (sourceVfs == nullptr) { return DLC::Unknown; } return detectDLC(sourcePath, *sourceVfs, journal); } ================================================ FILE: MarathonRecomp/install/installer.h ================================================ #pragma once #include #include #include "virtual_file_system.h" enum class DLC { Unknown, EpisodeSonic, EpisodeShadow, EpisodeSilver, EpisodeAmigo, MissionSonic, MissionShadow, MissionSilver, Count = MissionSilver }; struct Journal { enum class Result { Success, Cancelled, VirtualFileSystemFailed, DirectoryCreationFailed, FileMissing, FileReadFailed, FileHashFailed, FileCreationFailed, FileWriteFailed, ValidationFileMissing, DLCParsingFailed, UnknownDLCType }; uint64_t progressCounter = 0; uint64_t progressTotal = 0; std::list createdFiles; std::set createdDirectories; Result lastResult = Result::Success; std::string lastErrorMessage; }; using FilePair = std::pair; struct Installer { struct Input { std::filesystem::path gameSource; std::list dlcSources; }; struct DLCSource { std::unique_ptr sourceVfs; std::span filePairs; const uint64_t *fileHashes = nullptr; std::string targetSubDirectory; }; struct Sources { std::unique_ptr game; std::vector dlc; uint64_t totalSize = 0; }; static bool checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath); static bool checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc); static bool checkAllDLC(const std::filesystem::path &baseDirectory); static bool checkInstallIntegrity(const std::filesystem::path &baseDirectory, Journal &journal, const std::function &progressCallback); static bool computeTotalSize(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize); static bool checkFiles(std::span filePairs, const uint64_t *fileHashes, const std::filesystem::path &targetDirectory, Journal &journal, const std::function &progressCallback, bool checkSizeOnly); static bool copyFiles(std::span filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function &progressCallback); static bool parseContent(const std::filesystem::path &sourcePath, std::unique_ptr &targetVfs, Journal &journal); static bool parseSources(const Input &input, Journal &journal, Sources &sources); static bool install(const Sources &sources, const std::filesystem::path &targetDirectory, bool skipHashChecks, Journal &journal, std::chrono::seconds endWaitTime, const std::function &progressCallback); static void rollback(Journal &journal); // Convenience method for checking if the specified file contains the game. This should be used when the user selects the file. static bool parseGame(const std::filesystem::path &sourcePath); // Convenience method for the installer to check which DLC the file that was specified corresponds to. This should be used when the user selects the file. static DLC parseDLC(const std::filesystem::path &sourcePath); }; ================================================ FILE: MarathonRecomp/install/iso_file_system.cpp ================================================ // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/disc_image_device.cc /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2023 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "iso_file_system.h" #include ISOFileSystem::ISOFileSystem(const std::filesystem::path &isoPath) { mappedFile.open(isoPath); if (!mappedFile.isOpen()) { return; } name = (const char *)(isoPath.filename().u8string().data()); // Find root sector. const uint8_t *mappedFileData = mappedFile.data(); uint32_t gameOffset = 0; const size_t XeSectorSize = 2048; static const size_t PossibleOffsets[] = { 0x00000000, 0x0000FB20, 0x00020600, 0x02080000, 0x0FD90000, }; bool magicFound = false; const char RefMagic[] = "MICROSOFT*XBOX*MEDIA"; for (size_t i = 0; i < std::size(PossibleOffsets); i++) { size_t fileOffset = PossibleOffsets[i] + (32 * XeSectorSize); if ((fileOffset + strlen(RefMagic)) > mappedFile.size()) { continue; } if (std::memcmp(&mappedFileData[fileOffset], RefMagic, strlen(RefMagic)) == 0) { gameOffset = PossibleOffsets[i]; magicFound = true; } } size_t rootInfoOffset = gameOffset + (32 * XeSectorSize) + 20; if (!magicFound || (rootInfoOffset + 8) > mappedFile.size()) { mappedFile.close(); return; } // Parse root information. uint32_t rootSector = *(uint32_t *)(&mappedFileData[rootInfoOffset + 0]); uint32_t rootSize = *(uint32_t *)(&mappedFileData[rootInfoOffset + 4]); size_t rootOffset = gameOffset + (rootSector * XeSectorSize); const uint32_t MinRootSize = 13; const uint32_t MaxRootSize = 32 * 1024 * 1024; if ((rootSize < MinRootSize) || (rootSize > MaxRootSize)) { mappedFile.close(); return; } struct IterationStep { std::string fileNameBase; size_t nodeOffset = 0; size_t entryOffset = 0; IterationStep() = default; IterationStep(std::string fileNameBase, size_t nodeOffset, size_t entryOffset) : fileNameBase(fileNameBase), nodeOffset(nodeOffset), entryOffset(entryOffset) { } }; std::stack iterationStack; iterationStack.emplace("", rootOffset, 0); IterationStep step; uint16_t nodeL, nodeR; uint32_t sector, length; uint8_t attributes, nameLength; char fileName[256]; const uint8_t FileAttributeDirectory = 0x10; while (!iterationStack.empty()) { step = iterationStack.top(); iterationStack.pop(); size_t infoOffset = step.nodeOffset + step.entryOffset; if ((infoOffset + 14) > mappedFile.size()) { mappedFile.close(); return; } nodeL = *(uint16_t *)(&mappedFileData[infoOffset + 0]); nodeR = *(uint16_t *)(&mappedFileData[infoOffset + 2]); sector = *(uint32_t *)(&mappedFileData[infoOffset + 4]); length = *(uint32_t *)(&mappedFileData[infoOffset + 8]); attributes = *(uint8_t *)(&mappedFileData[infoOffset + 12]); nameLength = *(uint8_t *)(&mappedFileData[infoOffset + 13]); size_t nameOffset = infoOffset + 14; if ((nameOffset + nameLength) > mappedFile.size()) { mappedFile.close(); return; } memcpy(fileName, &mappedFileData[nameOffset], nameLength); fileName[nameLength] = '\0'; if (nodeL) { iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeL * 4); } if (nodeR) { iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeR * 4); } std::string fileNameUTF8 = step.fileNameBase + fileName; if (attributes & FileAttributeDirectory) { if (length > 0) { iterationStack.emplace(fileNameUTF8 + "/", gameOffset + sector * XeSectorSize, 0); } } else { fileMap[fileNameUTF8] = { gameOffset + sector * XeSectorSize, length}; } } } bool ISOFileSystem::load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const { auto it = fileMap.find(path); if (it != fileMap.end()) { if (fileDataMaxByteCount < std::get<1>(it->second)) { return false; } const uint8_t *mappedFileData = mappedFile.data(); memcpy(fileData, &mappedFileData[std::get<0>(it->second)], std::get<1>(it->second)); return true; } else { return false; } } size_t ISOFileSystem::getSize(const std::string &path) const { auto it = fileMap.find(path); if (it != fileMap.end()) { return std::get<1>(it->second); } else { return 0; } } bool ISOFileSystem::exists(const std::string &path) const { return fileMap.find(path) != fileMap.end(); } const std::string &ISOFileSystem::getName() const { return name; } bool ISOFileSystem::empty() const { return !mappedFile.isOpen(); } std::unique_ptr ISOFileSystem::create(const std::filesystem::path &isoPath) { std::unique_ptr isoFs = std::make_unique(isoPath); if (!isoFs->empty()) { return isoFs; } else { return nullptr; } } ================================================ FILE: MarathonRecomp/install/iso_file_system.h ================================================ // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/disc_image_device.cc /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2023 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #pragma once #include #include #include "virtual_file_system.h" #include struct ISOFileSystem : VirtualFileSystem { MemoryMappedFile mappedFile; std::map> fileMap; std::string name; ISOFileSystem(const std::filesystem::path &isoPath); bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override; size_t getSize(const std::string &path) const override; bool exists(const std::string &path) const override; const std::string &getName() const override; bool empty() const; static std::unique_ptr create(const std::filesystem::path &isoPath); }; ================================================ FILE: MarathonRecomp/install/update_checker.cpp ================================================ #include "update_checker.h" #include #include #include "version.h" #ifdef WIN32 #include #endif #include // UpdateChecker using json = nlohmann::json; static const char *CHECK_URL = "https://api.github.com/repos/sonicnext-dev/MarathonRecomp/releases/latest"; static const char *VISIT_URL = "https://github.com/sonicnext-dev/MarathonRecomp/releases/latest"; static const char *USER_AGENT = "MarathonRecomp-Agent"; static std::atomic g_updateCheckerInProgress = false; static std::atomic g_updateCheckerFinished = false; static UpdateChecker::Result g_updateCheckerResult = UpdateChecker::Result::NotStarted; size_t updateCheckerWriteCallback(void *contents, size_t size, size_t nmemb, std::string *output) { size_t totalSize = size * nmemb; output->append((char *)contents, totalSize); return totalSize; } static bool parseVersion(const std::string &versionStr, int &major, int &minor, int &revision) { size_t start = 0; if (versionStr[0] == 'v') { start = 1; } size_t firstDot = versionStr.find('.', start); size_t secondDot = versionStr.find('.', firstDot + 1); if (firstDot == std::string::npos || secondDot == std::string::npos) { return false; } try { major = std::stoi(versionStr.substr(start, firstDot - start)); minor = std::stoi(versionStr.substr(firstDot + 1, secondDot - firstDot - 1)); revision = std::stoi(versionStr.substr(secondDot + 1)); } catch (const std::exception &e) { LOGF_ERROR("Error while parsing version: {}.", e.what()); return false; } return true; } void updateCheckerThread() { CURL *curl = curl_easy_init(); CURLcode res; int major, minor, revision; std::string response; curl_easy_setopt(curl, CURLOPT_URL, CHECK_URL); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, updateCheckerWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT); res = curl_easy_perform(curl); if (res == CURLE_OK) { try { json root = json::parse(response); auto tag_name_element = root.find("tag_name"); if (tag_name_element != root.end() && tag_name_element->is_string()) { if (parseVersion(*tag_name_element, major, minor, revision)) { if ((g_versionMajor < major) || (g_versionMajor == major && g_versionMinor < minor) || (g_versionMajor == major && g_versionMinor == minor && g_versionRevision < revision)) { g_updateCheckerResult = UpdateChecker::Result::UpdateAvailable; } else { g_updateCheckerResult = UpdateChecker::Result::AlreadyUpToDate; } } else { LOG_ERROR("Error while parsing response: tag_name does not contain a valid version string."); g_updateCheckerResult = UpdateChecker::Result::Failed; } } else { LOG_ERROR("Error while parsing response: tag_name not found or not the right type."); g_updateCheckerResult = UpdateChecker::Result::Failed; } } catch (const json::exception &e) { LOGF_ERROR("Error while parsing response: {}", e.what()); g_updateCheckerResult = UpdateChecker::Result::Failed; } } else { LOGF_ERROR("Error while performing request: {}", curl_easy_strerror(res)); g_updateCheckerResult = UpdateChecker::Result::Failed; } curl_easy_cleanup(curl); g_updateCheckerFinished = true; g_updateCheckerInProgress = false; } void UpdateChecker::initialize() { curl_global_init(CURL_GLOBAL_DEFAULT); } bool UpdateChecker::start() { if (g_updateCheckerInProgress) { return false; } g_updateCheckerInProgress = true; g_updateCheckerFinished = false; std::thread thread(&updateCheckerThread); thread.detach(); return true; } UpdateChecker::Result UpdateChecker::check() { if (g_updateCheckerFinished) { return g_updateCheckerResult; } else if (g_updateCheckerInProgress) { return UpdateChecker::Result::InProgress; } else { return UpdateChecker::Result::NotStarted; } } void UpdateChecker::visitWebsite() { #if defined(WIN32) ShellExecuteA(0, 0, VISIT_URL, 0, 0, SW_SHOW); #elif defined(__linux__) std::string command = "xdg-open " + std::string(VISIT_URL) + " &"; std::system(command.c_str()); #elif defined(__APPLE__) std::string command = "open " + std::string(VISIT_URL) + " &"; std::system(command.c_str()); #else static_assert(false, "Visit website not implemented for this platform."); #endif } ================================================ FILE: MarathonRecomp/install/update_checker.h ================================================ #pragma once struct UpdateChecker { enum class Result { NotStarted, InProgress, AlreadyUpToDate, UpdateAvailable, Failed }; static void initialize(); static bool start(); static Result check(); static void visitWebsite(); }; ================================================ FILE: MarathonRecomp/install/virtual_file_system.h ================================================ #pragma once #include #include struct VirtualFileSystem { virtual ~VirtualFileSystem() { }; virtual bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const = 0; virtual size_t getSize(const std::string &path) const = 0; virtual bool exists(const std::string &path) const = 0; virtual const std::string &getName() const = 0; // Concrete implementation shortcut. bool load(const std::string &path, std::vector &fileData) { size_t fileDataSize = getSize(path); if (fileDataSize == 0) { return false; } fileData.resize(fileDataSize); return load(path, fileData.data(), fileDataSize); } }; ================================================ FILE: MarathonRecomp/install/xcontent_file_system.cpp ================================================ // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/xcontent_container_device.cc /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2023 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xcontent_file_system.h" #include #include #include enum class XContentPackageType { CON = 0x434F4E20, PIRS = 0x50495253, LIVE = 0x4C495645, }; struct XContentLicense { be licenseId; be licenseBits; be licenseFlags; }; #pragma pack(push, 1) struct XContentHeader { be magic; uint8_t signature[0x228]; XContentLicense licenses[0x10]; uint8_t contentId[0x14]; be headerSize; }; static_assert(sizeof(XContentHeader) == 0x344); struct StfsVolumeDescriptor { uint8_t descriptorLength; uint8_t version; union { uint8_t asByte; struct { uint8_t readOnlyFormat : 1; uint8_t rootActiveIndex : 1; uint8_t directoryOverallocated : 1; uint8_t directoryIndexBoundsValid : 1; } bits; } flags; uint16_t fileTableBlockCount; uint8_t fileTableBlockNumberRaw[3]; uint8_t topHashTableHash[0x14]; be totalBlockCount; be freeBlockCount; }; static_assert(sizeof(StfsVolumeDescriptor) == 0x24); struct StfsDirectoryEntry { char name[40]; struct { uint8_t nameLength : 6; uint8_t contiguous : 1; uint8_t directory : 1; } flags; uint8_t validDataBlocksRaw[3]; uint8_t allocatedDataBlocksRaw[3]; uint8_t startBlockNumberRaw[3]; be directoryIndex; be length; be createDate; be createTime; be modifiedDate; be modifiedTime; }; static_assert(sizeof(StfsDirectoryEntry) == 0x40); struct StfsDirectoryBlock { StfsDirectoryEntry entries[0x40]; }; static_assert(sizeof(StfsDirectoryBlock) == 0x1000); struct StfsHashEntry { uint8_t sha1[0x14]; be infoRaw; }; static_assert(sizeof(StfsHashEntry) == 0x18); struct StfsHashTable { StfsHashEntry entries[170]; be numBlocks; uint8_t padding[12]; }; static_assert(sizeof(StfsHashTable) == 0x1000); struct SvodDeviceDescriptor { uint8_t descriptorLength; uint8_t blockCacheElementCount; uint8_t workerThreadProcessor; uint8_t workerThreadPriority; uint8_t firstFragmentHashEntry[0x14]; union { uint8_t asByte; struct { uint8_t mustBeZeroForFutureUsage : 6; uint8_t enhancedGdfLayout : 1; uint8_t zeroForDownlevelClients : 1; } bits; } features; uint8_t numDataBlocksRaw[3]; uint8_t startDataBlockRaw[3]; uint8_t reserved[5]; }; static_assert(sizeof(SvodDeviceDescriptor) == 0x24); struct SvodDirectoryEntry { uint16_t nodeL; uint16_t nodeR; uint32_t dataBlock; uint32_t length; uint8_t attributes; uint8_t nameLength; }; static_assert(sizeof(SvodDirectoryEntry) == 0xE); struct XContentMetadata { be contentType; be metadataVersion; be contentSize; uint8_t executionInfo[24]; uint8_t consoleId[5]; be profileId; union { StfsVolumeDescriptor stfsVolumeDescriptor; SvodDeviceDescriptor svodDeviceDescriptor; }; be dataFileCount; be dataFileSize; be volumeType; be onlineCreator; be category; }; static_assert(sizeof(XContentMetadata) == 0x75); #pragma pack(pop) struct XContentContainerHeader { XContentHeader contentHeader; XContentMetadata contentMetadata; }; const uint32_t StfsBlockSize = 0x1000; const uint32_t StfsBlocksHashLevelAmount = 3; const uint32_t StfsBlocksPerHashLevel[StfsBlocksHashLevelAmount] = { 170, 28900, 4913000 }; const uint32_t StfsEndOfChain = 0xFFFFFF; const uint32_t StfsEntriesPerDirectoryBlock = StfsBlockSize / sizeof(StfsDirectoryEntry); uint32_t parseUint24(const uint8_t *bytes) { return bytes[0] | (bytes[1] << 8U) | (bytes[2] << 16U); } size_t blockIndexToOffset(uint64_t baseOffset, uint64_t blockIndex) { uint64_t block = blockIndex; for (uint32_t i = 0; i < StfsBlocksHashLevelAmount; i++) { uint32_t levelBase = StfsBlocksPerHashLevel[i]; block += ((blockIndex + levelBase) / levelBase); if (blockIndex < levelBase) { break; } } return baseOffset + (block << 12); } uint32_t blockIndexToHashBlockNumber(uint32_t blockIndex) { if (blockIndex < StfsBlocksPerHashLevel[0]) { return 0; } uint32_t block = (blockIndex / StfsBlocksPerHashLevel[0]) * (StfsBlocksPerHashLevel[0] + 1); block += ((blockIndex / StfsBlocksPerHashLevel[1]) + 1); if (blockIndex < StfsBlocksPerHashLevel[1]) { return block; } return block + 1; } size_t blockIndexToHashBlockOffset(uint64_t baseOffset, uint32_t blockIndex) { size_t blockNumber = blockIndexToHashBlockNumber(blockIndex); return baseOffset + (blockNumber << 12); } const StfsHashEntry *hashEntryFromBlockIndex(const uint8_t *fileData, uint64_t baseOffset, uint64_t blockIndex) { size_t hashOffset = blockIndexToHashBlockOffset(baseOffset, blockIndex); const StfsHashTable *hashTable = (const StfsHashTable *)(&fileData[hashOffset]); return &hashTable->entries[blockIndex % StfsBlocksPerHashLevel[0]]; } void blockToOffsetAndFile(SvodLayoutType svodLayoutType, size_t svodStartDataBlock, size_t svodBaseOffset, size_t block, size_t &outOffset, size_t &outFileIndex) { const size_t BlockSize = 0x800; const size_t HashBlockSize = 0x1000; const size_t BlocksPerL0Hash = 0x198; const size_t HashesPerL1Hash = 0xA1C4; const size_t BlocksPerFile = 0x14388; const size_t MaxFileSize = 0xA290000; size_t trueBlock = block - (svodStartDataBlock * 2); if (svodLayoutType == SvodLayoutType::EnhancedGDF) { trueBlock += 0x2; } size_t fileBlock = trueBlock % BlocksPerFile; outFileIndex = trueBlock / BlocksPerFile; size_t offset = 0; size_t level0TableCount = (fileBlock / BlocksPerL0Hash) + 1; offset += level0TableCount * HashBlockSize; size_t level1TableCount = (level0TableCount / HashesPerL1Hash) + 1; offset += level1TableCount * HashBlockSize; if (svodLayoutType == SvodLayoutType::SingleFile) { offset += svodBaseOffset; } outOffset = (fileBlock * BlockSize) + offset; if (outOffset >= MaxFileSize) { outOffset = (outOffset % MaxFileSize) + 0x2000; outFileIndex++; } } XContentFileSystem::XContentFileSystem(const std::filesystem::path &contentPath) { mappedFiles.emplace_back(); MemoryMappedFile &rootMappedFile = mappedFiles.back(); rootMappedFile.open(contentPath); if (!rootMappedFile.isOpen()) { return; } name = (const char *)(contentPath.filename().u8string().data()); const uint8_t *rootMappedFileData = rootMappedFile.data(); if (sizeof(XContentContainerHeader) > rootMappedFile.size()) { mappedFiles.clear(); return; } XContentContainerHeader contentContainerHeader = *(const XContentContainerHeader *)(rootMappedFileData); XContentPackageType packageType = XContentPackageType(contentContainerHeader.contentHeader.magic.get()); if (packageType != XContentPackageType::CON && packageType != XContentPackageType::LIVE && packageType != XContentPackageType::PIRS) { mappedFiles.clear(); return; } const XContentMetadata &metadata = contentContainerHeader.contentMetadata; volumeType = XContentVolumeType(metadata.volumeType.get()); if (volumeType == XContentVolumeType::STFS) { const StfsVolumeDescriptor &descriptor = metadata.stfsVolumeDescriptor; if (descriptor.descriptorLength != sizeof(StfsVolumeDescriptor) || !descriptor.flags.bits.readOnlyFormat) { mappedFiles.clear(); return; } baseOffset = ((contentContainerHeader.contentHeader.headerSize + StfsBlockSize - 1) / StfsBlockSize) * StfsBlockSize; uint32_t entryCount = 0; uint32_t tableBlockIndex = parseUint24(descriptor.fileTableBlockNumberRaw); uint32_t tableBlockCount = descriptor.fileTableBlockCount; std::map directoryNames; for (uint32_t i = 0; i < tableBlockCount; i++) { size_t offset = blockIndexToOffset(baseOffset, tableBlockIndex); if (offset + sizeof(StfsDirectoryBlock) > rootMappedFile.size()) { mappedFiles.clear(); return; } StfsDirectoryBlock *directoryBlock = (StfsDirectoryBlock *)(&rootMappedFileData[offset]); for (uint32_t j = 0; j < StfsEntriesPerDirectoryBlock; j++) { const StfsDirectoryEntry &directoryEntry = directoryBlock->entries[j]; if (directoryEntry.name[0] == '\0') { break; } std::string fileNameBase = directoryNames[directoryEntry.directoryIndex]; std::string fileName(directoryEntry.name, directoryEntry.flags.nameLength & 0x3F); if (directoryEntry.flags.directory) { directoryNames[entryCount++] = fileNameBase + fileName + "/"; continue; } uint32_t fileBlockIndex = parseUint24(directoryEntry.startBlockNumberRaw); uint32_t fileBlockCount = parseUint24(directoryEntry.allocatedDataBlocksRaw); fileMap[fileNameBase + fileName] = { directoryEntry.length, fileBlockIndex, fileBlockCount }; entryCount++; } const StfsHashEntry *hashEntry = hashEntryFromBlockIndex(rootMappedFileData, baseOffset, tableBlockIndex); tableBlockIndex = hashEntry->infoRaw & 0xFFFFFF; if (tableBlockIndex == StfsEndOfChain) { break; } } } else if (volumeType == XContentVolumeType::SVOD) { mappedFiles.clear(); // Close the root file and open all the files inside the directory with the same name instead. std::filesystem::path dataDirectory(contentPath.u8string() + u8".data"); if (!std::filesystem::is_directory(dataDirectory)) { return; } // Find all data files inside the directory. std::set orderedPaths; for (auto &entry : std::filesystem::directory_iterator(dataDirectory)) { if (!entry.is_regular_file()) { continue; } orderedPaths.emplace(entry.path()); } // Memory map all the files that were found. for (auto &path : orderedPaths) { mappedFiles.emplace_back(); if (!mappedFiles.back().open(path)) { mappedFiles.clear(); return; } } if (mappedFiles.empty()) { return; } // Determine the layout of the SVOD from the first file. MemoryMappedFile &firstMappedFile = mappedFiles.front(); const uint8_t *firstMappedFileData = firstMappedFile.data(); const char *RefMagic = "MICROSOFT*XBOX*MEDIA"; size_t RefXSFMagicOffset = 0x12000; size_t SingleFileMagicOffset = 0xD000; if (metadata.svodDeviceDescriptor.features.bits.enhancedGdfLayout) { size_t EGDFMagicOffset = 0x2000; if (EGDFMagicOffset >= firstMappedFile.size() || std::memcmp(&firstMappedFileData[EGDFMagicOffset], RefMagic, strlen(RefMagic)) != 0) { mappedFiles.clear(); return; } svodBaseOffset = 0; svodMagicOffset = EGDFMagicOffset; svodLayoutType = SvodLayoutType::EnhancedGDF; } else if (RefXSFMagicOffset < firstMappedFile.size() && std::memcmp(&firstMappedFileData[RefXSFMagicOffset], RefMagic, strlen(RefMagic)) == 0) { const char *XSFMagic = "XSF"; size_t XSFMagicOffset = 0x2000; svodBaseOffset = 0x10000; svodMagicOffset = 0x12000; if (std::memcmp(&firstMappedFileData[XSFMagicOffset], XSFMagic, strlen(XSFMagic)) == 0) { svodLayoutType = SvodLayoutType::XSF; } else { svodLayoutType = SvodLayoutType::Unknown; } } else if (SingleFileMagicOffset < firstMappedFile.size() && std::memcmp(&firstMappedFileData[SingleFileMagicOffset], RefMagic, strlen(RefMagic)) == 0) { svodBaseOffset = 0xB000; svodMagicOffset = 0xD000; svodLayoutType = SvodLayoutType::SingleFile; } else { mappedFiles.clear(); return; } svodStartDataBlock = parseUint24(metadata.svodDeviceDescriptor.startDataBlockRaw); struct IterationStep { std::string fileNameBase; uint32_t blockIndex = 0; uint32_t ordinalIndex = 0; IterationStep() = default; IterationStep(std::string fileNameBase, uint32_t blockIndex, uint32_t ordinalIndex) : fileNameBase(fileNameBase), blockIndex(blockIndex), ordinalIndex(ordinalIndex) { } }; std::stack iterationStack; uint32_t rootBlock = *(uint32_t *)(&firstMappedFileData[svodMagicOffset + 0x14]); iterationStack.emplace("", rootBlock, 0); IterationStep step; size_t fileOffset, fileIndex; char fileName[256]; const uint8_t FileAttributeDirectory = 0x10; while (!iterationStack.empty()) { step = iterationStack.top(); iterationStack.pop(); size_t ordinalOffset = step.ordinalIndex * 0x4; size_t blockOffset = ordinalOffset / 0x800; size_t trueOrdinalOffset = ordinalOffset % 0x800; blockToOffsetAndFile(svodLayoutType, svodStartDataBlock, svodBaseOffset, step.blockIndex + blockOffset, fileOffset, fileIndex); fileOffset += trueOrdinalOffset; if (fileIndex >= mappedFiles.size()) { mappedFiles.clear(); return; } const MemoryMappedFile &mappedFile = mappedFiles[fileIndex]; if ((fileOffset + sizeof(SvodDirectoryEntry)) > mappedFile.size()) { mappedFiles.clear(); return; } const uint8_t *mappedFileData = mappedFile.data(); const SvodDirectoryEntry *directoryEntry = (const SvodDirectoryEntry *)(&mappedFileData[fileOffset]); size_t nameOffset = fileOffset + sizeof(SvodDirectoryEntry); if ((nameOffset + directoryEntry->nameLength) > mappedFile.size()) { mappedFiles.clear(); return; } memcpy(fileName, &mappedFileData[nameOffset], directoryEntry->nameLength); fileName[directoryEntry->nameLength] = '\0'; if (directoryEntry->nodeL) { iterationStack.emplace(step.fileNameBase, step.blockIndex, directoryEntry->nodeL); } if (directoryEntry->nodeR) { iterationStack.emplace(step.fileNameBase, step.blockIndex, directoryEntry->nodeR); } std::string fileNameUTF8 = step.fileNameBase + fileName; if (directoryEntry->attributes & FileAttributeDirectory) { if (directoryEntry->length > 0) { iterationStack.emplace(fileNameUTF8 + "/", directoryEntry->dataBlock, 0); } } else { fileMap[fileNameUTF8] = { directoryEntry->length, directoryEntry->dataBlock, 0 }; } } } else { mappedFiles.clear(); } } bool XContentFileSystem::load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const { auto it = fileMap.find(path); if (it != fileMap.end()) { if (fileDataMaxByteCount < it->second.size) { return false; } if (volumeType == XContentVolumeType::STFS) { const MemoryMappedFile &rootMappedFile = mappedFiles.back(); const uint8_t *rootMappedFileData = rootMappedFile.data(); size_t fileDataOffset = 0; size_t remainingSize = it->second.size; uint32_t fileBlockIndex = it->second.blockIndex; for (uint32_t i = 0; i < it->second.blockCount && fileBlockIndex != StfsEndOfChain; i++) { size_t blockSize = std::min(size_t(StfsBlockSize), remainingSize); size_t blockOffset = blockIndexToOffset(baseOffset, fileBlockIndex); if (blockOffset + blockSize > rootMappedFile.size()) { return false; } memcpy(&fileData[fileDataOffset], &rootMappedFileData[blockOffset], blockSize); const StfsHashEntry *hashEntry = hashEntryFromBlockIndex(rootMappedFileData, baseOffset, fileBlockIndex); fileBlockIndex = hashEntry->infoRaw & 0xFFFFFF; fileDataOffset += blockSize; remainingSize -= blockSize; } return remainingSize == 0; } else if (volumeType == XContentVolumeType::SVOD) { size_t fileDataOffset = 0; size_t remainingSize = it->second.size; size_t currentBlock = it->second.blockIndex; while (remainingSize > 0) { size_t blockFileOffset, blockFileIndex; blockToOffsetAndFile(svodLayoutType, svodStartDataBlock, svodBaseOffset, currentBlock, blockFileOffset, blockFileIndex); if (blockFileIndex >= mappedFiles.size()) { return false; } const MemoryMappedFile &mappedFile = mappedFiles[blockFileIndex]; const uint8_t *mappedFileData = mappedFile.data(); size_t blockSize = std::min(size_t(0x800), remainingSize); if (blockFileOffset + blockSize > mappedFile.size()) { return false; } memcpy(&fileData[fileDataOffset], &mappedFileData[blockFileOffset], blockSize); fileDataOffset += blockSize; remainingSize -= blockSize; currentBlock++; } return remainingSize == 0; } else { return false; } } else { return false; } } size_t XContentFileSystem::getSize(const std::string &path) const { auto it = fileMap.find(path); if (it != fileMap.end()) { return it->second.size; } else { return 0; } } bool XContentFileSystem::exists(const std::string &path) const { return fileMap.find(path) != fileMap.end(); } const std::string &XContentFileSystem::getName() const { return name; } bool XContentFileSystem::empty() const { return mappedFiles.empty(); } std::unique_ptr XContentFileSystem::create(const std::filesystem::path &contentPath) { std::unique_ptr xContentFS = std::make_unique(contentPath); if (!xContentFS->empty()) { return xContentFS; } else { return nullptr; } } bool XContentFileSystem::check(const std::filesystem::path &contentPath) { std::ifstream contentStream(contentPath, std::ios::binary); if (!contentStream.is_open()) { return false; } uint32_t packageTypeUint = 0; contentStream.read((char *)(&packageTypeUint), sizeof(uint32_t)); packageTypeUint = ByteSwap(packageTypeUint); XContentPackageType packageType = XContentPackageType(packageTypeUint); return packageType == XContentPackageType::CON || packageType == XContentPackageType::LIVE || packageType == XContentPackageType::PIRS; } ================================================ FILE: MarathonRecomp/install/xcontent_file_system.h ================================================ // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/xcontent_container_device.cc /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2023 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #pragma once #include #include #include "virtual_file_system.h" #include enum class XContentVolumeType { STFS = 0, SVOD = 1, }; enum class SvodLayoutType { Unknown = 0x0, EnhancedGDF = 0x1, XSF = 0x2, SingleFile = 0x4, }; struct XContentFileSystem : VirtualFileSystem { struct File { size_t size = 0; uint32_t blockIndex = 0; uint32_t blockCount = 0; }; XContentVolumeType volumeType = XContentVolumeType::STFS; SvodLayoutType svodLayoutType = SvodLayoutType::Unknown; size_t svodStartDataBlock = 0; size_t svodBaseOffset = 0; size_t svodMagicOffset = 0; std::vector mappedFiles; uint64_t baseOffset = 0; std::map fileMap; std::string name; XContentFileSystem(const std::filesystem::path &contentPath); bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override; size_t getSize(const std::string &path) const override; bool exists(const std::string &path) const override; const std::string &getName() const override; bool empty() const; static std::unique_ptr create(const std::filesystem::path &contentPath); static bool check(const std::filesystem::path &contentPath); }; ================================================ FILE: MarathonRecomp/kernel/freelist.h ================================================ #pragma once template struct FreeList { std::vector items; std::vector freed{}; void Free(T& item) { std::destroy_at(&item); freed.push_back(&item - items.data()); } void Free(size_t index) { std::destroy_at(&items[index]); freed.push_back(index); } size_t Alloc() { if (freed.size()) { auto idx = freed[freed.size() - 1]; freed.pop_back(); std::construct_at(&items[idx]); return idx; } items.emplace_back(); return items.size() - 1; } T& operator[](size_t idx) { return items[idx]; } }; ================================================ FILE: MarathonRecomp/kernel/function.h ================================================ #pragma once #include #include #include "xbox.h" #include "memory.h" template constexpr std::tuple function_args(R(*)(T...)) noexcept { return std::tuple(); } template static constexpr decltype(V) constant_v = V; template static constexpr bool is_precise_v = std::is_same_v || std::is_same_v; template struct arg_count_t { static constexpr size_t value = std::tuple_size_v; }; template std::enable_if_t<(I >= sizeof...(TArgs)), void> _tuple_for(std::tuple&, const TCallable& callable) noexcept { } template std::enable_if_t<(I < sizeof...(TArgs)), void> _tuple_for(std::tuple& tpl, const TCallable& callable) noexcept { callable(std::get(tpl), I); _tuple_for(tpl, callable); } struct ArgTranslator { constexpr static uint64_t GetIntegerArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept { if (arg <= 7) { switch (arg) { case 0: return ctx.r3.u32; case 1: return ctx.r4.u32; case 2: return ctx.r5.u32; case 3: return ctx.r6.u32; case 4: return ctx.r7.u32; case 5: return ctx.r8.u32; case 6: return ctx.r9.u32; case 7: return ctx.r10.u32; default: break; } } return *reinterpret_cast*>(base + ctx.r1.u32 + 0x54 + ((arg - 8) * 8)); } static double GetPrecisionArgumentValue(const PPCContext& ctx, uint8_t* base, size_t arg) noexcept { switch (arg) { case 0: return ctx.f1.f64; case 1: return ctx.f2.f64; case 2: return ctx.f3.f64; case 3: return ctx.f4.f64; case 4: return ctx.f5.f64; case 5: return ctx.f6.f64; case 6: return ctx.f7.f64; case 7: return ctx.f8.f64; case 8: return ctx.f9.f64; case 9: return ctx.f10.f64; case 10: return ctx.f11.f64; case 11: return ctx.f12.f64; case 12: return ctx.f13.f64; [[unlikely]] default: break; } // TODO: get value from stack. return 0; } constexpr static void SetIntegerArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, uint64_t value) noexcept { if (arg <= 7) { switch (arg) { case 0: ctx.r3.u64 = value; return; case 1: ctx.r4.u64 = value; return; case 2: ctx.r5.u64 = value; return; case 3: ctx.r6.u64 = value; return; case 4: ctx.r7.u64 = value; return; case 5: ctx.r8.u64 = value; return; case 6: ctx.r9.u64 = value; return; case 7: ctx.r10.u64 = value; return; [[unlikely]] default: break; } } assert(arg < 7 && "Pushing to stack memory is not yet supported."); } static void SetPrecisionArgumentValue(PPCContext& ctx, uint8_t* base, size_t arg, double value) noexcept { switch (arg) { case 0: ctx.f1.f64 = value; return; case 1: ctx.f2.f64 = value; return; case 2: ctx.f3.f64 = value; return; case 3: ctx.f4.f64 = value; return; case 4: ctx.f5.f64 = value; return; case 5: ctx.f6.f64 = value; return; case 6: ctx.f7.f64 = value; return; case 7: ctx.f8.f64 = value; return; case 8: ctx.f9.f64 = value; return; case 9: ctx.f10.f64 = value; return; case 10: ctx.f11.f64 = value; return; case 11: ctx.f12.f64 = value; return; case 12: ctx.f13.f64 = value; return; [[unlikely]] default: break; } assert(arg < 12 && "Pushing to stack memory is not yet supported."); } template constexpr static std::enable_if_t, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept { if constexpr (is_precise_v) { return static_cast(GetPrecisionArgumentValue(ctx, base, idx)); } else { return static_cast(GetIntegerArgumentValue(ctx, base, idx)); } } template constexpr static std::enable_if_t, T> GetValue(PPCContext& ctx, uint8_t* base, size_t idx) noexcept { const auto v = GetIntegerArgumentValue(ctx, base, idx); if (!v) { return nullptr; } return reinterpret_cast(base + static_cast(v)); } template constexpr static std::enable_if_t, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept { if constexpr (is_precise_v) { SetPrecisionArgumentValue(ctx, base, idx, value); } else if constexpr (std::is_null_pointer_v) { SetIntegerArgumentValue(ctx, base, idx, 0); } else if constexpr (std::is_pointer_v) { SetIntegerArgumentValue(ctx, base, idx, g_memory.MapVirtual(value)); } else { SetIntegerArgumentValue(ctx, base, idx, value); } } template constexpr static std::enable_if_t, void> SetValue(PPCContext& ctx, uint8_t* base, size_t idx, T value) noexcept { const auto v = g_memory.MapVirtual((void*)value); if (!v) { return; } SetValue(ctx, base, idx, v); } }; struct Argument { int type{}; int ordinal{}; }; template constexpr std::array> GatherFunctionArguments(const T1& tpl) { std::array> args{}; int floatOrdinal{}; size_t i{}; if constexpr (!args.empty()) { std::apply([&](const auto& first, const auto&... rest) { auto append = [&](const T2& v) { if constexpr (is_precise_v) { args[i] = { 1, floatOrdinal++ }; } else { args[i] = { 0, static_cast(i) }; // what the fuck } i++; }; append(first); (append(rest), ...); }, tpl); } return args; } template constexpr std::array::value> GatherFunctionArguments() { return GatherFunctionArguments(function_args(Func)); } template struct arg_ordinal_t { static constexpr size_t value = GatherFunctionArguments()[I].ordinal; }; template void _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple&) noexcept requires (I >= sizeof...(TArgs)) { } template std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_host(PPCContext& ctx, uint8_t* base, std::tuple& tpl) noexcept { using T = std::tuple_element_t>; std::get(tpl) = ArgTranslator::GetValue(ctx, base, arg_ordinal_t::value); _translate_args_to_host(ctx, base, tpl); } template void _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple&) noexcept requires (I >= sizeof...(TArgs)) { } template std::enable_if_t<(I < sizeof...(TArgs)), void> _translate_args_to_guest(PPCContext& ctx, uint8_t* base, std::tuple& tpl) noexcept { using T = std::tuple_element_t>; ArgTranslator::SetValue(ctx, base, GatherFunctionArguments(std::tuple{})[I].ordinal, std::get(tpl)); _translate_args_to_guest(ctx, base, tpl); } template PPC_FUNC(HostToGuestFunction) { using ret_t = decltype(std::apply(Func, function_args(Func))); auto args = function_args(Func); _translate_args_to_host(ctx, base, args); if constexpr (std::is_same_v) { std::apply(Func, args); } else { auto v = std::apply(Func, args); if constexpr (std::is_pointer()) { if (v != nullptr) { ctx.r3.u64 = static_cast(reinterpret_cast(v) - reinterpret_cast(base)); } else { ctx.r3.u64 = 0; } } else if constexpr (is_precise_v) { ctx.f1.f64 = v; } else { ctx.r3.u64 = (uint64_t)v; } } } template T GuestToHostFunction(const TFunction& func, TArgs&&... argv) { auto args = std::make_tuple(std::forward(argv)...); auto& currentCtx = *GetPPCContext(); PPCContext newCtx; // NOTE: No need for zero initialization, has lots of unnecessary code generation. newCtx.r1 = currentCtx.r1; newCtx.r13 = currentCtx.r13; newCtx.fpscr = currentCtx.fpscr; _translate_args_to_guest(newCtx, g_memory.base, args); SetPPCContext(newCtx); if constexpr (std::is_function_v) func(newCtx, g_memory.base); else g_memory.FindFunction(func)(newCtx, g_memory.base); currentCtx.fpscr = newCtx.fpscr; SetPPCContext(currentCtx); if constexpr (std::is_pointer_v) { return reinterpret_cast((uint64_t)g_memory.Translate(newCtx.r3.u32)); } else if constexpr (is_precise_v) { return static_cast(newCtx.f1.f64); } else if constexpr (std::is_integral_v) { return static_cast(newCtx.r3.u64); } else { static_assert(std::is_void_v, "Unsupported return type."); } } #define GUEST_FUNCTION_HOOK(subroutine, function) \ PPC_FUNC(subroutine) { HostToGuestFunction(ctx, base); } #define GUEST_FUNCTION_STUB(subroutine) \ PPC_FUNC(subroutine) { } ================================================ FILE: MarathonRecomp/kernel/heap.cpp ================================================ #include #include "heap.h" #include "memory.h" #include "function.h" #include "xdm.h" constexpr size_t RESERVED_BEGIN = 0x7FEA0000; constexpr size_t RESERVED_END = 0xA0000000; void Heap::Init() { heap = o1heapInit(g_memory.Translate(0x20000), RESERVED_BEGIN - 0x20000); physicalHeap = o1heapInit(g_memory.Translate(RESERVED_END), 0x100000000 - RESERVED_END); } void* Heap::Alloc(size_t size) { std::lock_guard lock(mutex); return o1heapAllocate(heap, std::max(1, size)); } void* Heap::AllocPhysical(size_t size, size_t alignment) { size = std::max(1, size); alignment = alignment == 0 ? 0x1000 : std::max(16, alignment); std::lock_guard lock(physicalMutex); void* ptr = o1heapAllocate(physicalHeap, size + alignment); size_t aligned = ((size_t)ptr + alignment) & ~(alignment - 1); *((void**)aligned - 1) = ptr; *((size_t*)aligned - 2) = size + O1HEAP_ALIGNMENT; return (void*)aligned; } void Heap::Free(void* ptr) { if (ptr >= physicalHeap) { std::lock_guard lock(physicalMutex); o1heapFree(physicalHeap, *((void**)ptr - 1)); } else { std::lock_guard lock(mutex); o1heapFree(heap, ptr); } } size_t Heap::Size(void* ptr) { if (ptr) return *((size_t*)ptr - 2) - O1HEAP_ALIGNMENT; // relies on fragment header in o1heap.c return 0; } uint32_t RtlAllocateHeap(uint32_t heapHandle, uint32_t flags, uint32_t size) { void* ptr = g_userHeap.Alloc(size); if ((flags & 0x8) != 0) memset(ptr, 0, size); assert(ptr); return g_memory.MapVirtual(ptr); } uint32_t RtlReAllocateHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer, uint32_t size) { void* ptr = g_userHeap.Alloc(size); if ((flags & 0x8) != 0) memset(ptr, 0, size); if (memoryPointer != 0) { void* oldPtr = g_memory.Translate(memoryPointer); memcpy(ptr, oldPtr, std::min(size, g_userHeap.Size(oldPtr))); g_userHeap.Free(oldPtr); } assert(ptr); return g_memory.MapVirtual(ptr); } uint32_t RtlFreeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer) { if (memoryPointer != NULL) g_userHeap.Free(g_memory.Translate(memoryPointer)); return true; } uint32_t RtlSizeHeap(uint32_t heapHandle, uint32_t flags, uint32_t memoryPointer) { if (memoryPointer != NULL) return (uint32_t)g_userHeap.Size(g_memory.Translate(memoryPointer)); return 0; } uint32_t XAllocMem(uint32_t size, uint32_t flags) { void* ptr = (flags & 0x80000000) != 0 ? g_userHeap.AllocPhysical(size, (1ull << ((flags >> 24) & 0xF))) : g_userHeap.Alloc(size); if ((flags & 0x40000000) != 0) memset(ptr, 0, size); assert(ptr); return g_memory.MapVirtual(ptr); } void XFreeMem(uint32_t baseAddress, uint32_t flags) { if (baseAddress != NULL) g_userHeap.Free(g_memory.Translate(baseAddress)); } uint32_t XVirtualAlloc(void *lpAddress, unsigned int dwSize, unsigned int flAllocationType, unsigned int flProtect) { assert(!lpAddress); return g_memory.MapVirtual(g_userHeap.Alloc(dwSize)); } uint32_t XVirtualFree(uint32_t lpAddress, unsigned int dwSize, unsigned int dwFreeType) { if ((dwFreeType & 0x8000) != 0 && dwSize) return FALSE; if (lpAddress) g_userHeap.Free(g_memory.Translate(lpAddress)); return TRUE; } GUEST_FUNCTION_HOOK(sub_82915668, XVirtualAlloc); GUEST_FUNCTION_HOOK(sub_829156B8, XVirtualFree); GUEST_FUNCTION_STUB(sub_82535588); // HeapCreate // replaced // GUEST_FUNCTION_STUB(sub_82BD9250); // HeapDestroy GUEST_FUNCTION_HOOK(sub_82535B38, RtlAllocateHeap); // repalced GUEST_FUNCTION_HOOK(sub_82536420, RtlFreeHeap); // replaced GUEST_FUNCTION_HOOK(sub_82536708, RtlReAllocateHeap); // replaced GUEST_FUNCTION_HOOK(sub_82534DD0, RtlSizeHeap); // replaced GUEST_FUNCTION_HOOK(sub_82537E70, XAllocMem); // replaced GUEST_FUNCTION_HOOK(sub_82537F08, XFreeMem); // replaced ================================================ FILE: MarathonRecomp/kernel/heap.h ================================================ #pragma once #include "mutex.h" struct Heap { Mutex mutex; O1HeapInstance* heap; Mutex physicalMutex; O1HeapInstance* physicalHeap; void Init(); void* Alloc(size_t size); void* AllocPhysical(size_t size, size_t alignment); void Free(void* ptr); size_t Size(void* ptr); template T* Alloc(Args&&... args) { T* obj = (T*)Alloc(sizeof(T)); new (obj) T(std::forward(args)...); return obj; } template T* AllocPhysical(Args&&... args) { T* obj = (T*)AllocPhysical(sizeof(T), alignof(T)); new (obj) T(std::forward(args)...); return obj; } }; extern Heap g_userHeap; ================================================ FILE: MarathonRecomp/kernel/imports.cpp ================================================ #include #include #include #include #include #include #include "function.h" #include "xex.h" #include "xbox.h" #include "heap.h" #include "memory.h" #include #include "xam.h" #include "xdm.h" #include #include #ifdef _WIN32 #include #endif std::unordered_map g_handleDuplicates{}; struct Event final : KernelObject, HostObject { bool manualReset; std::atomic signaled; Event(XKEVENT* header) : manualReset(!header->Type), signaled(!!header->SignalState) { } Event(bool manualReset, bool initialState) : manualReset(manualReset), signaled(initialState) { } uint32_t Wait(uint32_t timeout) override { if (timeout == 0) { if (manualReset) { if (!signaled) return STATUS_TIMEOUT; } else { bool expected = true; if (!signaled.compare_exchange_strong(expected, false)) return STATUS_TIMEOUT; } } else if (timeout == INFINITE) { if (manualReset) { signaled.wait(false); } else { while (true) { bool expected = true; if (signaled.compare_exchange_weak(expected, false)) break; signaled.wait(expected); } } } else { assert(false && "Unhandled timeout value."); } return STATUS_SUCCESS; } bool Set() { signaled = true; if (manualReset) signaled.notify_all(); else signaled.notify_one(); return TRUE; } bool Reset() { signaled = false; return TRUE; } }; static std::atomic g_keSetEventGeneration; struct Semaphore final : KernelObject, HostObject { std::atomic count; uint32_t maximumCount; Semaphore(XKSEMAPHORE* semaphore) : count(semaphore->Header.SignalState), maximumCount(semaphore->Limit) { } Semaphore(uint32_t count, uint32_t maximumCount) : count(count), maximumCount(maximumCount) { } uint32_t Wait(uint32_t timeout) override { if (timeout == 0) { uint32_t currentCount = count.load(); if (currentCount != 0) { if (count.compare_exchange_weak(currentCount, currentCount - 1)) return STATUS_SUCCESS; } return STATUS_TIMEOUT; } else if (timeout == INFINITE) { uint32_t currentCount; while (true) { currentCount = count.load(); if (currentCount != 0) { if (count.compare_exchange_weak(currentCount, currentCount - 1)) return STATUS_SUCCESS; } else { count.wait(0); } } return STATUS_SUCCESS; } else { assert(false && "Unhandled timeout value."); return STATUS_TIMEOUT; } } void Release(uint32_t releaseCount, uint32_t* previousCount) { if (previousCount != nullptr) *previousCount = count; assert(count + releaseCount <= maximumCount); count += releaseCount; count.notify_all(); } }; inline void CloseKernelObject(XDISPATCHER_HEADER& header) { if (header.WaitListHead.Flink != OBJECT_SIGNATURE) { return; } DestroyKernelObject(header.WaitListHead.Blink); } uint32_t GuestTimeoutToMilliseconds(be* timeout) { return timeout ? (*timeout * -1) / 10000 : INFINITE; } void VdHSIOCalibrationLock() { LOG_UTILITY("!!! STUB !!!"); } void KeCertMonitorData() { LOG_UTILITY("!!! STUB !!!"); } void XexExecutableModuleHandle() { LOG_UTILITY("!!! STUB !!!"); } void ExLoadedCommandLine() { LOG_UTILITY("!!! STUB !!!"); } void KeDebugMonitorData() { LOG_UTILITY("!!! STUB !!!"); } void ExThreadObjectType() { LOG_UTILITY("!!! STUB !!!"); } void KeTimeStampBundle() { LOG_UTILITY("!!! STUB !!!"); } void XboxHardwareInfo() { LOG_UTILITY("!!! STUB !!!"); } void XGetVideoMode() { LOG_UTILITY("!!! STUB !!!"); } uint32_t XGetGameRegion() { if (Config::Language == ELanguage::Japanese) return 0x0101; return 0x03FF; } uint32_t XMsgStartIORequest(uint32_t App, uint32_t Message, XXOVERLAPPED* lpOverlapped, void* Buffer, uint32_t szBuffer) { return STATUS_SUCCESS; } uint32_t XamUserGetSigninState(uint32_t userIndex) { return true; } uint32_t XamGetSystemVersion() { return 0; } void XamContentDelete() { LOG_UTILITY("!!! STUB !!!"); } uint32_t XamContentGetCreator(uint32_t userIndex, const XCONTENT_DATA* contentData, be* isCreator, be* xuid, XXOVERLAPPED* overlapped) { if (isCreator) *isCreator = true; if (xuid) *xuid = 0xB13EBABEBABEBABE; return 0; } uint32_t XamContentGetDeviceState() { return 0; } uint32_t XamUserGetSigninInfo(uint32_t userIndex, uint32_t flags, XUSER_SIGNIN_INFO* info) { if (userIndex == 0) { memset(info, 0, sizeof(*info)); info->xuid = 0xB13EBABEBABEBABE; info->SigninState = 1; strcpy(info->Name, "SWA"); return 0; } return 0x00000525; // ERROR_NO_SUCH_USER } void XamShowSigninUI() { LOG_UTILITY("!!! STUB !!!"); } uint32_t XamShowDeviceSelectorUI ( uint32_t userIndex, uint32_t contentType, uint32_t contentFlags, uint64_t totalRequested, be* deviceId, XXOVERLAPPED* overlapped ) { XamNotifyEnqueueEvent(9, true); *deviceId = 1; XamNotifyEnqueueEvent(9, false); return 0; } void XamShowDirtyDiscErrorUI() { LOG_UTILITY("!!! STUB !!!"); } void XamEnableInactivityProcessing() { LOG_UTILITY("!!! STUB !!!"); } void XamResetInactivity() { LOG_UTILITY("!!! STUB !!!"); } void XamShowMessageBoxUIEx() { LOG_UTILITY("!!! STUB !!!"); } uint32_t XGetLanguage() { return (uint32_t)Config::Language.Value; } uint32_t XGetAVPack() { return 0; } void XamLoaderTerminateTitle() { LOG_UTILITY("!!! STUB !!!"); } void XamGetExecutionId() { LOG_UTILITY("!!! STUB !!!"); } void XamLoaderLaunchTitle() { LOG_UTILITY("!!! STUB !!!"); } void NtOpenFile() { LOG_UTILITY("!!! STUB !!!"); } void RtlInitAnsiString(XANSI_STRING* destination, char* source) { const uint16_t length = source ? (uint16_t)strlen(source) : 0; destination->Length = length; destination->MaximumLength = length + 1; destination->Buffer = source; } uint32_t NtCreateFile ( be* FileHandle, uint32_t DesiredAccess, XOBJECT_ATTRIBUTES* Attributes, XIO_STATUS_BLOCK* IoStatusBlock, uint64_t* AllocationSize, uint32_t FileAttributes, uint32_t ShareAccess, uint32_t CreateDisposition, uint32_t CreateOptions ) { LOG_UTILITY("!!! STUB !!!"); return 0; } uint32_t NtClose(uint32_t handle) { if (handle == GUEST_INVALID_HANDLE_VALUE) return 0xFFFFFFFF; if (IsKernelObject(handle)) { // If the handle was duplicated, just decrement the duplication count. Otherwise, delete the object. const auto& it = g_handleDuplicates.find(handle); if (it == g_handleDuplicates.end() || it->second == 0) DestroyKernelObject(handle); else if (--it->second == 0) g_handleDuplicates.erase(it); return 0; } else { assert(false && "Unrecognized kernel object."); return 0xFFFFFFFF; } } void NtSetInformationFile() { LOG_UTILITY("!!! STUB !!!"); } uint32_t FscSetCacheElementCount() { return 0; } uint32_t FscGetCacheElementCount() { return 0; } uint32_t XamLoaderGetLaunchDataSize() { return 0; } uint32_t XamLoaderGetLaunchData() { return 0; } uint32_t XamLoaderSetLaunchData() { return 0; } uint32_t NtWaitForSingleObjectEx(uint32_t Handle, uint32_t WaitMode, uint32_t Alertable, be* Timeout) { if (Handle == GUEST_INVALID_HANDLE_VALUE) return 0xFFFFFFFF; uint32_t timeout = GuestTimeoutToMilliseconds(Timeout); // assert(timeout == 0 || timeout == INFINITE); if (IsKernelObject(Handle)) { return GetKernelObject(Handle)->Wait(timeout); } else { assert(false && "Unrecognized handle value."); } return STATUS_TIMEOUT; } void NtWriteFile() { LOG_UTILITY("!!! STUB !!!"); } void vsprintf_x() { LOG_UTILITY("!!! STUB !!!"); } uint32_t ExGetXConfigSetting(uint16_t Category, uint16_t Setting, void* Buffer, uint16_t SizeOfBuffer, be* RequiredSize) { uint32_t data[4]{}; switch (Category) { // XCONFIG_SECURED_CATEGORY case 0x0002: { switch (Setting) { // XCONFIG_SECURED_AV_REGION case 0x0002: data[0] = ByteSwap(0x00001000); // USA/Canada break; default: return 1; } } case 0x0003: { switch (Setting) { case 0x0001: // XCONFIG_USER_TIME_ZONE_BIAS case 0x0002: // XCONFIG_USER_TIME_ZONE_STD_NAME case 0x0003: // XCONFIG_USER_TIME_ZONE_DLT_NAME case 0x0004: // XCONFIG_USER_TIME_ZONE_STD_DATE case 0x0005: // XCONFIG_USER_TIME_ZONE_DLT_DATE case 0x0006: // XCONFIG_USER_TIME_ZONE_STD_BIAS case 0x0007: // XCONFIG_USER_TIME_ZONE_DLT_BIAS data[0] = 0; break; // XCONFIG_USER_LANGUAGE case 0x0009: data[0] = ByteSwap((uint32_t)Config::Language.Value); break; // XCONFIG_USER_VIDEO_FLAGS case 0x000A: data[0] = ByteSwap(0x00040000); break; // XCONFIG_USER_RETAIL_FLAGS case 0x000C: data[0] = ByteSwap(1); break; // XCONFIG_USER_COUNTRY case 0x000E: data[0] = ByteSwap(103); break; default: return 1; } } } *RequiredSize = 4; memcpy(Buffer, data, std::min((size_t)SizeOfBuffer, sizeof(data))); return 0; } void NtQueryVirtualMemory() { LOG_UTILITY("!!! STUB !!!"); } void MmQueryStatistics() { LOG_UTILITY("!!! STUB !!!"); } uint32_t NtCreateEvent(be* handle, void* objAttributes, uint32_t eventType, uint32_t initialState) { *handle = GetKernelHandle(CreateKernelObject(!eventType, !!initialState)); return 0; } uint32_t XexCheckExecutablePrivilege() { return 0; } void DbgPrint() { LOG_UTILITY("!!! STUB !!!"); } void __C_specific_handler_x() { LOG_UTILITY("!!! STUB !!!"); } uint32_t RtlNtStatusToDosError(uint32_t Status) { // See https://github.com/wine-mirror/wine/blob/master/dlls/ntdll/error.c#L47-L64 if (Status == 0 || (Status & 0x20000000) != 0) return Status; if ((Status & 0xF0000000) == 0xD0000000) Status &= ~0x10000000; const uint32_t hi = (Status >> 16) & 0xFFFF; if (hi == 0x8007 || hi == 0xC001 || hi == 0xC007) return Status & 0xFFFF; switch (Status) { case uint32_t(STATUS_NOT_IMPLEMENTED): return ERROR_CALL_NOT_IMPLEMENTED; case uint32_t(STATUS_SEMAPHORE_LIMIT_EXCEEDED): return ERROR_TOO_MANY_POSTS; default: LOGF_WARNING("Unimplemented NtStatus translation: {:#08x}", Status); return Status; } } void XexGetProcedureAddress() { LOG_UTILITY("!!! STUB !!!"); } void XexGetModuleSection() { LOG_UTILITY("!!! STUB !!!"); } uint32_t RtlUnicodeToMultiByteN(char* MultiByteString, uint32_t MaxBytesInMultiByteString, be* BytesInMultiByteString, const be* UnicodeString, uint32_t BytesInUnicodeString) { const auto reqSize = BytesInUnicodeString / sizeof(uint16_t); if (BytesInMultiByteString) *BytesInMultiByteString = reqSize; if (reqSize > MaxBytesInMultiByteString) return STATUS_FAIL_CHECK; for (size_t i = 0; i < reqSize; i++) { const auto c = UnicodeString[i].get(); MultiByteString[i] = c < 256 ? c : '?'; } return STATUS_SUCCESS; } uint32_t KeDelayExecutionThread(uint32_t WaitMode, bool Alertable, be* Timeout) { // We don't do async file reads. if (Alertable) return STATUS_USER_APC; uint32_t timeout = GuestTimeoutToMilliseconds(Timeout); #ifdef _WIN32 Sleep(timeout); #else if (timeout == 0) std::this_thread::yield(); else std::this_thread::sleep_for(std::chrono::milliseconds(timeout)); #endif return STATUS_SUCCESS; } void ExFreePool() { LOG_UTILITY("!!! STUB !!!"); } void NtQueryInformationFile() { LOG_UTILITY("!!! STUB !!!"); } void NtQueryVolumeInformationFile() { LOG_UTILITY("!!! STUB !!!"); } void NtQueryDirectoryFile() { LOG_UTILITY("!!! STUB !!!"); } void NtReadFileScatter() { LOG_UTILITY("!!! STUB !!!"); } void NtReadFile() { LOG_UTILITY("!!! STUB !!!"); } uint32_t NtDuplicateObject(uint32_t SourceHandle, be* TargetHandle, uint32_t Options) { if (SourceHandle == GUEST_INVALID_HANDLE_VALUE) return 0xFFFFFFFF; if (IsKernelObject(SourceHandle)) { // Increment handle duplicate count. const auto& it = g_handleDuplicates.find(SourceHandle); if (it != g_handleDuplicates.end()) ++it->second; else g_handleDuplicates[SourceHandle] = 1; *TargetHandle = SourceHandle; return 0; } else { assert(false && "Unrecognized kernel object."); return 0xFFFFFFFF; } } void NtAllocateVirtualMemory() { __builtin_trap(); LOG_UTILITY("!!! STUB !!!"); } void NtFreeVirtualMemory() { LOG_UTILITY("!!! STUB !!!"); } void ObDereferenceObject() { LOG_UTILITY("!!! STUB !!!"); } void KeSetBasePriorityThread(GuestThreadHandle* hThread, int priority) { #ifdef _WIN32 if (priority == 16) { priority = 15; } else if (priority == -16) { priority = -15; } SetThreadPriority(hThread == GetKernelObject(CURRENT_THREAD_HANDLE) ? GetCurrentThread() : hThread->thread.native_handle(), priority); #endif } uint32_t ObReferenceObjectByHandle(uint32_t handle, uint32_t objectType, be* object) { *object = handle; return 0; } void KeQueryBasePriorityThread() { LOG_UTILITY("!!! STUB !!!"); } uint32_t NtSuspendThread(GuestThreadHandle* hThread, uint32_t* suspendCount) { assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE) && hThread->GetThreadId() == GuestThread::GetCurrentThreadId()); hThread->suspended = true; hThread->suspended.wait(true); return S_OK; } uint32_t KeSetAffinityThread(uint32_t Thread, uint32_t Affinity, be* lpPreviousAffinity) { if (lpPreviousAffinity) *lpPreviousAffinity = 2; return 0; } void RtlLeaveCriticalSection(XRTL_CRITICAL_SECTION* cs) { // printf("RtlLeaveCriticalSection"); cs->RecursionCount = cs->RecursionCount.get() - 1; if (cs->RecursionCount.get() != 0) return; std::atomic_ref owningThread(cs->OwningThread); owningThread.store(0); owningThread.notify_one(); } void RtlEnterCriticalSection(XRTL_CRITICAL_SECTION* cs) { uint32_t thisThread = g_ppcContext->r13.u32; // printf("RtlEnterCriticalSection %x %x %x %x\n", thisThread, cs->OwningThread, cs->LockCount, cs->RecursionCount); assert(thisThread != NULL); std::atomic_ref owningThread(cs->OwningThread); while (true) { uint32_t previousOwner = 0; if (owningThread.compare_exchange_weak(previousOwner, thisThread) || previousOwner == thisThread) { cs->RecursionCount = cs->RecursionCount.get() + 1; return; } // printf("wait start %x\n", cs); owningThread.wait(previousOwner); // printf("wait end\n"); } } void RtlImageXexHeaderField() { LOG_UTILITY("!!! STUB !!!"); } void HalReturnToFirmware() { LOG_UTILITY("!!! STUB !!!"); } void RtlFillMemoryUlong() { LOG_UTILITY("!!! STUB !!!"); } void KeBugCheckEx() { __builtin_debugtrap(); } uint32_t KeGetCurrentProcessType() { return 1; } void RtlCompareMemoryUlong() { LOG_UTILITY("!!! STUB !!!"); } uint32_t RtlInitializeCriticalSection(XRTL_CRITICAL_SECTION* cs) { // printf("RtlInitializeCriticalSection %x\n", cs); cs->Header.Absolute = 0; cs->LockCount = -1; cs->RecursionCount = 0; cs->OwningThread = 0; return 0; } void RtlRaiseException_x() { LOG_UTILITY("!!! STUB !!!"); } void KfReleaseSpinLock(uint32_t* spinLock) { std::atomic_ref spinLockRef(*spinLock); spinLockRef = 0; } void KfAcquireSpinLock(uint32_t* spinLock) { std::atomic_ref spinLockRef(*spinLock); while (true) { uint32_t expected = 0; if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32)) break; std::this_thread::yield(); } } uint64_t KeQueryPerformanceFrequency() { return 49875000; } void MmFreePhysicalMemory(uint32_t type, uint32_t guestAddress) { if (guestAddress != NULL) g_userHeap.Free(g_memory.Translate(guestAddress)); } bool VdPersistDisplay(uint32_t a1, uint32_t* a2) { *a2 = NULL; return false; } void VdSwap() { LOG_UTILITY("!!! STUB !!!"); } void VdGetSystemCommandBuffer() { LOG_UTILITY("!!! STUB !!!"); } void KeReleaseSpinLockFromRaisedIrql(uint32_t* spinLock) { std::atomic_ref spinLockRef(*spinLock); spinLockRef = 0; } void KeAcquireSpinLockAtRaisedIrql(uint32_t* spinLock) { std::atomic_ref spinLockRef(*spinLock); while (true) { uint32_t expected = 0; if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32)) break; std::this_thread::yield(); } } uint32_t KiApcNormalRoutineNop() { return 0; } void VdEnableRingBufferRPtrWriteBack() { LOG_UTILITY("!!! STUB !!!"); } void VdInitializeRingBuffer() { LOG_UTILITY("!!! STUB !!!"); } uint32_t MmGetPhysicalAddress(uint32_t address) { LOGF_UTILITY("0x{:x}", address); return address; } void VdSetSystemCommandBufferGpuIdentifierAddress() { LOG_UTILITY("!!! STUB !!!"); } void _vsnprintf_x() { LOG_UTILITY("!!! STUB !!!"); } void sprintf_x() { LOG_UTILITY("!!! STUB !!!"); } int32_t ExRegisterTitleTerminateNotification(uint32_t* reg, uint32_t create) { LOG_UTILITY("!!! STUB !!!"); return 0; } void VdShutdownEngines() { LOG_UTILITY("!!! STUB !!!"); } void VdQueryVideoMode(XVIDEO_MODE* vm) { memset(vm, 0, sizeof(XVIDEO_MODE)); vm->DisplayWidth = 1280; vm->DisplayHeight = 720; vm->IsInterlaced = false; vm->IsWidescreen = true; vm->IsHighDefinition = true; vm->RefreshRate = 0x42700000; vm->VideoStandard = 1; vm->Unknown4A = 0x4A; vm->Unknown01 = 0x01; } void VdGetCurrentDisplayInformation() { LOG_UTILITY("!!! STUB !!!"); } void VdSetDisplayMode() { LOG_UTILITY("!!! STUB !!!"); } void VdSetGraphicsInterruptCallback() { LOG_UTILITY("!!! STUB !!!"); } uint32_t VdInitializeEngines() { LOG_UTILITY("!!! STUB !!!"); return 1; } void VdIsHSIOTrainingSucceeded() { LOG_UTILITY("!!! STUB !!!"); } void VdGetCurrentDisplayGamma() { LOG_UTILITY("!!! STUB !!!"); } void VdQueryVideoFlags() { LOG_UTILITY("!!! STUB !!!"); } void VdCallGraphicsNotificationRoutines() { LOG_UTILITY("!!! STUB !!!"); } void VdInitializeScalerCommandBuffer() { LOG_UTILITY("!!! STUB !!!"); } void KeLeaveCriticalRegion() { LOG_UTILITY("!!! STUB !!!"); } uint32_t VdRetrainEDRAM() { return 0; } void VdRetrainEDRAMWorker() { LOG_UTILITY("!!! STUB !!!"); } void KeEnterCriticalRegion() { LOG_UTILITY("!!! STUB !!!"); } uint32_t MmAllocatePhysicalMemoryEx ( uint32_t flags, uint32_t size, uint32_t protect, uint32_t minAddress, uint32_t maxAddress, uint32_t alignment ) { LOGF_UTILITY("0x{:x}, 0x{:x}, 0x{:x}, 0x{:x}, 0x{:x}, 0x{:x}", flags, size, protect, minAddress, maxAddress, alignment); return g_memory.MapVirtual(g_userHeap.AllocPhysical(size, alignment)); } void ObDeleteSymbolicLink() { LOG_UTILITY("!!! STUB !!!"); } void ObCreateSymbolicLink() { LOG_UTILITY("!!! STUB !!!"); } uint32_t MmQueryAddressProtect(uint32_t guestAddress) { return PAGE_READWRITE; } void VdEnableDisableClockGating() { LOG_UTILITY("!!! STUB !!!"); } void KeBugCheck() { __builtin_debugtrap(); } void KeLockL2() { LOG_UTILITY("!!! STUB !!!"); } void KeUnlockL2() { LOG_UTILITY("!!! STUB !!!"); } bool KeSetEvent(XKEVENT* pEvent, uint32_t Increment, bool Wait) { bool result = QueryKernelObject(*pEvent)->Set(); ++g_keSetEventGeneration; g_keSetEventGeneration.notify_all(); return result; } bool KeResetEvent(XKEVENT* pEvent) { return QueryKernelObject(*pEvent)->Reset(); } uint32_t KeWaitForSingleObject(XDISPATCHER_HEADER* Object, uint32_t WaitReason, uint32_t WaitMode, bool Alertable, be* Timeout) { const uint32_t timeout = GuestTimeoutToMilliseconds(Timeout); assert(timeout == INFINITE); switch (Object->Type) { case 0: case 1: QueryKernelObject(*Object)->Wait(timeout); break; case 5: QueryKernelObject(*Object)->Wait(timeout); break; default: assert(false && "Unrecognized kernel object type."); return STATUS_TIMEOUT; } return STATUS_SUCCESS; } static std::vector g_tlsFreeIndices; static size_t g_tlsNextIndex = 0; static Mutex g_tlsAllocationMutex; static uint32_t& KeTlsGetValueRef(size_t index) { // Having this a global thread_local variable // for some reason crashes on boot in debug builds. thread_local std::vector s_tlsValues; if (s_tlsValues.size() <= index) { s_tlsValues.resize(index + 1, 0); } return s_tlsValues[index]; } uint32_t KeTlsGetValue(uint32_t dwTlsIndex) { return KeTlsGetValueRef(dwTlsIndex); } uint32_t KeTlsSetValue(uint32_t dwTlsIndex, uint32_t lpTlsValue) { KeTlsGetValueRef(dwTlsIndex) = lpTlsValue; return TRUE; } uint32_t KeTlsAlloc() { std::lock_guard lock(g_tlsAllocationMutex); if (!g_tlsFreeIndices.empty()) { size_t index = g_tlsFreeIndices.back(); g_tlsFreeIndices.pop_back(); return index; } return g_tlsNextIndex++; } uint32_t KeTlsFree(uint32_t dwTlsIndex) { std::lock_guard lock(g_tlsAllocationMutex); g_tlsFreeIndices.push_back(dwTlsIndex); return TRUE; } uint32_t XMsgInProcessCall(uint32_t app, uint32_t message, be* param1, be* param2) { if (message == 0x7001B) { uint32_t* ptr = (uint32_t*)g_memory.Translate(param1[1]); ptr[0] = 0; ptr[1] = 0; } return 0; } void XamUserReadProfileSettings ( uint32_t titleId, uint32_t userIndex, uint32_t xuidCount, uint64_t* xuids, uint32_t settingCount, uint32_t* settingIds, be* bufferSize, void* buffer, void* overlapped ) { if (buffer != nullptr) { memset(buffer, 0, *bufferSize); } else { *bufferSize = 4; } } void NetDll_WSAStartup() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_WSACleanup() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_socket() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_closesocket() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_setsockopt() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_bind() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_connect() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_listen() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_accept() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_select() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_recv() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_send() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_inet_addr() { LOG_UTILITY("!!! STUB !!!"); } void NetDll___WSAFDIsSet() { LOG_UTILITY("!!! STUB !!!"); } void XMsgStartIORequestEx() { LOG_UTILITY("!!! STUB !!!"); } void XexGetModuleHandle() { LOG_UTILITY("!!! STUB !!!"); } bool RtlTryEnterCriticalSection(XRTL_CRITICAL_SECTION* cs) { // printf("RtlTryEnterCriticalSection\n"); uint32_t thisThread = g_ppcContext->r13.u32; assert(thisThread != NULL); std::atomic_ref owningThread(cs->OwningThread); uint32_t previousOwner = 0; if (owningThread.compare_exchange_weak(previousOwner, thisThread) || previousOwner == thisThread) { cs->RecursionCount = cs->RecursionCount.get() + 1; return true; } return false; } void RtlInitializeCriticalSectionAndSpinCount(XRTL_CRITICAL_SECTION* cs, uint32_t spinCount) { // printf("RtlInitializeCriticalSectionAndSpinCount\n"); cs->Header.Absolute = (spinCount + 255) >> 8; cs->LockCount = -1; cs->RecursionCount = 0; cs->OwningThread = 0; } void _vswprintf_x() { LOG_UTILITY("!!! STUB !!!"); } void _vscwprintf_x() { LOG_UTILITY("!!! STUB !!!"); } void _swprintf_x() { LOG_UTILITY("!!! STUB !!!"); } void _snwprintf_x() { LOG_UTILITY("!!! STUB !!!"); } void XeCryptBnQwBeSigVerify() { LOG_UTILITY("!!! STUB !!!"); } void XeKeysGetKey() { LOG_UTILITY("!!! STUB !!!"); } void XeCryptRotSumSha() { LOG_UTILITY("!!! STUB !!!"); } void XeCryptSha() { LOG_UTILITY("!!! STUB !!!"); } void KeEnableFpuExceptions() { LOG_UTILITY("!!! STUB !!!"); } void RtlUnwind_x() { LOG_UTILITY("!!! STUB !!!"); } void RtlCaptureContext_x() { LOG_UTILITY("!!! STUB !!!"); } void NtQueryFullAttributesFile() { LOG_UTILITY("!!! STUB !!!"); } uint32_t RtlMultiByteToUnicodeN(be* UnicodeString, uint32_t MaxBytesInUnicodeString, be* BytesInUnicodeString, const char* MultiByteString, uint32_t BytesInMultiByteString) { uint32_t length = std::min(MaxBytesInUnicodeString / 2, BytesInMultiByteString); for (size_t i = 0; i < length; i++) UnicodeString[i] = MultiByteString[i]; if (BytesInUnicodeString != nullptr) *BytesInUnicodeString = length * 2; return STATUS_SUCCESS; } void DbgBreakPoint() { LOG_UTILITY("!!! STUB !!!"); } void MmQueryAllocationSize() { LOG_UTILITY("!!! STUB !!!"); } uint32_t NtClearEvent(Event* handle, uint32_t* previousState) { handle->Reset(); return 0; } uint32_t NtResumeThread(GuestThreadHandle* hThread, uint32_t* suspendCount) { assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE)); hThread->suspended = false; hThread->suspended.notify_all(); return S_OK; } uint32_t NtSetEvent(Event* handle, uint32_t* previousState) { handle->Set(); return 0; } uint32_t NtCreateSemaphore(be* Handle, XOBJECT_ATTRIBUTES* ObjectAttributes, uint32_t InitialCount, uint32_t MaximumCount) { *Handle = GetKernelHandle(CreateKernelObject(InitialCount, MaximumCount)); return STATUS_SUCCESS; } uint32_t NtReleaseSemaphore(Semaphore* Handle, uint32_t ReleaseCount, int32_t* PreviousCount) { // the game releases semaphore with 1 maximum number of releases more than once if (Handle->count + ReleaseCount > Handle->maximumCount) return STATUS_SEMAPHORE_LIMIT_EXCEEDED; uint32_t previousCount; Handle->Release(ReleaseCount, &previousCount); if (PreviousCount != nullptr) *PreviousCount = ByteSwap(previousCount); return STATUS_SUCCESS; } void NtWaitForMultipleObjectsEx() { LOG_UTILITY("!!! STUB !!!"); } void RtlCompareStringN() { LOG_UTILITY("!!! STUB !!!"); } void _snprintf_x() { LOG_UTILITY("!!! STUB !!!"); } void StfsControlDevice() { LOG_UTILITY("!!! STUB !!!"); } void StfsCreateDevice() { LOG_UTILITY("!!! STUB !!!"); } void NtFlushBuffersFile() { LOG_UTILITY("!!! STUB !!!"); } void KeQuerySystemTime(be* time) { constexpr int64_t FILETIME_EPOCH_DIFFERENCE = 116444736000000000LL; auto now = std::chrono::system_clock::now(); auto timeSinceEpoch = now.time_since_epoch(); int64_t currentTime100ns = std::chrono::duration_cast>>(timeSinceEpoch).count(); currentTime100ns += FILETIME_EPOCH_DIFFERENCE; *time = currentTime100ns; } struct TIME_FIELDS { be Year; be Month; be Day; be Hour; be Minute; be Second; be Milliseconds; be Weekday; }; void RtlTimeToTimeFields(const be* time, TIME_FIELDS* timeFields) { constexpr uint64_t TICKS_PER_MILLISECOND = 10000; constexpr uint64_t TICKS_PER_SECOND = 10000000; constexpr uint64_t TICKS_PER_MINUTE = 600000000; constexpr uint64_t TICKS_PER_HOUR = 36000000000; constexpr uint64_t TICKS_PER_DAY = 864000000000; static const int DaysInMonth[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, // Non-leap {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} // Leap }; // Calculate total days since January 1, 1601 uint64_t days = *time / TICKS_PER_DAY; uint64_t remainingTicks = *time % TICKS_PER_DAY; timeFields->Hour = static_cast(remainingTicks / TICKS_PER_HOUR); remainingTicks %= TICKS_PER_HOUR; timeFields->Minute = static_cast(remainingTicks / TICKS_PER_MINUTE); remainingTicks %= TICKS_PER_MINUTE; timeFields->Second = static_cast(remainingTicks / TICKS_PER_SECOND); remainingTicks %= TICKS_PER_SECOND; timeFields->Milliseconds = static_cast(remainingTicks / TICKS_PER_MILLISECOND); // Calculate day of week (January 1, 1601 was a Monday = 1) timeFields->Weekday = static_cast((days + 1) % 7); // Calculate year uint32_t year = 1601; // Each 400-year cycle has 146097 days uint32_t cycles400 = static_cast(days / 146097); days %= 146097; year += cycles400 * 400; // Handle 100-year cycles (24 leap years + 76 normal years = 36524 days) // Except the 4th century which has 36525 days uint32_t cycles100 = static_cast(days / 36524); if (cycles100 == 4) cycles100 = 3; // Last day of 400-year cycle days -= cycles100 * 36524; year += cycles100 * 100; // Handle 4-year cycles (1 leap year + 3 normal years = 1461 days) uint32_t cycles4 = static_cast(days / 1461); days %= 1461; year += cycles4 * 4; // Handle individual years within 4-year cycle uint32_t yearInCycle = static_cast(days / 365); if (yearInCycle == 4) yearInCycle = 3; // Last day of leap cycle days -= yearInCycle * 365; if (yearInCycle > 0) { // Account for leap days in previous years of this cycle days -= (yearInCycle - 1) / 4; } year += yearInCycle; timeFields->Year = static_cast(year); // Determine if current year is a leap year bool isLeapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); // Calculate month and day const int* monthDays = DaysInMonth[isLeapYear ? 1 : 0]; uint32_t dayOfYear = static_cast(days) + 1; // Convert to 1-based uint16_t month = 1; while (dayOfYear > static_cast(monthDays[month - 1])) { dayOfYear -= monthDays[month - 1]; month++; } timeFields->Month = month; timeFields->Day = static_cast(dayOfYear); } void RtlFreeAnsiString() { LOG_UTILITY("!!! STUB !!!"); } void RtlUnicodeStringToAnsiString() { LOG_UTILITY("!!! STUB !!!"); } void RtlInitUnicodeString() { LOG_UTILITY("!!! STUB !!!"); } void ExTerminateThread() { LOG_UTILITY("!!! STUB !!!"); } uint32_t ExCreateThread(be* handle, uint32_t stackSize, be* threadId, uint32_t xApiThreadStartup, uint32_t startAddress, uint32_t startContext, uint32_t creationFlags) { uint32_t hostThreadId; *handle = GetKernelHandle(GuestThread::Start({ startAddress, startContext, creationFlags }, &hostThreadId)); LOGF_UTILITY("0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X}, 0x{:X} {:X}", (intptr_t)handle, stackSize, (intptr_t)threadId, xApiThreadStartup, startAddress, startContext, creationFlags, hostThreadId); if (threadId != nullptr) *threadId = hostThreadId; return 0; } void IoInvalidDeviceRequest() { LOG_UTILITY("!!! STUB !!!"); } void ObReferenceObject() { LOG_UTILITY("!!! STUB !!!"); } void IoCreateDevice() { LOG_UTILITY("!!! STUB !!!"); } void IoDeleteDevice() { LOG_UTILITY("!!! STUB !!!"); } void ExAllocatePoolTypeWithTag() { LOG_UTILITY("!!! STUB !!!"); } void RtlTimeFieldsToTime() { LOG_UTILITY("!!! STUB !!!"); } void IoCompleteRequest() { LOG_UTILITY("!!! STUB !!!"); } void RtlUpcaseUnicodeChar() { LOG_UTILITY("!!! STUB !!!"); } void ObIsTitleObject() { LOG_UTILITY("!!! STUB !!!"); } void IoCheckShareAccess() { LOG_UTILITY("!!! STUB !!!"); } void IoSetShareAccess() { LOG_UTILITY("!!! STUB !!!"); } void IoRemoveShareAccess() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_XNetStartup() { LOG_UTILITY("!!! STUB !!!"); } void NetDll_XNetGetTitleXnAddr() { LOG_UTILITY("!!! STUB !!!"); } uint32_t KeWaitForMultipleObjects(uint32_t Count, xpointer* Objects, uint32_t WaitType, uint32_t WaitReason, uint32_t WaitMode, uint32_t Alertable, be* Timeout) { // FIXME: This function is only accounting for events. const uint64_t timeout = GuestTimeoutToMilliseconds(Timeout); assert(timeout == INFINITE); if (WaitType == 0) // Wait all { for (size_t i = 0; i < Count; i++) QueryKernelObject(*Objects[i])->Wait(timeout); } else { thread_local std::vector s_events; s_events.resize(Count); for (size_t i = 0; i < Count; i++) s_events[i] = QueryKernelObject(*Objects[i]); while (true) { uint32_t generation = g_keSetEventGeneration.load(); for (size_t i = 0; i < Count; i++) { if (s_events[i]->Wait(0) == STATUS_SUCCESS) { return STATUS_WAIT_0 + i; } } g_keSetEventGeneration.wait(generation); } } return STATUS_SUCCESS; } uint32_t KeRaiseIrqlToDpcLevel() { return 0; } void KfLowerIrql() { } uint32_t KeReleaseSemaphore(XKSEMAPHORE* semaphore, uint32_t increment, uint32_t adjustment, uint32_t wait) { auto* object = QueryKernelObject(semaphore->Header); object->Release(adjustment, nullptr); return STATUS_SUCCESS; } void XAudioGetVoiceCategoryVolume() { LOG_UTILITY("!!! STUB !!!"); } uint32_t XAudioGetVoiceCategoryVolumeChangeMask(uint32_t Driver, be* Mask) { *Mask = 0; return 0; } uint32_t KeResumeThread(GuestThreadHandle* object) { assert(object != GetKernelObject(CURRENT_THREAD_HANDLE)); object->suspended = false; object->suspended.notify_all(); return 0; } void KeInitializeSemaphore(XKSEMAPHORE* semaphore, uint32_t count, uint32_t limit) { semaphore->Header.Type = 5; semaphore->Header.SignalState = count; semaphore->Limit = limit; auto* object = QueryKernelObject(semaphore->Header); } void XMAReleaseContext() { LOG_UTILITY("!!! STUB !!!"); } void XMACreateContext() { LOG_UTILITY("!!! STUB !!!"); } // uint32_t XAudioRegisterRenderDriverClient(be* callback, be* driver) // { // //printf("XAudioRegisterRenderDriverClient(): %x %x\n"); // // *driver = apu::RegisterClient(callback[0], callback[1]); // return 0; // } // void XAudioUnregisterRenderDriverClient() // { // printf("!!! STUB !!! XAudioUnregisterRenderDriverClient\n"); // } // uint32_t XAudioSubmitRenderDriverFrame(uint32_t driver, void* samples) // { // // printf("!!! STUB !!! XAudioSubmitRenderDriverFrame\n"); // apu::SubmitFrames(samples); // // return 0; // } void XapiInitProcess() { printf("XapiInitProcess Invoked\n"); int *XapiProcessHeap = (int *)g_memory.Translate(0x82D57540); *XapiProcessHeap = 1; } GUEST_FUNCTION_HOOK(sub_825383D8, XapiInitProcess) GUEST_FUNCTION_HOOK(__imp__XGetVideoMode, VdQueryVideoMode); // XGetVideoMode GUEST_FUNCTION_HOOK(__imp__XNotifyGetNext, XNotifyGetNext); GUEST_FUNCTION_HOOK(__imp__XGetGameRegion, XGetGameRegion); GUEST_FUNCTION_HOOK(__imp__XMsgStartIORequest, XMsgStartIORequest); GUEST_FUNCTION_HOOK(__imp__XamUserGetSigninState, XamUserGetSigninState); GUEST_FUNCTION_HOOK(__imp__XamGetSystemVersion, XamGetSystemVersion); GUEST_FUNCTION_HOOK(__imp__XamContentCreateEx, XamContentCreateEx); GUEST_FUNCTION_HOOK(__imp__XamContentDelete, XamContentDelete); GUEST_FUNCTION_HOOK(__imp__XamContentClose, XamContentClose); GUEST_FUNCTION_HOOK(__imp__XamContentGetCreator, XamContentGetCreator); GUEST_FUNCTION_HOOK(__imp__XamContentCreateEnumerator, XamContentCreateEnumerator); GUEST_FUNCTION_HOOK(__imp__XamContentGetDeviceState, XamContentGetDeviceState); GUEST_FUNCTION_HOOK(__imp__XamContentGetDeviceData, XamContentGetDeviceData); GUEST_FUNCTION_HOOK(__imp__XamEnumerate, XamEnumerate); GUEST_FUNCTION_HOOK(__imp__XamNotifyCreateListener, XamNotifyCreateListener); GUEST_FUNCTION_HOOK(__imp__XamUserGetSigninInfo, XamUserGetSigninInfo); GUEST_FUNCTION_HOOK(__imp__XamShowSigninUI, XamShowSigninUI); GUEST_FUNCTION_HOOK(__imp__XamShowDeviceSelectorUI, XamShowDeviceSelectorUI); GUEST_FUNCTION_HOOK(__imp__XamShowMessageBoxUI, XamShowMessageBoxUI); GUEST_FUNCTION_HOOK(__imp__XamShowDirtyDiscErrorUI, XamShowDirtyDiscErrorUI); GUEST_FUNCTION_HOOK(__imp__XamEnableInactivityProcessing, XamEnableInactivityProcessing); GUEST_FUNCTION_HOOK(__imp__XamResetInactivity, XamResetInactivity); GUEST_FUNCTION_HOOK(__imp__XamShowMessageBoxUIEx, XamShowMessageBoxUIEx); GUEST_FUNCTION_HOOK(__imp__XGetLanguage, XGetLanguage); GUEST_FUNCTION_HOOK(__imp__XGetAVPack, XGetAVPack); GUEST_FUNCTION_HOOK(__imp__XamLoaderTerminateTitle, XamLoaderTerminateTitle); GUEST_FUNCTION_HOOK(__imp__XamGetExecutionId, XamGetExecutionId); GUEST_FUNCTION_HOOK(__imp__XamLoaderLaunchTitle, XamLoaderLaunchTitle); GUEST_FUNCTION_HOOK(__imp__NtOpenFile, NtOpenFile); GUEST_FUNCTION_HOOK(__imp__RtlInitAnsiString, RtlInitAnsiString); GUEST_FUNCTION_HOOK(__imp__NtCreateFile, NtCreateFile); GUEST_FUNCTION_HOOK(__imp__NtClose, NtClose); GUEST_FUNCTION_HOOK(__imp__NtSetInformationFile, NtSetInformationFile); GUEST_FUNCTION_HOOK(__imp__FscGetCacheElementCount, FscGetCacheElementCount); GUEST_FUNCTION_HOOK(__imp__FscSetCacheElementCount, FscSetCacheElementCount); GUEST_FUNCTION_HOOK(__imp__XamLoaderGetLaunchDataSize, XamLoaderGetLaunchDataSize); GUEST_FUNCTION_HOOK(__imp__XamLoaderGetLaunchData, XamLoaderGetLaunchData); GUEST_FUNCTION_HOOK(__imp__XamLoaderSetLaunchData, XamLoaderSetLaunchData); GUEST_FUNCTION_HOOK(__imp__NtWaitForSingleObjectEx, NtWaitForSingleObjectEx); GUEST_FUNCTION_HOOK(__imp__NtWriteFile, NtWriteFile); GUEST_FUNCTION_HOOK(__imp__ExGetXConfigSetting, ExGetXConfigSetting); GUEST_FUNCTION_HOOK(__imp__NtQueryVirtualMemory, NtQueryVirtualMemory); GUEST_FUNCTION_HOOK(__imp__MmQueryStatistics, MmQueryStatistics); GUEST_FUNCTION_HOOK(__imp__NtCreateEvent, NtCreateEvent); GUEST_FUNCTION_HOOK(__imp__XexCheckExecutablePrivilege, XexCheckExecutablePrivilege); GUEST_FUNCTION_HOOK(__imp__DbgPrint, DbgPrint); GUEST_FUNCTION_HOOK(__imp____C_specific_handler, __C_specific_handler_x); GUEST_FUNCTION_HOOK(__imp__RtlNtStatusToDosError, RtlNtStatusToDosError); GUEST_FUNCTION_HOOK(__imp__XexGetProcedureAddress, XexGetProcedureAddress); GUEST_FUNCTION_HOOK(__imp__XexGetModuleSection, XexGetModuleSection); GUEST_FUNCTION_HOOK(__imp__RtlUnicodeToMultiByteN, RtlUnicodeToMultiByteN); GUEST_FUNCTION_HOOK(__imp__KeDelayExecutionThread, KeDelayExecutionThread); GUEST_FUNCTION_HOOK(__imp__ExFreePool, ExFreePool); GUEST_FUNCTION_HOOK(__imp__NtQueryInformationFile, NtQueryInformationFile); GUEST_FUNCTION_HOOK(__imp__NtQueryVolumeInformationFile, NtQueryVolumeInformationFile); GUEST_FUNCTION_HOOK(__imp__NtQueryDirectoryFile, NtQueryDirectoryFile); GUEST_FUNCTION_HOOK(__imp__NtReadFileScatter, NtReadFileScatter); GUEST_FUNCTION_HOOK(__imp__NtReadFile, NtReadFile); GUEST_FUNCTION_HOOK(__imp__NtDuplicateObject, NtDuplicateObject); GUEST_FUNCTION_HOOK(__imp__NtAllocateVirtualMemory, NtAllocateVirtualMemory); GUEST_FUNCTION_HOOK(__imp__NtFreeVirtualMemory, NtFreeVirtualMemory); GUEST_FUNCTION_HOOK(__imp__ObDereferenceObject, ObDereferenceObject); GUEST_FUNCTION_HOOK(__imp__KeSetBasePriorityThread, KeSetBasePriorityThread); GUEST_FUNCTION_HOOK(__imp__ObReferenceObjectByHandle, ObReferenceObjectByHandle); GUEST_FUNCTION_HOOK(__imp__KeQueryBasePriorityThread, KeQueryBasePriorityThread); GUEST_FUNCTION_HOOK(__imp__NtSuspendThread, NtSuspendThread); GUEST_FUNCTION_HOOK(__imp__KeSetAffinityThread, KeSetAffinityThread); GUEST_FUNCTION_HOOK(__imp__RtlLeaveCriticalSection, RtlLeaveCriticalSection); GUEST_FUNCTION_HOOK(__imp__RtlEnterCriticalSection, RtlEnterCriticalSection); GUEST_FUNCTION_HOOK(__imp__RtlImageXexHeaderField, RtlImageXexHeaderField); GUEST_FUNCTION_HOOK(__imp__HalReturnToFirmware, HalReturnToFirmware); GUEST_FUNCTION_HOOK(__imp__RtlFillMemoryUlong, RtlFillMemoryUlong); GUEST_FUNCTION_HOOK(__imp__KeBugCheckEx, KeBugCheckEx); GUEST_FUNCTION_HOOK(__imp__KeGetCurrentProcessType, KeGetCurrentProcessType); GUEST_FUNCTION_HOOK(__imp__RtlCompareMemoryUlong, RtlCompareMemoryUlong); GUEST_FUNCTION_HOOK(__imp__RtlInitializeCriticalSection, RtlInitializeCriticalSection); GUEST_FUNCTION_HOOK(__imp__RtlRaiseException, RtlRaiseException_x); GUEST_FUNCTION_HOOK(__imp__KfReleaseSpinLock, KfReleaseSpinLock); GUEST_FUNCTION_HOOK(__imp__KfAcquireSpinLock, KfAcquireSpinLock); GUEST_FUNCTION_HOOK(__imp__KeQueryPerformanceFrequency, KeQueryPerformanceFrequency); GUEST_FUNCTION_HOOK(__imp__MmFreePhysicalMemory, MmFreePhysicalMemory); GUEST_FUNCTION_HOOK(__imp__VdPersistDisplay, VdPersistDisplay); GUEST_FUNCTION_HOOK(__imp__VdSwap, VdSwap); GUEST_FUNCTION_HOOK(__imp__VdGetSystemCommandBuffer, VdGetSystemCommandBuffer); GUEST_FUNCTION_HOOK(__imp__KeReleaseSpinLockFromRaisedIrql, KeReleaseSpinLockFromRaisedIrql); GUEST_FUNCTION_HOOK(__imp__KeAcquireSpinLockAtRaisedIrql, KeAcquireSpinLockAtRaisedIrql); GUEST_FUNCTION_HOOK(__imp__KiApcNormalRoutineNop, KiApcNormalRoutineNop); GUEST_FUNCTION_HOOK(__imp__VdEnableRingBufferRPtrWriteBack, VdEnableRingBufferRPtrWriteBack); GUEST_FUNCTION_HOOK(__imp__VdInitializeRingBuffer, VdInitializeRingBuffer); GUEST_FUNCTION_HOOK(__imp__MmGetPhysicalAddress, MmGetPhysicalAddress); GUEST_FUNCTION_HOOK(__imp__VdSetSystemCommandBufferGpuIdentifierAddress, VdSetSystemCommandBufferGpuIdentifierAddress); GUEST_FUNCTION_HOOK(__imp__ExRegisterTitleTerminateNotification, ExRegisterTitleTerminateNotification); GUEST_FUNCTION_HOOK(__imp__VdShutdownEngines, VdShutdownEngines); GUEST_FUNCTION_HOOK(__imp__VdQueryVideoMode, VdQueryVideoMode); GUEST_FUNCTION_HOOK(__imp__VdGetCurrentDisplayInformation, VdGetCurrentDisplayInformation); GUEST_FUNCTION_HOOK(__imp__VdSetDisplayMode, VdSetDisplayMode); GUEST_FUNCTION_HOOK(__imp__VdSetGraphicsInterruptCallback, VdSetGraphicsInterruptCallback); GUEST_FUNCTION_HOOK(__imp__VdInitializeEngines, VdInitializeEngines); GUEST_FUNCTION_HOOK(__imp__VdIsHSIOTrainingSucceeded, VdIsHSIOTrainingSucceeded); GUEST_FUNCTION_HOOK(__imp__VdGetCurrentDisplayGamma, VdGetCurrentDisplayGamma); GUEST_FUNCTION_HOOK(__imp__VdQueryVideoFlags, VdQueryVideoFlags); GUEST_FUNCTION_HOOK(__imp__VdCallGraphicsNotificationRoutines, VdCallGraphicsNotificationRoutines); GUEST_FUNCTION_HOOK(__imp__VdInitializeScalerCommandBuffer, VdInitializeScalerCommandBuffer); GUEST_FUNCTION_HOOK(__imp__KeLeaveCriticalRegion, KeLeaveCriticalRegion); GUEST_FUNCTION_HOOK(__imp__VdRetrainEDRAM, VdRetrainEDRAM); GUEST_FUNCTION_HOOK(__imp__VdRetrainEDRAMWorker, VdRetrainEDRAMWorker); GUEST_FUNCTION_HOOK(__imp__KeEnterCriticalRegion, KeEnterCriticalRegion); GUEST_FUNCTION_HOOK(__imp__MmAllocatePhysicalMemoryEx, MmAllocatePhysicalMemoryEx); GUEST_FUNCTION_HOOK(__imp__ObDeleteSymbolicLink, ObDeleteSymbolicLink); GUEST_FUNCTION_HOOK(__imp__ObCreateSymbolicLink, ObCreateSymbolicLink); GUEST_FUNCTION_HOOK(__imp__MmQueryAddressProtect, MmQueryAddressProtect); GUEST_FUNCTION_HOOK(__imp__VdEnableDisableClockGating, VdEnableDisableClockGating); GUEST_FUNCTION_HOOK(__imp__KeBugCheck, KeBugCheck); GUEST_FUNCTION_HOOK(__imp__KeLockL2, KeLockL2); GUEST_FUNCTION_HOOK(__imp__KeUnlockL2, KeUnlockL2); GUEST_FUNCTION_HOOK(__imp__KeSetEvent, KeSetEvent); GUEST_FUNCTION_HOOK(__imp__KeResetEvent, KeResetEvent); GUEST_FUNCTION_HOOK(__imp__KeWaitForSingleObject, KeWaitForSingleObject); GUEST_FUNCTION_HOOK(__imp__KeTlsGetValue, KeTlsGetValue); GUEST_FUNCTION_HOOK(__imp__KeTlsSetValue, KeTlsSetValue); GUEST_FUNCTION_HOOK(__imp__KeTlsAlloc, KeTlsAlloc); GUEST_FUNCTION_HOOK(__imp__KeTlsFree, KeTlsFree); GUEST_FUNCTION_HOOK(__imp__XMsgInProcessCall, XMsgInProcessCall); GUEST_FUNCTION_HOOK(__imp__XamUserReadProfileSettings, XamUserReadProfileSettings); GUEST_FUNCTION_HOOK(__imp__NetDll_WSAStartup, NetDll_WSAStartup); GUEST_FUNCTION_HOOK(__imp__NetDll_WSACleanup, NetDll_WSACleanup); GUEST_FUNCTION_HOOK(__imp__NetDll_socket, NetDll_socket); GUEST_FUNCTION_HOOK(__imp__NetDll_closesocket, NetDll_closesocket); GUEST_FUNCTION_HOOK(__imp__NetDll_setsockopt, NetDll_setsockopt); GUEST_FUNCTION_HOOK(__imp__NetDll_bind, NetDll_bind); GUEST_FUNCTION_HOOK(__imp__NetDll_connect, NetDll_connect); GUEST_FUNCTION_HOOK(__imp__NetDll_listen, NetDll_listen); GUEST_FUNCTION_HOOK(__imp__NetDll_accept, NetDll_accept); GUEST_FUNCTION_HOOK(__imp__NetDll_select, NetDll_select); GUEST_FUNCTION_HOOK(__imp__NetDll_recv, NetDll_recv); GUEST_FUNCTION_HOOK(__imp__NetDll_send, NetDll_send); GUEST_FUNCTION_HOOK(__imp__NetDll_inet_addr, NetDll_inet_addr); GUEST_FUNCTION_HOOK(__imp__NetDll___WSAFDIsSet, NetDll___WSAFDIsSet); GUEST_FUNCTION_HOOK(__imp__XMsgStartIORequestEx, XMsgStartIORequestEx); GUEST_FUNCTION_HOOK(__imp__XamInputGetCapabilities, XamInputGetCapabilities); GUEST_FUNCTION_HOOK(__imp__XamInputGetState, XamInputGetState); GUEST_FUNCTION_HOOK(__imp__XamInputSetState, XamInputSetState); GUEST_FUNCTION_HOOK(__imp__XexGetModuleHandle, XexGetModuleHandle); GUEST_FUNCTION_HOOK(__imp__RtlTryEnterCriticalSection, RtlTryEnterCriticalSection); GUEST_FUNCTION_HOOK(__imp__RtlInitializeCriticalSectionAndSpinCount, RtlInitializeCriticalSectionAndSpinCount); GUEST_FUNCTION_HOOK(__imp__XeCryptBnQwBeSigVerify, XeCryptBnQwBeSigVerify); GUEST_FUNCTION_HOOK(__imp__XeKeysGetKey, XeKeysGetKey); GUEST_FUNCTION_HOOK(__imp__XeCryptRotSumSha, XeCryptRotSumSha); GUEST_FUNCTION_HOOK(__imp__XeCryptSha, XeCryptSha); GUEST_FUNCTION_HOOK(__imp__KeEnableFpuExceptions, KeEnableFpuExceptions); GUEST_FUNCTION_HOOK(__imp__RtlUnwind, RtlUnwind_x); GUEST_FUNCTION_HOOK(__imp__RtlCaptureContext, RtlCaptureContext_x); GUEST_FUNCTION_HOOK(__imp__NtQueryFullAttributesFile, NtQueryFullAttributesFile); GUEST_FUNCTION_HOOK(__imp__RtlMultiByteToUnicodeN, RtlMultiByteToUnicodeN); GUEST_FUNCTION_HOOK(__imp__DbgBreakPoint, DbgBreakPoint); GUEST_FUNCTION_HOOK(__imp__MmQueryAllocationSize, MmQueryAllocationSize); GUEST_FUNCTION_HOOK(__imp__NtClearEvent, NtClearEvent); GUEST_FUNCTION_HOOK(__imp__NtResumeThread, NtResumeThread); GUEST_FUNCTION_HOOK(__imp__NtSetEvent, NtSetEvent); GUEST_FUNCTION_HOOK(__imp__NtCreateSemaphore, NtCreateSemaphore); GUEST_FUNCTION_HOOK(__imp__NtReleaseSemaphore, NtReleaseSemaphore); GUEST_FUNCTION_HOOK(__imp__NtWaitForMultipleObjectsEx, NtWaitForMultipleObjectsEx); GUEST_FUNCTION_HOOK(__imp__RtlCompareStringN, RtlCompareStringN); GUEST_FUNCTION_HOOK(__imp__StfsControlDevice, StfsControlDevice); GUEST_FUNCTION_HOOK(__imp__StfsCreateDevice, StfsCreateDevice); GUEST_FUNCTION_HOOK(__imp__NtFlushBuffersFile, NtFlushBuffersFile); GUEST_FUNCTION_HOOK(__imp__KeQuerySystemTime, KeQuerySystemTime); GUEST_FUNCTION_HOOK(__imp__RtlTimeToTimeFields, RtlTimeToTimeFields); GUEST_FUNCTION_HOOK(__imp__RtlFreeAnsiString, RtlFreeAnsiString); GUEST_FUNCTION_HOOK(__imp__RtlUnicodeStringToAnsiString, RtlUnicodeStringToAnsiString); GUEST_FUNCTION_HOOK(__imp__RtlInitUnicodeString, RtlInitUnicodeString); GUEST_FUNCTION_HOOK(__imp__ExTerminateThread, ExTerminateThread); GUEST_FUNCTION_HOOK(__imp__ExCreateThread, ExCreateThread); GUEST_FUNCTION_HOOK(__imp__IoInvalidDeviceRequest, IoInvalidDeviceRequest); GUEST_FUNCTION_HOOK(__imp__ObReferenceObject, ObReferenceObject); GUEST_FUNCTION_HOOK(__imp__IoCreateDevice, IoCreateDevice); GUEST_FUNCTION_HOOK(__imp__IoDeleteDevice, IoDeleteDevice); GUEST_FUNCTION_HOOK(__imp__ExAllocatePoolTypeWithTag, ExAllocatePoolTypeWithTag); GUEST_FUNCTION_HOOK(__imp__RtlTimeFieldsToTime, RtlTimeFieldsToTime); GUEST_FUNCTION_HOOK(__imp__IoCompleteRequest, IoCompleteRequest); GUEST_FUNCTION_HOOK(__imp__RtlUpcaseUnicodeChar, RtlUpcaseUnicodeChar); GUEST_FUNCTION_HOOK(__imp__ObIsTitleObject, ObIsTitleObject); GUEST_FUNCTION_HOOK(__imp__IoCheckShareAccess, IoCheckShareAccess); GUEST_FUNCTION_HOOK(__imp__IoSetShareAccess, IoSetShareAccess); GUEST_FUNCTION_HOOK(__imp__IoRemoveShareAccess, IoRemoveShareAccess); GUEST_FUNCTION_HOOK(__imp__NetDll_XNetStartup, NetDll_XNetStartup); GUEST_FUNCTION_HOOK(__imp__NetDll_XNetGetTitleXnAddr, NetDll_XNetGetTitleXnAddr); GUEST_FUNCTION_HOOK(__imp__KeWaitForMultipleObjects, KeWaitForMultipleObjects); GUEST_FUNCTION_HOOK(__imp__KeRaiseIrqlToDpcLevel, KeRaiseIrqlToDpcLevel); GUEST_FUNCTION_HOOK(__imp__KfLowerIrql, KfLowerIrql); GUEST_FUNCTION_HOOK(__imp__KeReleaseSemaphore, KeReleaseSemaphore); GUEST_FUNCTION_HOOK(__imp__XAudioGetVoiceCategoryVolume, XAudioGetVoiceCategoryVolume); GUEST_FUNCTION_HOOK(__imp__XAudioGetVoiceCategoryVolumeChangeMask, XAudioGetVoiceCategoryVolumeChangeMask); GUEST_FUNCTION_HOOK(__imp__KeResumeThread, KeResumeThread); GUEST_FUNCTION_HOOK(__imp__KeInitializeSemaphore, KeInitializeSemaphore); GUEST_FUNCTION_HOOK(__imp__XMAReleaseContext, XMAReleaseContext); GUEST_FUNCTION_HOOK(__imp__XMACreateContext, XMACreateContext); GUEST_FUNCTION_HOOK(__imp__XAudioRegisterRenderDriverClient, XAudioRegisterRenderDriverClient); GUEST_FUNCTION_HOOK(__imp__XAudioUnregisterRenderDriverClient, XAudioUnregisterRenderDriverClient); GUEST_FUNCTION_HOOK(__imp__XAudioSubmitRenderDriverFrame, XAudioSubmitRenderDriverFrame); ================================================ FILE: MarathonRecomp/kernel/io/file_system.cpp ================================================ #include "file_system.h" #include #include #include #include #include #include #include #include #include #include struct FileHandle : KernelObject { std::fstream stream; std::filesystem::path path; }; struct FindHandle : KernelObject { std::error_code ec; ankerl::unordered_dense::map> searchResult; // Relative path, file size, is directory decltype(searchResult)::iterator iterator; FindHandle(const std::string_view& path) { auto addDirectory = [&](const std::filesystem::path& directory) { for (auto& entry : std::filesystem::directory_iterator(directory, ec)) { std::u8string relativePath = entry.path().lexically_relative(directory).u8string(); searchResult.emplace(relativePath, std::make_pair(entry.is_directory(ec) ? 0 : entry.file_size(ec), entry.is_directory(ec))); } }; std::string_view pathNoPrefix = path; size_t index = pathNoPrefix.find(":\\"); if (index != std::string_view::npos) pathNoPrefix.remove_prefix(index + 2); // Force add a work folder to let the game see the files in mods, // if by some rare chance the user has no DLC or update files. if (pathNoPrefix.empty()) searchResult.emplace(u8"work", std::make_pair(0, true)); // Look for only work folder in mod folders, AR files cause issues. if (pathNoPrefix.starts_with("work")) { std::string pathStr(pathNoPrefix); std::replace(pathStr.begin(), pathStr.end(), '\\', '/'); for (size_t i = 0; ; i++) { auto* includeDirs = ModLoader::GetIncludeDirectories(i); if (includeDirs == nullptr) break; for (auto& includeDir : *includeDirs) addDirectory(includeDir / pathStr); } } addDirectory(FileSystem::ResolvePath(path, false)); iterator = searchResult.begin(); } void fillFindData(WIN32_FIND_DATAA* lpFindFileData) { if (iterator->second.second) lpFindFileData->dwFileAttributes = ByteSwap(FILE_ATTRIBUTE_DIRECTORY); else lpFindFileData->dwFileAttributes = ByteSwap(FILE_ATTRIBUTE_NORMAL); strncpy(lpFindFileData->cFileName, (const char *)(iterator->first.c_str()), sizeof(lpFindFileData->cFileName)); lpFindFileData->nFileSizeLow = ByteSwap(uint32_t(iterator->second.first >> 32U)); lpFindFileData->nFileSizeHigh = ByteSwap(uint32_t(iterator->second.first)); lpFindFileData->ftCreationTime = {}; lpFindFileData->ftLastAccessTime = {}; lpFindFileData->ftLastWriteTime = {}; } }; FileHandle* XCreateFileA ( const char* lpFileName, uint32_t dwDesiredAccess, uint32_t dwShareMode, void* lpSecurityAttributes, uint32_t dwCreationDisposition, uint32_t dwFlagsAndAttributes ) { assert(((dwDesiredAccess & ~(GENERIC_READ | GENERIC_WRITE | FILE_READ_DATA)) == 0) && "Unknown desired access bits."); assert(((dwShareMode & ~(FILE_SHARE_READ | FILE_SHARE_WRITE)) == 0) && "Unknown share mode bits."); assert(((dwCreationDisposition & ~(CREATE_NEW | CREATE_ALWAYS)) == 0) && "Unknown creation disposition bits."); std::filesystem::path filePath = FileSystem::ResolvePath(lpFileName, true); std::fstream fileStream; std::ios::openmode fileOpenMode = std::ios::binary; if (dwDesiredAccess & (GENERIC_READ | FILE_READ_DATA)) { fileOpenMode |= std::ios::in; } if (dwDesiredAccess & GENERIC_WRITE) { fileOpenMode |= std::ios::out; } fileStream.open(filePath, fileOpenMode); if (!fileStream.is_open()) { std::filesystem::path cachedPath = FindInPathCache(filePath.string()); if (!cachedPath.empty()) { fileStream.open(cachedPath, fileOpenMode); } } if (!fileStream.is_open()) { #ifdef _WIN32 GuestThread::SetLastError(GetLastError()); #else switch (errno) { case EACCES: GuestThread::SetLastError(ERROR_ACCESS_DENIED); break; case EEXIST: GuestThread::SetLastError(ERROR_FILE_EXISTS); break; case ENOENT: default: // Use ERROR_PATH_NOT_FOUND as a catch-all for other errors. GuestThread::SetLastError(ERROR_PATH_NOT_FOUND); break; } #endif return GetInvalidKernelObject(); } FileHandle *fileHandle = CreateKernelObject(); fileHandle->stream = std::move(fileStream); fileHandle->path = std::move(filePath); return fileHandle; } static uint32_t XGetFileSizeA(FileHandle* hFile, be* lpFileSizeHigh) { std::error_code ec; auto fileSize = std::filesystem::file_size(hFile->path, ec); if (!ec) { if (lpFileSizeHigh != nullptr) { *lpFileSizeHigh = uint32_t(fileSize >> 32U); } return (uint32_t)(fileSize); } return INVALID_FILE_SIZE; } uint32_t XGetFileSizeExA(FileHandle* hFile, LARGE_INTEGER* lpFileSize) { std::error_code ec; auto fileSize = std::filesystem::file_size(hFile->path, ec); if (!ec) { if (lpFileSize != nullptr) { lpFileSize->QuadPart = ByteSwap(fileSize); } return TRUE; } return FALSE; } uint32_t XReadFile ( FileHandle* hFile, void* lpBuffer, uint32_t nNumberOfBytesToRead, be* lpNumberOfBytesRead, XOVERLAPPED* lpOverlapped ) { uint32_t result = FALSE; if (lpOverlapped != nullptr) { std::streamoff streamOffset = lpOverlapped->Offset + (std::streamoff(lpOverlapped->OffsetHigh.get()) << 32U); hFile->stream.clear(); hFile->stream.seekg(streamOffset, std::ios::beg); if (hFile->stream.bad()) { return FALSE; } } uint32_t numberOfBytesRead; hFile->stream.read((char *)(lpBuffer), nNumberOfBytesToRead); if (!hFile->stream.bad()) { numberOfBytesRead = uint32_t(hFile->stream.gcount()); result = TRUE; } if (result) { if (lpOverlapped != nullptr) { lpOverlapped->Internal = 0; lpOverlapped->InternalHigh = numberOfBytesRead; } else if (lpNumberOfBytesRead != nullptr) { *lpNumberOfBytesRead = numberOfBytesRead; } } return result; } uint32_t XSetFilePointer(FileHandle* hFile, int32_t lDistanceToMove, be* lpDistanceToMoveHigh, uint32_t dwMoveMethod) { int32_t distanceToMoveHigh = lpDistanceToMoveHigh ? lpDistanceToMoveHigh->get() : 0; std::streamoff streamOffset = lDistanceToMove + (std::streamoff(distanceToMoveHigh) << 32U); std::fstream::seekdir streamSeekDir = {}; switch (dwMoveMethod) { case FILE_BEGIN: streamSeekDir = std::ios::beg; break; case FILE_CURRENT: streamSeekDir = std::ios::cur; break; case FILE_END: streamSeekDir = std::ios::end; break; default: assert(false && "Unknown move method."); break; } hFile->stream.clear(); hFile->stream.seekg(streamOffset, streamSeekDir); if (hFile->stream.bad()) { return INVALID_SET_FILE_POINTER; } std::streampos streamPos = hFile->stream.tellg(); if (lpDistanceToMoveHigh != nullptr) *lpDistanceToMoveHigh = int32_t(streamPos >> 32U); return uint32_t(streamPos); } uint32_t XSetFilePointerEx(FileHandle* hFile, int32_t lDistanceToMove, LARGE_INTEGER* lpNewFilePointer, uint32_t dwMoveMethod) { std::fstream::seekdir streamSeekDir = {}; switch (dwMoveMethod) { case FILE_BEGIN: streamSeekDir = std::ios::beg; break; case FILE_CURRENT: streamSeekDir = std::ios::cur; break; case FILE_END: streamSeekDir = std::ios::end; break; default: assert(false && "Unknown move method."); break; } hFile->stream.clear(); hFile->stream.seekg(lDistanceToMove, streamSeekDir); if (hFile->stream.bad()) { return FALSE; } if (lpNewFilePointer != nullptr) { lpNewFilePointer->QuadPart = ByteSwap(int64_t(hFile->stream.tellg())); } return TRUE; } FindHandle* XFindFirstFileA(const char* lpFileName, WIN32_FIND_DATAA* lpFindFileData) { std::string_view path = lpFileName; if (path.find("\\*") == (path.size() - 2) || path.find("/*") == (path.size() - 2)) { path.remove_suffix(1); } else if (path.find("\\*.*") == (path.size() - 4) || path.find("/*.*") == (path.size() - 4)) { path.remove_suffix(3); } else { assert(!std::filesystem::path(path).has_extension() && "Unknown search pattern."); } FindHandle findHandle(path); if (findHandle.searchResult.empty()) return GetInvalidKernelObject(); findHandle.fillFindData(lpFindFileData); return CreateKernelObject(std::move(findHandle)); } uint32_t XFindNextFileA(FindHandle* Handle, WIN32_FIND_DATAA* lpFindFileData) { Handle->iterator++; if (Handle->iterator == Handle->searchResult.end()) { return FALSE; } else { Handle->fillFindData(lpFindFileData); return TRUE; } } uint32_t XReadFileEx(FileHandle* hFile, void* lpBuffer, uint32_t nNumberOfBytesToRead, XOVERLAPPED* lpOverlapped, uint32_t lpCompletionRoutine) { uint32_t result = FALSE; uint32_t numberOfBytesRead; std::streamoff streamOffset = lpOverlapped->Offset + (std::streamoff(lpOverlapped->OffsetHigh.get()) << 32U); hFile->stream.clear(); hFile->stream.seekg(streamOffset, std::ios::beg); if (hFile->stream.bad()) return FALSE; hFile->stream.read((char *)(lpBuffer), nNumberOfBytesToRead); if (!hFile->stream.bad()) { numberOfBytesRead = uint32_t(hFile->stream.gcount()); result = TRUE; } if (result) { lpOverlapped->Internal = 0; lpOverlapped->InternalHigh = numberOfBytesRead; } return result; } uint32_t XGetFileAttributesA(const char* lpFileName) { std::filesystem::path filePath = FileSystem::ResolvePath(lpFileName, true); if (std::filesystem::is_directory(filePath)) return FILE_ATTRIBUTE_DIRECTORY; else if (std::filesystem::is_regular_file(filePath)) return FILE_ATTRIBUTE_NORMAL; else return INVALID_FILE_ATTRIBUTES; } uint32_t XWriteFile(FileHandle* hFile, const void* lpBuffer, uint32_t nNumberOfBytesToWrite, be* lpNumberOfBytesWritten, void* lpOverlapped) { assert(lpOverlapped == nullptr && "Overlapped not implemented."); hFile->stream.write((const char *)(lpBuffer), nNumberOfBytesToWrite); if (hFile->stream.bad()) return FALSE; if (lpNumberOfBytesWritten != nullptr) *lpNumberOfBytesWritten = uint32_t(hFile->stream.gcount()); return TRUE; } std::filesystem::path FileSystem::ResolvePath(const std::string_view& path, bool checkForMods) { LOGF_IMPL(Utility, "Game", "Loading file: \"{}\"", path.data()); if (checkForMods) { std::filesystem::path resolvedPath = ModLoader::ResolvePath(path); if (!resolvedPath.empty()) { if (ModLoader::s_isLogTypeConsole) LOGF_IMPL(Utility, "Mod Loader", "Loading file: \"{}\"", reinterpret_cast(resolvedPath.u8string().c_str())); return resolvedPath; } } thread_local std::string builtPath; builtPath.clear(); size_t index = path.find(":\\"); if (index != std::string::npos) { // rooted folder, handle direction const std::string_view root = path.substr(0, index); const auto newRoot = XamGetRootPath(root); if (!newRoot.empty()) { builtPath += newRoot; builtPath += '/'; } builtPath += path.substr(index + 2); } else { builtPath += path; } std::replace(builtPath.begin(), builtPath.end(), '\\', '/'); return std::u8string_view((const char8_t*)builtPath.c_str()); } GUEST_FUNCTION_HOOK(sub_82537400, XCreateFileA); // replaced GUEST_FUNCTION_HOOK(sub_826FD090, XGetFileSizeA); // replaced GUEST_FUNCTION_HOOK(sub_826FDC88, XGetFileSizeExA); // replaced GUEST_FUNCTION_HOOK(sub_82537118, XReadFile); // replaced GUEST_FUNCTION_HOOK(sub_825372B8, XSetFilePointer); // replaced // GUEST_FUNCTION_HOOK(sub_831CE888, XSetFilePointerEx); GUEST_FUNCTION_HOOK(sub_826F2570, XFindFirstFileA); // replaced GUEST_FUNCTION_HOOK(sub_826FD2B8, XFindNextFileA); // replaced // GUEST_FUNCTION_HOOK(sub_831CDF40, XReadFileEx); GUEST_FUNCTION_HOOK(sub_826FD250, XGetFileAttributesA); // replaced // GUEST_FUNCTION_HOOK(sub_831CE3F8, XCreateFileA); GUEST_FUNCTION_HOOK(sub_826FCBD0, XWriteFile); // replaced ================================================ FILE: MarathonRecomp/kernel/io/file_system.h ================================================ #pragma once struct FileSystem { static std::filesystem::path ResolvePath(const std::string_view& path, bool checkForMods); }; ================================================ FILE: MarathonRecomp/kernel/memory.cpp ================================================ #include #include "memory.h" Memory::Memory() { #ifdef _WIN32 base = (uint8_t*)VirtualAlloc((void*)0x100000000ull, PPC_MEMORY_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (base == nullptr) base = (uint8_t*)VirtualAlloc(nullptr, PPC_MEMORY_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (base == nullptr) return; DWORD oldProtect; VirtualProtect(base, 4096, PAGE_NOACCESS, &oldProtect); #else base = (uint8_t*)mmap((void*)0x100000000ull, PPC_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (base == (uint8_t*)MAP_FAILED) base = (uint8_t*)mmap(NULL, PPC_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (base == nullptr) return; mprotect(base, 4096, PROT_NONE); #endif for (size_t i = 0; PPCFuncMappings[i].guest != 0; i++) { if (PPCFuncMappings[i].host != nullptr) InsertFunction(PPCFuncMappings[i].guest, PPCFuncMappings[i].host); } } void* MmGetHostAddress(uint32_t ptr) { return g_memory.Translate(ptr); } ================================================ FILE: MarathonRecomp/kernel/memory.h ================================================ #pragma once #ifndef _WIN32 #define MEM_COMMIT 0x00001000 #define MEM_RESERVE 0x00002000 #endif struct Memory { uint8_t* base{}; Memory(); bool IsInMemoryRange(const void* host) const noexcept { return host >= base && host < (base + PPC_MEMORY_SIZE); } void* Translate(size_t offset) const noexcept { if (offset) assert(offset < PPC_MEMORY_SIZE); return base + offset; } uint32_t MapVirtual(const void* host) const noexcept { if (host) assert(IsInMemoryRange(host)); return static_cast(static_cast(host) - base); } PPCFunc* FindFunction(uint32_t guest) const noexcept { return PPC_LOOKUP_FUNC(base, guest); } void InsertFunction(uint32_t guest, PPCFunc* host) { PPC_LOOKUP_FUNC(base, guest) = host; } }; extern "C" void* MmGetHostAddress(uint32_t ptr); extern Memory g_memory; ================================================ FILE: MarathonRecomp/kernel/xam.cpp ================================================ #include #include #include #include "xam.h" #include "xdm.h" #include #include #include #include #include #include "xxHashMap.h" #include #include struct XamListener : KernelObject { uint32_t id{}; uint64_t areas{}; std::vector> notifications; XamListener(const XamListener&) = delete; XamListener& operator=(const XamListener&) = delete; XamListener(); ~XamListener(); }; struct XamEnumeratorBase : KernelObject { virtual uint32_t Next(void* buffer) { return -1; } }; template::iterator> struct XamEnumerator : XamEnumeratorBase { uint32_t fetch; size_t size; TIterator position; TIterator begin; TIterator end; XamEnumerator() = default; XamEnumerator(uint32_t fetch, size_t size, TIterator begin, TIterator end) : fetch(fetch), size(size), position(begin), begin(begin), end(end) { } uint32_t Next(void* buffer) override { if (position == end) { return -1; } if (buffer == nullptr) { for (size_t i = 0; i < fetch; i++) { if (position == end) { return i == 0 ? -1 : i; } ++position; } } for (size_t i = 0; i < fetch; i++) { if (position == end) { return i == 0 ? -1 : i; } memcpy(buffer, &*position, size); ++position; buffer = (void*)((size_t)buffer + size); } return fetch; } }; std::array, 3> gContentRegistry{}; std::unordered_set gListeners{}; xxHashMap gRootMap; std::string_view XamGetRootPath(const std::string_view& root) { const auto result = gRootMap.find(StringHash(root)); if (result == gRootMap.end()) return ""; return result->second; } void XamRootCreate(const std::string_view& root, const std::string_view& path) { gRootMap.emplace(StringHash(root), path); } XamListener::XamListener() { gListeners.insert(this); } XamListener::~XamListener() { gListeners.erase(this); } XCONTENT_DATA XamMakeContent(uint32_t type, const std::string_view& name) { XCONTENT_DATA data{ 1, type }; strncpy(data.szFileName, name.data(), sizeof(data.szFileName)); return data; } void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root) { const auto idx = data.dwContentType - 1; gContentRegistry[idx].emplace(StringHash(data.szFileName), XHOSTCONTENT_DATA{ data }).first->second.szRoot = root; } void XamRegisterContent(uint32_t type, const std::string_view name, const std::string_view& root) { XCONTENT_DATA data{ 1, type, {}, "" }; strncpy(data.szFileName, name.data(), sizeof(data.szFileName)); XamRegisterContent(data, root); } uint32_t XamNotifyCreateListener(uint64_t qwAreas) { auto* listener = CreateKernelObject(); listener->areas = qwAreas; return GetKernelHandle(listener); } void XamNotifyEnqueueEvent(uint32_t dwId, uint32_t dwParam) { for (const auto& listener : gListeners) { if (((1 << MSG_AREA(dwId)) & listener->areas) == 0) continue; listener->notifications.emplace_back(dwId, dwParam); } } bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be* pdwId, be* pParam) { auto& listener = *GetKernelObject(hNotification); if (dwMsgFilter) { for (size_t i = 0; i < listener.notifications.size(); i++) { if (std::get<0>(listener.notifications[i]) == dwMsgFilter) { if (pdwId) *pdwId = std::get<0>(listener.notifications[i]); if (pParam) *pParam = std::get<1>(listener.notifications[i]); listener.notifications.erase(listener.notifications.begin() + i); return true; } } } else { if (listener.notifications.empty()) return false; if (pdwId) *pdwId = std::get<0>(listener.notifications[0]); if (pParam) *pParam = std::get<1>(listener.notifications[0]); listener.notifications.erase(listener.notifications.begin()); return true; } return false; } uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be* wszTitle, be* wszText, uint32_t cButtons, xpointer>* pwszButtons, uint32_t dwFocusButton, uint32_t dwFlags, be* pResult, XXOVERLAPPED* pOverlapped) { *pResult = cButtons ? cButtons - 1 : 0; #if _DEBUG assert("XamShowMessageBoxUI encountered!" && false); #elif _WIN32 // This code is Win32-only as it'll most likely crash, misbehave or // cause corruption due to using a different type of memory than what // wchar_t is on Linux. Windows uses 2 bytes while Linux uses 4 bytes. std::vector texts{}; texts.emplace_back(reinterpret_cast(wszTitle)); texts.emplace_back(reinterpret_cast(wszText)); for (size_t i = 0; i < cButtons; i++) texts.emplace_back(reinterpret_cast(pwszButtons[i].get())); for (auto& text : texts) { for (size_t i = 0; i < text.size(); i++) ByteSwapInplace(text[i]); } wprintf(L"[XamShowMessageBoxUI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); wprintf(L"[XamShowMessageBoxUI] If you are encountering this message and the game has ceased functioning,\n"); wprintf(L"[XamShowMessageBoxUI] please create an issue at https://github.com/sonicnext-dev/MarathonRecomp/issues.\n"); wprintf(L"[XamShowMessageBoxUI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); wprintf(L"[XamShowMessageBoxUI] %ls\n", texts[0].c_str()); wprintf(L"[XamShowMessageBoxUI] %ls\n", texts[1].c_str()); wprintf(L"[XamShowMessageBoxUI] "); for (size_t i = 0; i < cButtons; i++) { wprintf(L"%ls", texts[2 + i].c_str()); if (i != cButtons - 1) wprintf(L" | "); } wprintf(L"\n"); wprintf(L"[XamShowMessageBoxUI] Defaulted to button: %d\n", pResult->get()); wprintf(L"[XamShowMessageBoxUI] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); #endif if (pOverlapped) { pOverlapped->dwCompletionContext = GuestThread::GetCurrentThreadId(); pOverlapped->Error = 0; pOverlapped->Length = -1; } XamNotifyEnqueueEvent(9, 0); return 0; } uint32_t XamContentCreateEnumerator(uint32_t dwUserIndex, uint32_t DeviceID, uint32_t dwContentType, uint32_t dwContentFlags, uint32_t cItem, be* pcbBuffer, be* phEnum) { if (dwUserIndex != 0) { GuestThread::SetLastError(ERROR_NO_SUCH_USER); return 0xFFFFFFFF; } const auto& registry = gContentRegistry[dwContentType - 1]; const auto& values = registry | std::views::values; auto* enumerator = CreateKernelObject>(cItem, sizeof(_XCONTENT_DATA), values.begin(), values.end()); if (pcbBuffer) *pcbBuffer = sizeof(_XCONTENT_DATA) * cItem; *phEnum = GetKernelHandle(enumerator); return 0; } uint32_t XamEnumerate(uint32_t hEnum, uint32_t dwFlags, void* pvBuffer, uint32_t cbBuffer, be* pcItemsReturned, XXOVERLAPPED* pOverlapped) { auto* enumerator = GetKernelObject(hEnum); const auto count = enumerator->Next(pvBuffer); if (count == -1) return ERROR_NO_MORE_FILES; if (pcItemsReturned) *pcItemsReturned = count; return ERROR_SUCCESS; } // Patch DLC enumeration format string to allow for whitespace PPC_FUNC_IMPL(__imp__sub_825B14B8); PPC_FUNC(sub_825B14B8) { const char* _str = (const char*)g_memory.Translate(ctx.r3.u32); uint32_t _1; char _str_end[255]; sscanf(_str, "download:\\%08x\\%255[^\n\r]", &_1, _str_end); XCONTENT_DATA _data; _data.dwContentType = 2; _data.DeviceID = _1; memcpy(&_data.szFileName, &_str_end, strlen(_str_end) + 1); XamContentCreateEx(0, "download", &_data, 3, 0, 0, 0, 0, 0); } uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const XCONTENT_DATA* pContentData, uint32_t dwContentFlags, be* pdwDisposition, be* pdwLicenseMask, uint32_t dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped) { const auto& registry = gContentRegistry[pContentData->dwContentType - 1]; const auto exists = registry.contains(StringHash(pContentData->szFileName)); const auto mode = dwContentFlags & 0xF; if (mode == CREATE_ALWAYS) { if (pdwDisposition) *pdwDisposition = XCONTENT_NEW; if (!exists) { std::filesystem::path rootPath; if (pContentData->dwContentType == XCONTENTTYPE_SAVEDATA) { rootPath = GetSavePath(true); } else if (pContentData->dwContentType == XCONTENTTYPE_DLC) { rootPath = GetGamePath() / "dlc"; } else { rootPath = GetGamePath(); } const std::string root = (const char*)rootPath.u8string().c_str(); XamRegisterContent(*pContentData, root); std::error_code ec; std::filesystem::create_directory(rootPath, ec); XamRootCreate(szRootName, root); } else { XamRootCreate(szRootName, registry.find(StringHash(pContentData->szFileName))->second.szRoot); } return ERROR_SUCCESS; } if (mode == OPEN_EXISTING) { if (exists) { if (pdwDisposition) *pdwDisposition = XCONTENT_EXISTING; XamRootCreate(szRootName, registry.find(StringHash(pContentData->szFileName))->second.szRoot); return ERROR_SUCCESS; } else { if (pdwDisposition) *pdwDisposition = XCONTENT_NEW; return ERROR_PATH_NOT_FOUND; } } return ERROR_PATH_NOT_FOUND; } uint32_t XamContentClose(const char* szRootName, XXOVERLAPPED* pOverlapped) { gRootMap.erase(StringHash(szRootName)); return 0; } uint32_t XamContentGetDeviceData(uint32_t DeviceID, XDEVICE_DATA* pDeviceData) { pDeviceData->DeviceID = DeviceID; pDeviceData->DeviceType = XCONTENTDEVICETYPE_HDD; pDeviceData->ulDeviceBytes = 0x10000000; pDeviceData->ulDeviceFreeBytes = 0x10000000; pDeviceData->wszName[0] = 'S'; pDeviceData->wszName[1] = 'o'; pDeviceData->wszName[2] = 'n'; pDeviceData->wszName[3] = 'i'; pDeviceData->wszName[4] = 'c'; pDeviceData->wszName[5] = '\0'; return 0; } uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint32_t flags, XAMINPUT_CAPABILITIES* caps) { if (userIndex != 0) return ERROR_NO_SUCH_USER; uint32_t result = hid::GetCapabilities(userIndex, caps); if (result == ERROR_SUCCESS) { ByteSwapInplace(caps->Flags); ByteSwapInplace(caps->Gamepad.wButtons); ByteSwapInplace(caps->Gamepad.sThumbLX); ByteSwapInplace(caps->Gamepad.sThumbLY); ByteSwapInplace(caps->Gamepad.sThumbRX); ByteSwapInplace(caps->Gamepad.sThumbRY); ByteSwapInplace(caps->Vibration.wLeftMotorSpeed); ByteSwapInplace(caps->Vibration.wRightMotorSpeed); } return result; } uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state) { if (userIndex != 0) return ERROR_NO_SUCH_USER; memset(state, 0, sizeof(*state)); if (hid::IsInputAllowed()) hid::GetState(userIndex, state); auto keyboardState = SDL_GetKeyboardState(NULL); if (GameWindow::s_isFocused && !keyboardState[SDL_SCANCODE_LALT]) { if (keyboardState[Config::Key_LeftStickUp]) state->Gamepad.sThumbLY = 32767; if (keyboardState[Config::Key_LeftStickDown]) state->Gamepad.sThumbLY = -32768; if (keyboardState[Config::Key_LeftStickLeft]) state->Gamepad.sThumbLX = -32768; if (keyboardState[Config::Key_LeftStickRight]) state->Gamepad.sThumbLX = 32767; if (keyboardState[Config::Key_RightStickUp]) state->Gamepad.sThumbRY = 32767; if (keyboardState[Config::Key_RightStickDown]) state->Gamepad.sThumbRY = -32768; if (keyboardState[Config::Key_RightStickLeft]) state->Gamepad.sThumbRX = -32768; if (keyboardState[Config::Key_RightStickRight]) state->Gamepad.sThumbRX = 32767; if (keyboardState[Config::Key_LeftTrigger]) state->Gamepad.bLeftTrigger = 0xFF; if (keyboardState[Config::Key_RightTrigger]) state->Gamepad.bRightTrigger = 0xFF; if (keyboardState[Config::Key_DPadUp]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_UP; if (keyboardState[Config::Key_DPadDown]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_DOWN; if (keyboardState[Config::Key_DPadLeft]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_LEFT; if (keyboardState[Config::Key_DPadRight]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_DPAD_RIGHT; if (keyboardState[Config::Key_Start]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_START; if (keyboardState[Config::Key_Back]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_BACK; if (keyboardState[Config::Key_LeftBumper]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_LEFT_SHOULDER; if (keyboardState[Config::Key_RightBumper]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_RIGHT_SHOULDER; if (keyboardState[Config::Key_A]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_A; if (keyboardState[Config::Key_B]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_B; if (keyboardState[Config::Key_X]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_X; if (keyboardState[Config::Key_Y]) state->Gamepad.wButtons |= XAMINPUT_GAMEPAD_Y; } state->Gamepad.wButtons &= ~hid::g_prohibitedButtons; if (hid::g_isLeftStickProhibited) { state->Gamepad.sThumbLX = 0; state->Gamepad.sThumbLY = 0; } if (hid::g_isRightStickProhibited) { state->Gamepad.sThumbRX = 0; state->Gamepad.sThumbRY = 0; } ByteSwapInplace(state->Gamepad.wButtons); ByteSwapInplace(state->Gamepad.sThumbLX); ByteSwapInplace(state->Gamepad.sThumbLY); ByteSwapInplace(state->Gamepad.sThumbRX); ByteSwapInplace(state->Gamepad.sThumbRY); return ERROR_SUCCESS; } uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration) { if (userIndex != 0) return ERROR_NO_SUCH_USER; if (!hid::IsInputDeviceController()) return ERROR_SUCCESS; ByteSwapInplace(vibration->wLeftMotorSpeed); ByteSwapInplace(vibration->wRightMotorSpeed); return hid::SetState(userIndex, vibration); } ================================================ FILE: MarathonRecomp/kernel/xam.h ================================================ #pragma once #include #define MSGID(Area, Number) (uint32_t)((uint16_t)(Area) << 16 | (uint16_t)(Number)) #define MSG_AREA(msgid) (((msgid) >> 16) & 0xFFFF) #define MSG_NUMBER(msgid) ((msgid) & 0xFFFF) XCONTENT_DATA XamMakeContent(uint32_t type, const std::string_view& name); void XamRegisterContent(const XCONTENT_DATA& data, const std::string_view& root); std::string_view XamGetRootPath(const std::string_view& root); void XamRootCreate(const std::string_view& root, const std::string_view& path); uint32_t XamNotifyCreateListener(uint64_t qwAreas); void XamNotifyEnqueueEvent(uint32_t dwId, uint32_t dwParam); // i made it the fuck up bool XNotifyGetNext(uint32_t hNotification, uint32_t dwMsgFilter, be* pdwId, be* pParam); uint32_t XamShowMessageBoxUI(uint32_t dwUserIndex, be* wszTitle, be* wszText, uint32_t cButtons, xpointer>* pwszButtons, uint32_t dwFocusButton, uint32_t dwFlags, be* pResult, XXOVERLAPPED* pOverlapped); uint32_t XamContentCreateEnumerator(uint32_t dwUserIndex, uint32_t DeviceID, uint32_t dwContentType, uint32_t dwContentFlags, uint32_t cItem, be* pcbBuffer, be* phEnum); uint32_t XamEnumerate(uint32_t hEnum, uint32_t dwFlags, void* pvBuffer, uint32_t cbBuffer, be* pcItemsReturned, XXOVERLAPPED* pOverlapped); uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const XCONTENT_DATA* pContentData, uint32_t dwContentFlags, be* pdwDisposition, be* pdwLicenseMask, uint32_t dwFileCacheSize, uint64_t uliContentSize, PXXOVERLAPPED pOverlapped); uint32_t XamContentGetDeviceData(uint32_t DeviceID, XDEVICE_DATA* pDeviceData); uint32_t XamContentClose(const char* szRootName, XXOVERLAPPED* pOverlapped); uint32_t XamInputGetCapabilities(uint32_t unk, uint32_t userIndex, uint32_t flags, XAMINPUT_CAPABILITIES* caps); uint32_t XamInputGetState(uint32_t userIndex, uint32_t flags, XAMINPUT_STATE* state); uint32_t XamInputSetState(uint32_t userIndex, uint32_t flags, XAMINPUT_VIBRATION* vibration); ================================================ FILE: MarathonRecomp/kernel/xdbf.h ================================================ #pragma once #include #include extern XDBFWrapper g_xdbfWrapper; extern std::unordered_map g_xdbfTextureCache; namespace xdbf { inline std::string FixInvalidSequences(std::string& str) { std::string result; result.reserve(str.size()); for (size_t i = 0; i < str.size(); ++i) { auto& c = str[i]; if (c == '\r' || c == '\n') { // Remove spaces before line breaks. while (!result.empty() && result.back() == ' ') result.pop_back(); // Skip duplicate line breaks. while (i + 1 < str.size() && (str[i + 1] == '\r' || str[i + 1] == '\n')) ++i; // Add a space if the next char isn't one. if (i + 1 < str.size() && str[i + 1] != ' ') result += ' '; } else { result += c; } } return result; } } ================================================ FILE: MarathonRecomp/kernel/xdm.cpp ================================================ #include #include "xdm.h" #include "freelist.h" Mutex g_kernelLock; void DestroyKernelObject(KernelObject* obj) { obj->~KernelObject(); g_userHeap.Free(obj); } uint32_t GetKernelHandle(KernelObject* obj) { assert(obj != GetInvalidKernelObject()); return g_memory.MapVirtual(obj); } void DestroyKernelObject(uint32_t handle) { DestroyKernelObject(GetKernelObject(handle)); } bool IsKernelObject(uint32_t handle) { return (handle & 0x80000000) != 0; } bool IsKernelObject(void* obj) { return IsKernelObject(g_memory.MapVirtual(obj)); } bool IsInvalidKernelObject(void* obj) { return obj == GetInvalidKernelObject(); } ================================================ FILE: MarathonRecomp/kernel/xdm.h ================================================ #pragma once #include "heap.h" #include "memory.h" #define OBJECT_SIGNATURE (uint32_t)'XBOX' #define GUEST_INVALID_HANDLE_VALUE 0xFFFFFFFF #ifndef _WIN32 #define S_OK 0x00000000 #define FALSE 0x00000000 #define TRUE 0x00000001 #define STATUS_SUCCESS 0x00000000 #define STATUS_WAIT_0 0x00000000 #define STATUS_USER_APC 0x000000C0 #define STATUS_TIMEOUT 0x00000102 #define STATUS_NOT_IMPLEMENTED 0xC0000002 #define STATUS_SEMAPHORE_LIMIT_EXCEEDED 0xC0000047 #define STATUS_FAIL_CHECK 0xC0000229 #define INFINITE 0xFFFFFFFF #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define GENERIC_READ 0x80000000 #define GENERIC_WRITE 0x40000000 #define FILE_READ_DATA 0x0001 #define FILE_SHARE_READ 0x00000001 #define FILE_SHARE_WRITE 0x00000002 #define CREATE_NEW 1 #define CREATE_ALWAYS 2 #define OPEN_EXISTING 3 #define INVALID_FILE_SIZE 0xFFFFFFFF #define INVALID_SET_FILE_POINTER 0xFFFFFFFF #define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF #define FILE_BEGIN 0 #define FILE_CURRENT 1 #define FILE_END 2 #define ERROR_NO_MORE_FILES 0x12 #define ERROR_NO_SUCH_USER 0x525 #define ERROR_SUCCESS 0x0 #define ERROR_PATH_NOT_FOUND 0x3 #define ERROR_ACCESS_DENIED 0x5 #define ERROR_FILE_EXISTS 0x50 #define ERROR_CALL_NOT_IMPLEMENTED 0x78 #define ERROR_BAD_ARGUMENTS 0xA0 #define ERROR_TOO_MANY_POSTS 0x12A #define ERROR_DEVICE_NOT_CONNECTED 0x48F #define PAGE_READWRITE 0x04 typedef union _LARGE_INTEGER { struct { uint32_t LowPart; int32_t HighPart; }; struct { uint32_t LowPart; int32_t HighPart; } u; int64_t QuadPart; } LARGE_INTEGER; static_assert(sizeof(LARGE_INTEGER) == 8); typedef struct _FILETIME { uint32_t dwLowDateTime; uint32_t dwHighDateTime; } FILETIME; static_assert(sizeof(FILETIME) == 8); typedef struct _WIN32_FIND_DATAA { uint32_t dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; uint32_t nFileSizeHigh; uint32_t nFileSizeLow; uint32_t dwReserved0; uint32_t dwReserved1; char cFileName[260]; char cAlternateFileName[14]; } WIN32_FIND_DATAA; static_assert(sizeof(WIN32_FIND_DATAA) == 320); #endif struct KernelObject { virtual ~KernelObject() { } virtual uint32_t Wait(uint32_t timeout) { assert(false && "Wait not implemented for this kernel object."); return STATUS_TIMEOUT; } }; template inline T* CreateKernelObject(Args&&... args) { static_assert(std::is_base_of_v); return g_userHeap.AllocPhysical(std::forward(args)...); } template inline T* GetKernelObject(uint32_t handle) { assert(handle != GUEST_INVALID_HANDLE_VALUE); return reinterpret_cast(g_memory.Translate(handle)); } uint32_t GetKernelHandle(KernelObject* obj); void DestroyKernelObject(KernelObject* obj); void DestroyKernelObject(uint32_t handle); bool IsKernelObject(uint32_t handle); bool IsKernelObject(void* obj); bool IsInvalidKernelObject(void* obj); template inline T* GetInvalidKernelObject() { return reinterpret_cast(g_memory.Translate(GUEST_INVALID_HANDLE_VALUE)); } extern Mutex g_kernelLock; template inline T* QueryKernelObject(XDISPATCHER_HEADER& header) { std::lock_guard guard{ g_kernelLock }; if (header.WaitListHead.Flink != OBJECT_SIGNATURE) { header.WaitListHead.Flink = OBJECT_SIGNATURE; auto* obj = CreateKernelObject(reinterpret_cast(&header)); header.WaitListHead.Blink = g_memory.MapVirtual(obj); return obj; } return static_cast(g_memory.Translate(header.WaitListHead.Blink.get())); } // Get object without initialisation template inline T* TryQueryKernelObject(XDISPATCHER_HEADER& header) { if (header.WaitListHead.Flink != OBJECT_SIGNATURE) return nullptr; return static_cast(g_memory.Translate(header.WaitListHead.Blink.get())); } ================================================ FILE: MarathonRecomp/locale/achievement_locale.cpp ================================================ #include #include std::unordered_map> g_achLocale = { // Sonic Episode: Cleared { 1, { { ELanguage::English, { "Sonic's Episode: Cleared", "Clear Sonic's episode!", "Cleared Sonic's episode." } }, { ELanguage::Japanese, { "ソニックエピソードクリア", "ソニックのエピソードをクリアせよ!", "ソニックのエピソードをクリアした" } }, { ELanguage::German, { "Sonic's Episode: Abgeschlossen", "Schließe Sonic's Episode ab!", "Sonic's Episode abgeschlossen." } }, { ELanguage::French, { "Episode de Sonic : réussi", "Réussir l'épisode de Sonic!", "Réussissez l'épisode de Sonic." } }, { ELanguage::Spanish, { "Episodio de Sonic: Superado", "¡Supera el episodio de Sonic!", "Has superado el episodio de Sonic." } }, { ELanguage::Italian, { "Episodio di Sonic: completato", "Completa l'episodio di Sonic!", "Hai completato l'episodio di Sonic." } }, } }, // Shadow Episode: Cleared { 2, { { ELanguage::English, { "Shadow's Episode: Cleared", "Clear Shadow's episode!", "Cleared Shadow's episode." } }, { ELanguage::Japanese, { "シャドウエピソードクリア", "シャドウのエピソードをクリアせよ!", "シャドウのエピソードをクリアした" } }, { ELanguage::German, { "Shadow's Episode: Abgeschlossen", "Schließe Shadow's Episode ab!", "Shadow's Episode abgeschlossen." } }, { ELanguage::French, { "Episode de Shadow : réussi", "Réussir l'épisode de Shadow!", "Réussissez l'épisode de Shadow." } }, { ELanguage::Spanish, { "Episodio de Shadow: Superado", "¡Supera el episodio de Shadow!", "Has superado el episodio de Shadow." } }, { ELanguage::Italian, { "Episodio di Shadow: completato", "Completa l'episodio di Shadow!", "Hai completato l'episodio di Shadow." } }, } }, // Silver Episode: Cleared { 3, { { ELanguage::English, { "Silver's Episode: Cleared", "Clear Silver's episode!", "Cleared Silver's episode." } }, { ELanguage::Japanese, { "シルバーエピソードクリア", "シルバーのエピソードをクリアせよ!", "シルバーのエピソードをクリアした" } }, { ELanguage::German, { "Silver's Episode: Abgeschlossen", "Schließe Silver's Episode ab!", "Silver's Episode abgeschlossen." } }, { ELanguage::French, { "Episode de Silver : réussi", "Réussir l'épisode de Silver!", "Réussissez l'épisode de Silver." } }, { ELanguage::Spanish, { "Episodio de Silver: Superado", "¡Supera el episodio de Silver!", "Has superado el episodio de Silver." } }, { ELanguage::Italian, { "Episodio di Silver: completato", "Completa l'episodio di Silver!", "Hai completato l'episodio di Silver." } }, } }, // One to reach the end { 4, { { ELanguage::English, { "One To Reach The End", "Clear the last hidden episode!", "Cleared the last episode." } }, { ELanguage::Japanese, { "終わりを刻む者", "ラスト隠れたエピソードをクリアせよ!", "ラストエピソードをクリアした" } }, { ELanguage::German, { "Das Ende erreicht", "Schließe die letzte versteckte Episode ab!", "Die letzte Episode abgeschlossen." } }, { ELanguage::French, { "A atteint la fin", "Réussir dans l'épisode secret final!", "Réussissez dans l'épisode final." } }, { ELanguage::Spanish, { "Llegar hasta el final", "¡Supera el último episodio secreto!", "Has superado el último episodio." } }, { ELanguage::Italian, { "Colui che giunse alla fine", "Concludi l'ultimo episodio nascosto!", "Hai completato l'ultimo episodio." } }, } }, // Sonic Episode: Completed { 5, { { ELanguage::English, { "Sonic's Episode: Completed", "Clear all of Sonic's hard ACT missions.", "Cleared all of Sonic's ACT missions." } }, { ELanguage::Japanese, { "ソニックエピソードコンプリート", "ソニックのACTミッションを全てクリアせよ", "ソニックのACTミッションを全てクリアした" } }, { ELanguage::German, { "Sonic's Episode: absolviert", "Absolviere alle von Sonic's AKT-Missionen auf der schwierigsten Stufe.", "Alle von Sonic's AKT-Missionen absolviert." } }, { ELanguage::French, { "Episode de Sonic : terminé", "Réussir toutes les missions ACTE difficiles de Sonic.", "Réussissez toutes les missions ACTE de Sonic." } }, { ELanguage::Spanish, { "Episodio de Sonic: Completado", "Supera todas las misiones ACTO de Sonic en modo difícil.", "Has superado todas las misiones ACTO de Sonic." } }, { ELanguage::Italian, { "Episodio di Sonic: superato", "Completa tutte le missioni ATTO difficili di Sonic.", "Hai completato tutte le missioni ATTO difficili di Sonic." } }, } }, // Shadow Episode: Completed { 6, { { ELanguage::English, { "Shadow's Episode: Completed", "Clear all of Shadow's hard ACT missions.", "Cleared all of Shadow's ACT missions." } }, { ELanguage::Japanese, { "シャドウエピソードコンプリート", "シャドウのACTミッションを全てクリアせよ", "シャドウのACTミッションを全てクリアした" } }, { ELanguage::German, { "Shadow's Episode: absolviert", "Absolviere alle von Shadow's AKT-Missionen auf der schwierigsten Stufe.", "Alle von Shadow's AKT-Missionen absolviert." } }, { ELanguage::French, { "Episode de Shadow : terminé", "Réussir toutes les missions ACTE difficiles de Shadow.", "Réussissez toutes les missions ACTE difficiles de Shadow." } }, { ELanguage::Spanish, { "Episodio de Shadow: Completado", "Supera todas las misiones ACTO de Shadow en modo difícil.", "Has superado todas las misiones ACTO de Shadow." } }, { ELanguage::Italian, { "Episodio di Shadow: superato", "Completa tutte le missioni ATTO difficili di Shadow.", "Hai completato tutte le missioni ATTO difficili di Shadow." } }, } }, // Silver Episode: Completed { 7, { { ELanguage::English, { "Silver's Episode: Completed", "Clear all of Silver's hard ACT missions.", "Cleared all of Silver's ACT missions." } }, { ELanguage::Japanese, { "シルバーエピソードコンプリート", "シルバーのACTミッションを全てクリアせよ", "シルバーのACTミッションを全てクリアした" } }, { ELanguage::German, { "Silver's Episode: absolviert", "Absolviere alle von Silver's AKT-Missionen auf der schwierigsten Stufe.", "Alle von Silver's AKT-Missionen absolviert." } }, { ELanguage::French, { "Episode de Silver : terminé", "Réussir toutes les missions ACTE difficiles de Silver.", "Réussissez toutes les missions ACTE difficiles de Silver." } }, { ELanguage::Spanish, { "Episodio de Silver: Completado", "Supera todas las misiones ACTO de Silver en modo difícil.", "Has superado todas las misiones ACTO de Silver." } }, { ELanguage::Italian, { "Episodio di Silver: superato", "Completa tutte le missioni ATTO difficili di Silver.", "Hai completato tutte le missioni ATTO difficili di Silver." } }, } }, // Shadow Episode: Mastered { 8, { { ELanguage::English, { "Shadow's Episode: Mastered", "Clear all of Shadow's ACT missions with Rank S.", "Achieved Rank S on all of Shadow's ACT missions." } }, { ELanguage::Japanese, { "シャドウエピソードマスター", "シャドウのACTミッションを全てランクSでクリアせよ", "シャドウのACTミッションを全てランクSでクリアした" } }, { ELanguage::German, { "Shadow's Episode: gemeistert", "Schließe alle von Shadow's AKT-Missionen mit einen S-Rang ab.", "Einen S-Rang bei allen AKT-Missionen von Shadow erreicht." } }, { ELanguage::French, { "Episode de Shadow : dominé", "Terminez toutes les missions ACTE de Shadow avec un rang S.", "Obtenu un rang S dans toutes les missions ACTE de Shadow." } }, { ELanguage::Spanish, { "Episodio de Shadow: Dominado", "Supera todas las misiones ACTO de Shadow con rango S.", "Has conseguido el rango S en todas las misiones ACTO de Shadow." } }, { ELanguage::Italian, { "Episodio di Shadow: stravinto", "Completa tutte le missioni ATTO di Shadow in posizione S.", "Hai ottenuto la posizione S in tutte le missioni ATTO di Shadow." } }, } }, // Sonic Episode: Mastered { 9, { { ELanguage::English, { "Sonic's Episode: Mastered", "Clear all of Sonic's ACT missions with Rank S.", "Achieved Rank S on all of Sonic's ACT missions." } }, { ELanguage::Japanese, { "ソニックエピソードマスター", "ソニックのACTミッションを全てランクSでクリアせよ", "ソニックのACTミッションを全てランクSでクリアした" } }, { ELanguage::German, { "Sonic's Episode: gemeistert", "Schließe alle von Sonic's AKT-Missionen mit einen S-Rang ab.", "Einen S-Rang bei allen AKT-Missionen von Sonic erreicht." } }, { ELanguage::French, { "Episode de Sonic : dominé", "Terminez toutes les missions ACTE de Sonic avec un rang S.", "Obtenu un rang S dans toutes les missions ACTE de Sonic." } }, { ELanguage::Spanish, { "Episodio de Sonic: Dominado", "Supera todas las misiones ACTO de Sonic con rango S.", "Has conseguido el rango S en todas las misiones ACTO de Sonic." } }, { ELanguage::Italian, { "Episodio di Sonic: stravinto", "Completa tutte le missioni ATTO di Sonic in posizione S.", "Hai ottenuto la posizione S in tutte le missioni ATTO di Sonic." } }, } }, // Silver Episode: Mastered { 10, { { ELanguage::English, { "Silver's Episode: Mastered", "Clear all of Silver's ACT missions with Rank S.", "Achieved Rank S on all of Silver's ACT missions." } }, { ELanguage::Japanese, { "シルバーエピソードマスター", "シルバーのACTミッションを全てランクSでクリアせよ", "シルバーのACTミッションを全てランクSでクリアした" } }, { ELanguage::German, { "Silver's Episode: gemeistert", "Schließe alle von Silver's AKT-Missionen mit einen S-Rang ab.", "Einen S-Rang bei allen AKT-Missionen von Silver erreicht." } }, { ELanguage::French, { "Episode de Silver : dominé", "Terminez toutes les missions ACTE de Silver avec un rang S.", "Obtenu un rang S dans toutes les missions ACTE de Silver." } }, { ELanguage::Spanish, { "Episodio de Silver: Dominado", "Supera todas las misiones ACTO de Silver con rango S.", "Has conseguido el rango S en todas las misiones ACTO de Silver." } }, { ELanguage::Italian, { "Episodio di Silver: stravinto", "Completa tutte le missioni ATTO di Silver in posizione S.", "Hai ottenuto la posizione S in tutte le missioni ATTO di Silver." } }, } }, // Nights of Kronos { 11, { { ELanguage::English, { "Knights of Kronos", "Clear all of the last hidden episode with Rank S.", "Achieved Rank S on all of the last episode." } }, { ELanguage::Japanese, { "ナイツオブクロノス", "ラスト隠れたエピソードを全てランクSでクリアせよ", "ラストエピソードを全てランクSでクリアした" } }, { ELanguage::German, { "Ritter von Kronos", "Schließe alles von der letzten versteckten Episode mit einem S-Rang ab.", "Einen S-Rang in der letzten Episode erreicht." } }, { ELanguage::French, { "Chevaliers de Kronos", "Terminez toutes l'épisode secret final avec un rang S.", "Obtenu un rang S dans toutes l'épisode secret final." } }, { ELanguage::Spanish, { "Caballeros de Kronos", "Supera todo el último episodio secreto con rango S.", "Has conseguido el rango S en todo el último episodio." } }, { ELanguage::Italian, { "Cavalieri di Kronos", "Completa tutto l'ultimo episodio nascosto in posizione S.", "Hai ottenuto la posizione S in tutto l'ultimo episodio." } }, } }, // Legend of Soleanna { 12, { { ELanguage::English, { "Legend of Soleanna", "Clear all ACT missions with Rank S.", "Achieved Rank S on all ACT missions." } }, { ELanguage::Japanese, { "ソレアナの伝説", "全てのACTミッションをランクSでクリアせよ", "全てのACTミッションをランクSでクリアした" } }, { ELanguage::German, { "Legende von Soleanna", "Schließe alle AKT-Missionen mit einem S-Rang ab.", "Einen S-Rang bei allen AKT-Missionen erreicht." } }, { ELanguage::French, { "Légende de Soleanna", "Terminez toutes les missions ACTE avec un rang S.", "Obtenu un rang S dans toutes les missions ACTE." } }, { ELanguage::Spanish, { "Leyenda de Soleanna", "Supera todas las misiones ACTO con rango S.", "Has conseguido el rango S en todas las misiones ACTO." } }, { ELanguage::Italian, { "Leggenda di Soleanna", "Completa tutte le missioni ATTO in posizione S.", "Hai ottenuto la posizione S in tutte le missioni ATTO." } }, } }, // Silver Medalist { 13, { { ELanguage::English, { "Silver Medalist", "Collect all of the Silver Medals scattered around Soleanna.", "Collected all of the Silver Medals in Soleanna." } }, { ELanguage::Japanese, { "シルバーメダリスト", "ソレアナのシルバーメダルを全て取得せよ", "ソレアナのシルバーメダルを全て取得した" } }, { ELanguage::German, { "Silbermedaillengewinner", "Sammel alle Silbermedaillen die um Soleanna verstreut sind.", "Alle Silbermedaillen in Soleanna gesammelt." } }, { ELanguage::French, { "Médaillé d'argent", "Collectionnez toutes les médailles d'argent dispersées autour de Soleanna.", "Collectionné toutes les médailles d'argent à Soleanna." } }, { ELanguage::Spanish, { "Medallista de plata", "Reúne todas las medallas de plata repartidas por Soleanna.", "Has conseguido todas las medallas de plata de Soleanna." } }, { ELanguage::Italian, { "Medaglie d'argento", "Raccogli tutte le medaglie d'argento disseminate in giro per Soleanna.", "Hai raccolto tutte le medaglie d'argento di Soleanna." } }, } }, // Gold Medalist { 14, { { ELanguage::English, { "Gold Medalist", "Collect all of the legendary Gold Medals of Soleanna.", "Collected all of the Gold Medals of Soleanna." } }, { ELanguage::Japanese, { "ゴールドメダリスト", "ソレアナの伝説的なゴールドメダルを全て取得せよ", "ソレアナのゴールドメダルを全て取得した" } }, { ELanguage::German, { "Goldmedaillengewinner", "Sammel alle legendäre Goldmedaillen in Soleanna.", "Alle Goldmedaillen in Soleanna gesammelt." } }, { ELanguage::French, { "Médaillé d'or", "Collectionnez toutes les médailles d'or légendaires de Soleanna.", "Collectionné toutes les médailles d'or légendaires de Soleanna." } }, { ELanguage::Spanish, { "Medallista de oro", "Reúne todas las legendarias medallas de oro de Soleanna.", "Has conseguido todas las medallas de oro de Soleanna." } }, { ELanguage::Italian, { "Medaglie d'oro", "Raccogli tutte le medaglie d'oro di Soleanna.", "Hai raccolto tutte le medaglie d'oro di Soleanna." } }, } }, // Blue Phantom { 15, { { ELanguage::English, { "Blue Phantom", "Unlock all of Sonic's abilities.", "Learned all of Sonic's abilities." } }, { ELanguage::Japanese, { "蒼き幻影", "ソニックのアクションを全て習得せよ", "ソニックのアクションを全て習得した" } }, { ELanguage::German, { "Blaues Phantom", "Schalte alle von Sonic's Fähigkeiten frei.", "Alle von Sonic's Fähigkeiten gelernt." } }, { ELanguage::French, { "Fantôme bleu", "Débloquez toutes les capacités de Sonic.", "Appris toutes les capacités de Sonic." } }, { ELanguage::Spanish, { "Fantasma azul", "Desbloquea todas las habilidades de Sonic.", "Has aprendido todas las habilidades de Sonic." } }, { ELanguage::Italian, { "Fantasma Blu", "Sblocca tutte le abilità di Sonic.", "Hai imparato tutte le abilità di Sonic." } }, } }, // Ultimate Life Form { 16, { { ELanguage::English, { "Ultimate Life Form", "Unlock all of Shadow's abilities.", "Learned all of Shadow's abilities." } }, { ELanguage::Japanese, { "究極の生命体", "シャドウのアクションを全て習得せよ", "シャドウのアクションを全て習得した" } }, { ELanguage::German, { "Ultimative Lebensform", "Schalte alle von Shadow's Fähigkeiten frei.", "Alle von Shadow's Fähigkeiten gelernt." } }, { ELanguage::French, { "Forme de vie ultime", "Débloquez toutes les capacités de Shadow.", "Appris toutes les capacités de Shadow." } }, { ELanguage::Spanish, { "Forma de vida definitiva", "Desbloquea todas las habilidades de Shadow.", "Has aprendido todas las habilidades de Shadow." } }, { ELanguage::Italian, { "Essere Supremo", "Sblocca tutte le abilità di Shadow.", "Hai imparato tutte le abilità di Shadow." } }, } }, // Psychic Soldier { 17, { { ELanguage::English, { "Psychic Soldier", "Unlock all of Silver's abilities.", "Learned all of Silver's abilities." } }, { ELanguage::Japanese, { "サイキックソルジャー", "シルバーのアクションを全て習得せよ", "シルバーのアクションを全て習得した" } }, { ELanguage::German, { "Übernatürlicher Soldat", "Schalte alle von Silver's Fähigkeiten frei.", "Alle von Silver's Fähigkeiten gelernt." } }, { ELanguage::French, { "Soldat psychique", "Débloquez toutes les capacités de Silver.", "Appris toutes les capacités de Silver." } }, { ELanguage::Spanish, { "Soldado psíquico", "Desbloquea todas las habilidades de Silver.", "Has aprendido todas las habilidades de Silver." } }, { ELanguage::Italian, { "Soldato Psichico", "Sblocca tutte le abilità di Silver.", "Hai imparato tutte le abilità di Silver." } }, } }, // Soleanna's Hero { 18, { { ELanguage::English, { "Soleanna's Hero", "Clear all of Sonic's TOWN missions.", "Cleared all of Sonic's TOWN missions." } }, { ELanguage::Japanese, { "ソレアナの英雄", "ソニックのタウンミッションを全てクリアせよ", "ソニックのタウンミッションを全てクリアした" } }, { ELanguage::German, { "Soleanna's Held", "Schließe alle von Sonic's STADT-Missionen ab.", "Alle von Sonic's STADT-Missionen abgeschlossen." } }, { ELanguage::French, { "Héros de Soleanna", "Terminez toutes les missions VILLE de Sonic.", "Terminé toutes les missions VILLE de Sonic." } }, { ELanguage::Spanish, { "Héroe de Soleanna", "Supera todas las misiones CIUDAD de Sonic.", "Has superado todas las misiones CIUDAD de Sonic." } }, { ELanguage::Italian, { "Eroe di Soleanna", "Completa tutte le missioni CITTÀ di Sonic.", "Hai completato tutte le missioni CITTÀ di Sonic." } }, } }, // Elite Agent { 19, { { ELanguage::English, { "Elite Agent", "Clear all of Shadow's TOWN missions.", "Cleared all of Shadow's TOWN missions." } }, { ELanguage::Japanese, { "エリートエージェント", "シャドウのタウンミッションを全てクリアせよ", "シャドウのタウンミッションを全てクリアした" } }, { ELanguage::German, { "Elite-Agent", "Schließe alle von Shadow's STADT-Missionen ab.", "Alle von Shadow's STADT-Missionen abgeschlossen." } }, { ELanguage::French, { "Agent d'élite", "Terminez toutes les missions VILLE de Shadow.", "Terminé toutes les missions VILLE de Shadow." } }, { ELanguage::Spanish, { "Agente de élite", "Supera todas las misiones CIUDAD de Shadow.", "Has superado todas las misiones CIUDAD de Shadow." } }, { ELanguage::Italian, { "Agente Scelto", "Completa tutte le missioni CITTÀ di Shadow.", "Hai completato tutte le missioni CITTÀ di Shadow." } }, } }, // Silver The Liberator { 20, { { ELanguage::English, { "Silver the Liberator", "Clear all of Silver's TOWN missions.", "Cleared all of Silver's TOWN missions." } }, { ELanguage::Japanese, { "解放者シルバー", "シルバーのタウンミッションを全てクリアせよ", "シルバーのタウンミッションを全てクリアした" } }, { ELanguage::German, { "Silver der Befreier", "Schließe alle von Silver's STADT-Missionen ab.", "Alle von Silver's STADT-Missionen abgeschlossen." } }, { ELanguage::French, { "Silver le libérateur", "Terminez toutes les missions VILLE de Silver.", "Terminé toutes les missions VILLE de Silver." } }, { ELanguage::Spanish, { "Silver el Liberador", "Supera todas las misiones CIUDAD de Silver.", "Has superado todas las misiones CIUDAD de Silver." } }, { ELanguage::Italian, { "Silver il Liberatore", "Completa tutte le missioni CITTÀ di Silver.", "Hai completato tutte le missioni CITTÀ di Silver." } }, } }, // Soleanna's blue wind { 21, { { ELanguage::English, { "Soleanna's Blue Wind", "Clear all of Sonic's TOWN missions with Rank S.", "Achieved Rank S on all of Sonic's TOWN missions." } }, { ELanguage::Japanese, { "ソレアナの蒼き風", "ソニックのタウンミッションを全てランクSでクリアせよ", "ソニックのタウンミッションを全てランクSでクリアした" } }, { ELanguage::German, { "Soleanna's Blauer Wind", "Schließe alle von Sonic's STADT-Missionen mit einem S-Rang ab.", "Einen S-Rang in allen STADT-Missionen mit Sonic erreicht." } }, { ELanguage::French, { "Vent bleu de Soleanna", "Terminez toutes les missions VILLE de Sonic avec un rang S.", "Obtenu un rang S dans toutes les missions VILLE de Sonic." } }, { ELanguage::Spanish, { "El viento azul de Soleanna", "Supera todas las misiones CIUDAD de Sonic con rango S.", "Has conseguido rango S en todas las misiones CIUDAD de Sonic." } }, { ELanguage::Italian, { "Vento Blu di Soleanna", "Completa tutte le missioni CITTÀ di Sonic in posizione S.", "Hai ottenuto la posizione S in tutte le missioni CITTÀ di Sonic." } }, } }, // Dark Hero { 22, { { ELanguage::English, { "Dark Hero", "Clear all of Shadow's TOWN missions with Rank S.", "Achieved Rank S on all of Shadow's TOWN missions." } }, { ELanguage::Japanese, { "ダーク英雄", "シャドウのタウンミッションを全てランクSでクリアせよ", "シャドウのタウンミッションを全てランクSでクリアした" } }, { ELanguage::German, { "Dunkler Held", "Schließe alle von Shadow's STADT-Missionen mit einem S-Rang ab.", "Einen S-Rang in allen STADT-Missionen mit Shadow erreicht." } }, { ELanguage::French, { "Héros des ténèbres", "Terminez toutes les missions VILLE de Shadow avec un rang S.", "Obtenu un rang S dans toutes les missions VILLE de Shadow." } }, { ELanguage::Spanish, { "Héroe oscuro", "Supera todas las misiones CIUDAD de Shadow con rango S.", "Has conseguido rango S en todas las misiones CIUDAD de Shadow." } }, { ELanguage::Italian, { "Eroe Oscuro", "Completa tutte le missioni CITTÀ di Shadow in posizione S.", "Hai ottenuto la posizione S in tutte le missioni CITTÀ di Shadow." } }, } }, // Silver The Savior. { 23, { { ELanguage::English, { "Silver the Savior", "Clear all of Silver's TOWN missions with Rank S.", "Achieved Rank S on all of Silver's TOWN missions." } }, { ELanguage::Japanese, { "救世主シルバー", "シルバーのタウンミッションを全てランクSでクリアせよ", "シルバーのタウンミッションを全てランクSでクリアした" } }, { ELanguage::German, { "Silver der Retter", "Schließe alle von Silver's STADT-Missionen mit einem S-Rang ab.", "Einen S-Rang in allen STADT-Missionen mit Silver erreicht." } }, { ELanguage::French, { "Silver le sauveur", "Terminez toutes les missions VILLE de Silver avec un rang S.", "Obtenu un rang S dans toutes les missions VILLE de Silver." } }, { ELanguage::Spanish, { "Silver el Salvador", "Supera todas las misiones CIUDAD de Silver con rango S.", "Has conseguido rango S en todas las misiones CIUDAD de Silver." } }, { ELanguage::Italian, { "Silver il Salvatore", "Completa tutte le missioni CITTÀ di Silver in posizione S.", "Hai ottenuto la posizione S in tutte le missioni CITTÀ di Silver." } }, } } }; AchievementLocale& GetAchievementLocale(const int key) { auto localeFindResult = g_achLocale.find(key); if (localeFindResult != g_achLocale.end()) { auto languageFindResult = localeFindResult->second.find(Config::Language); if (languageFindResult == localeFindResult->second.end()) languageFindResult = localeFindResult->second.find(ELanguage::English); if (languageFindResult != localeFindResult->second.end()) return languageFindResult->second; } return g_achLocaleMissing; } ================================================ FILE: MarathonRecomp/locale/achievement_locale.h ================================================ #pragma once #include struct AchievementLocale { std::string Name{}; std::string LockedDesc{}; std::string UnlockedDesc{}; }; inline AchievementLocale g_achLocaleMissing = { g_localeMissing, g_localeMissing, g_localeMissing }; extern std::unordered_map> g_achLocale; AchievementLocale& GetAchievementLocale(const int key); ================================================ FILE: MarathonRecomp/locale/config_locale.cpp ================================================ #include /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! LOCALISATION NOTES !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Ensure brand names are always presented on the same line. Correct: This is a string that contains a brand name like Xbox 360, which is one of the two consoles to have a port of SONIC THE HEDGEHOG. Incorrect: This is a string that contains a brand name like Xbox 360, which is one of the two consoles to have a port of SONIC THE HEDGEHOG. - Ensure your locale is added in the correct order following the language enum. Correct: { { ELanguage::English, "Example" }, { ELanguage::Japanese, "Example" }, { ELanguage::German, "Example" }, { ELanguage::French, "Example" }, { ELanguage::Spanish, "Example" }, { ELanguage::Italian, "Example" } } Incorrect: { { ELanguage::English, "Example" }, { ELanguage::French, "Example" }, { ELanguage::Spanish, "Example" }, { ELanguage::German, "Example" }, { ELanguage::Italian, "Example" }, { ELanguage::Japanese, "Example" } } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ #define CONFIG_DEFINE_LOCALE(name) \ CONFIG_LOCALE g_##name##_locale = #define CONFIG_DEFINE_ENUM_LOCALE(type) \ CONFIG_ENUM_LOCALE(type) g_##type##_locale = CONFIG_DEFINE_LOCALE(Language) { { ELanguage::English, { "Language", "Change the language used for text." } }, { ELanguage::Japanese, { "言語", "ゲーム内の表示言語を変更できます" } }, { ELanguage::German, { "Sprache", "Ändere die Textsprache." } }, { ELanguage::French, { "Langue", "Modifier la langue utilisée pour le texte." } }, { ELanguage::Spanish, { "Idioma", "Cambia el idioma utilizado para los textos." } }, { ELanguage::Italian, { "Lingua", "Cambia la lingua utilizzata per il testo." } } }; // Notes: do not localise this. CONFIG_DEFINE_ENUM_LOCALE(ELanguage) { { ELanguage::English, { { ELanguage::English, { "English", "" } }, { ELanguage::Japanese, { "日本語", "" } }, { ELanguage::German, { "Deutsch", "" } }, { ELanguage::French, { "Français", "" } }, { ELanguage::Spanish, { "Español", "" } }, { ELanguage::Italian, { "Italiano", "" } } } } }; CONFIG_DEFINE_LOCALE(VoiceLanguage) { { ELanguage::English, { "Voice Language", "Change the language used for character voices." } }, { ELanguage::Japanese, { "音声言語", "ゲーム内の音声言語を変更できます" } }, { ELanguage::German, { "Stimmeinstellung", "Ändere die Sprache, die für Charakterstimmen benutzt wird." } }, { ELanguage::French, { "Langue de doublage", "Modifie la langue utilisée pour la voix des personnages." } }, { ELanguage::Spanish, { "Idioma de voz", "Cambia el idioma utilizado para las voces de los personajes." } }, { ELanguage::Italian, { "Lingua delle voci", "Modifica la lingua utilizzata per le voci dei personaggi." } } }; CONFIG_DEFINE_ENUM_LOCALE(EVoiceLanguage) { { ELanguage::English, { { EVoiceLanguage::English, { "English", "" } }, { EVoiceLanguage::Japanese, { "Japanese", "" } } } }, { ELanguage::Japanese, { { EVoiceLanguage::English, { "英語", "" } }, { EVoiceLanguage::Japanese, { "日本語", "" } } } }, { ELanguage::German, { { EVoiceLanguage::English, { "Englisch", "" } }, { EVoiceLanguage::Japanese, { "Japanisch", "" } } } }, { ELanguage::French, { { EVoiceLanguage::English, { "Anglais", "" } }, { EVoiceLanguage::Japanese, { "Japonais", "" } } } }, { ELanguage::Spanish, { { EVoiceLanguage::English, { "Inglés", "" } }, { EVoiceLanguage::Japanese, { "Japonés", "" } } } }, { ELanguage::Italian, { { EVoiceLanguage::English, { "Inglese", "" } }, { EVoiceLanguage::Japanese, { "Giapponese", "" } } } }, }; CONFIG_DEFINE_LOCALE(Subtitles) { { ELanguage::English, { "Subtitles", "Show subtitles during dialogue." } }, { ELanguage::Japanese, { "字幕", "字幕の表示を選択できます" } }, { ELanguage::German, { "Untertitel", "Zeige Untertitel bei Dialogen." } }, { ELanguage::French, { "Sous-titres", "Affiche les sous-titres pendant les dialogues." } }, { ELanguage::Spanish, { "Subtítulos", "Muestra subtítulos durante los diálogos." } }, { ELanguage::Italian, { "Sottotitoli", "Mostra i sottotitoli durante i dialoghi." } } }; CONFIG_DEFINE_LOCALE(Hints) { { ELanguage::English, { "Hints", "Show hints during gameplay." } }, { ELanguage::Japanese, { "ヒントリング", "ゲーム内にヒントリングを表示するか選択できます" } }, { ELanguage::German, { "Hinweise", "Zeige Hinweise während des Spiels." } }, { ELanguage::French, { "Indices", "Affiche les indices pendant le jeu." } }, { ELanguage::Spanish, { "Pistas", "Muestra pistas durante el juego." } }, { ELanguage::Italian, { "Indizi", "Mostra degli indizzi durante il gioco." } } }; CONFIG_DEFINE_LOCALE(ControlTutorial) { { ELanguage::English, { "Control Tutorial", "Show controller hints during gameplay." } }, { ELanguage::Japanese, { "アクションナビ", "ゲーム内にアクションナビを表示するか選択できます" } }, { ELanguage::German, { "Steuerungsanleitung", "Zeige Steuerungshinweise während des Spiels." } }, { ELanguage::French, { "Indication des commandes", "Affiche les indications des commandes pendant le jeu." } }, { ELanguage::Spanish, { "Tutorial de controles", "Muestra pistas de controles durante el juego." } }, { ELanguage::Italian, { "Tutorial dei comandi", "Mostra i tutorial dei comandi durante il gioco." } } }; CONFIG_DEFINE_LOCALE(Autosave) { { ELanguage::English, { "Autosave", "Save the game automatically at manual save points." } }, { ELanguage::Japanese, { "オートセーブ", "手動セーブポイントでゲームを自動的にセーブします" } }, { ELanguage::German, { "Automatisches Speichern", "Speichert das Spiel automatisch an manuellen Speicherpunkten." } }, { ELanguage::French, { "Sauvegarde Auto", "Sauvegarder automatiquement la partie lorsque le jeu propose une sauvegarde." } }, { ELanguage::Spanish, { "Autoguardado", "Guarda el juego automáticamente en los puntos de guardado manuales." } }, { ELanguage::Italian, { "Salvataggio automatico", "Salva automaticamente il gioco nei punti di salvataggio manuali." } } }; CONFIG_DEFINE_LOCALE(AchievementNotifications) { { ELanguage::English, { "Achievement Notifications", "Show notifications for unlocking achievements. Achievements will still be rewarded with notifications disabled." } }, { ELanguage::Japanese, { "実績通知", "実績通知の有無を選択できます オフにしても実績は付与されます" } }, { ELanguage::German, { "Erfolgsbenachrichtigungen", "Zeige Benachrichtigungen für das Freischalten von Erfolgen. Erfolge werden weiterhin freigeschaltet, auch wenn die Benachrichtigungen ausgeschaltet sind." } }, { ELanguage::French, { "Notification des succès", "Affiche les notifications pour le déverrouillage des succès. Les succès seront toujours obtenus même si les notifications sont désactivées." } }, { ELanguage::Spanish, { "Notificaciones de logros", "Muestra notificaciones al desbloquear logros. Los logros se seguirán obteniendo aunque las notificaciones estén desactivadas." } }, { ELanguage::Italian, { "Notifiche obiettivi", "Mostra delle notifiche quando sblocchi degli obiettivi. Gli obiettivi verranno comunque assegnati anche con le notifiche disattivate." } } }; CONFIG_DEFINE_LOCALE(HorizontalCamera) { { ELanguage::English, { "Horizontal Camera", "Change how the camera moves left and right." } }, { ELanguage::Japanese, { "カメラの左右", "カメラ左右の動く方向を選択できます" } }, { ELanguage::German, { "Horizontale Kamera", "Ändere wie sich die Kamera nach links und rechts bewegt." } }, { ELanguage::French, { "Caméra horizontale", "Modifie la rotation horizontale de la caméra." } }, { ELanguage::Spanish, { "Cámara horizontal", "Cambia cómo se mueve la camara hacia la izquierda y la derecha." } }, { ELanguage::Italian, { "Telecamera orizzontale", "Modifica come la telecamera si muove da sinistra a destra." } } }; CONFIG_DEFINE_LOCALE(VerticalCamera) { { ELanguage::English, { "Vertical Camera", "Change how the camera moves up and down." } }, { ELanguage::Japanese, { "カメラの上下", "カメラ上下の動く方向を選択できます" } }, { ELanguage::German, { "Vertikale Kamera", "Ändere wie sich die Kamera nach oben und unten bewegt." } }, { ELanguage::French, { "Caméra verticale", "Modifie la rotation verticale de la caméra." } }, { ELanguage::Spanish, { "Cámara vertical", "Cambia cómo se mueve la camara hacia arriba y abajo." } }, { ELanguage::Italian, { "Telecamera verticale", "Modifica come la telecamera si muove su e giù." } } }; CONFIG_DEFINE_ENUM_LOCALE(ECameraRotationMode) { { ELanguage::English, { { ECameraRotationMode::Normal, { "Normal", "" } }, { ECameraRotationMode::Reverse, { "Reverse", "" } } } }, { ELanguage::Japanese, { { ECameraRotationMode::Normal, { "ノーマル", "" } }, { ECameraRotationMode::Reverse, { "リバース", "" } } } }, { ELanguage::German, { { ECameraRotationMode::Normal, { "Normal", "" } }, { ECameraRotationMode::Reverse, { "Invertiert", "" } } } }, { ELanguage::French, { { ECameraRotationMode::Normal, { "Normale", "" } }, { ECameraRotationMode::Reverse, { "Inversée", "" } } } }, { ELanguage::Spanish, { { ECameraRotationMode::Normal, { "Normal", "" } }, { ECameraRotationMode::Reverse, { "Invertido", "" } } } }, { ELanguage::Italian, { { ECameraRotationMode::Normal, { "Normale", "" } }, { ECameraRotationMode::Reverse, { "Invertita", "" } } } } }; CONFIG_DEFINE_LOCALE(AllowBackgroundInput) { { ELanguage::English, { "Allow Background Input", "Allow controller input whilst the game window is unfocused." } }, { ELanguage::Japanese, { "バックグラウンド入力", "フォーカスされていないゲームに入力できるか選択できます" } }, { ELanguage::German, { "Erlaube Hintergrundeingaben", "Erlaube Eingaben deines Controllers auch wenn das Spielfenster nicht fokussiert ist." } }, { ELanguage::French, { "Manette en arrière plan", "Permet d'utiliser la manette dans le jeu lorsque qu'il n'est pas au premier plan." } }, { ELanguage::Spanish, { "Control en segundo plano", "Permite controlar el juego con un mando mientras la ventana esté en segundo plano." } }, { ELanguage::Italian, { "Input con la finestra inattiva", "Attiva/disattiva i tasti del controller mentre la finestra è inattiva." } } }; CONFIG_DEFINE_LOCALE(ControllerIcons) { { ELanguage::English, { "Controller Icons", "Change the icons to match your controller." } }, { ELanguage::Japanese, { "コントローラーアイコン", "ゲーム内のコントローラーアイコンを変更できます" } }, { ELanguage::German, { "Controllersymbole", "Ändere die Controllersymbole, um sie auf dein Modell anzupassen." } }, { ELanguage::French, { "Icône des boutons", "Modifie les icônes pour les faire correspondre à votre manette." } }, { ELanguage::Spanish, { "Iconos del mando", "Cambia los iconos para que coincidan con tu mando." } }, { ELanguage::Italian, { "Icone dei tasti", "Modifica le icone per farle corrispondere con il tuo controller." } } }; CONFIG_DEFINE_ENUM_LOCALE(EControllerIcons) { { ELanguage::English, { { EControllerIcons::Auto, { "Auto", "Auto: the game will determine which icons to use based on the current input device." } }, { EControllerIcons::Xbox, { "Xbox", "" } }, { EControllerIcons::PlayStation, { "PlayStation", "" } } } }, { ELanguage::Japanese, { { EControllerIcons::Auto, { "自動検出", "自動検出: コントローラーアイコンを使用している入力デバイスに応じて自動的に決定します" } }, { EControllerIcons::Xbox, { "Xbox", "" } }, { EControllerIcons::PlayStation, { "PlayStation", "" } } } }, { ELanguage::German, { { EControllerIcons::Auto, { "Auto", "Auto: Das Spiel erkennt automatisch deinen Controller um die Symbole dementsprechend anzupassen." } }, { EControllerIcons::Xbox, { "Xbox", "" } }, { EControllerIcons::PlayStation, { "PlayStation", "" } } } }, { ELanguage::French, { { EControllerIcons::Auto, { "Auto", "Auto : le jeu déterminera automatiquement quelles icônes utiliser en fonction du périphérique d'entrée." } }, { EControllerIcons::Xbox, { "Xbox", "" } }, { EControllerIcons::PlayStation, { "PlayStation", "" } } } }, { ELanguage::Spanish, { { EControllerIcons::Auto, { "Auto", "Auto: el juego determinará de forma automática qué iconos utilizar dependiendo del dispositivo de entrada actual." } }, { EControllerIcons::Xbox, { "Xbox", "" } }, { EControllerIcons::PlayStation, { "PlayStation", "" } } } }, { ELanguage::Italian, { { EControllerIcons::Auto, { "Auto", "Auto: il gioco determinerà quali icone da utilizzare in base al dispositivo di input attuale." } }, { EControllerIcons::Xbox, { "Xbox", "" } }, { EControllerIcons::PlayStation, { "PlayStation", "" } } } } }; CONFIG_DEFINE_LOCALE(LightDash) { { ELanguage::English, { "Light Dash", "Change how Light Dash is activated for Sonic and Shadow." } }, { ELanguage::Japanese, { "ライトダッシュ", "ソニックとシャドウのライトダッシュの発動方法を変更します" } }, { ELanguage::German, { "Lichtsprint", "Ändere wie der Lichtsprint von Sonic und Shadow aktiviert wird." } }, { ELanguage::French, { "Course éclair", "Modifier la façon dont Course éclair est activée pour Sonic et Shadow." } }, { ELanguage::Spanish, { "Acelerón ligero", "Cambia cómo se activa el acelerón ligero para Sonic y Shadow." } }, { ELanguage::Italian, { "Super spinta", "Cambia il modo in cui viene attivata la Super spinta per Sonic e Shadow." } } }; CONFIG_DEFINE_ENUM_LOCALE(ELightDash) { { ELanguage::English, { { ELightDash::X, { "Press ${picture(button_x)}", "" } }, { ELightDash::Y, { "Press ${picture(button_y)}", "" } } } }, { ELanguage::Japanese, { { ELightDash::X, { "${picture(button_x)}を押す", "" } }, { ELightDash::Y, { "${picture(button_y)}を押す", "" } } } }, { ELanguage::German, { { ELightDash::X, { "Drücke ${picture(button_x)}", "" } }, { ELightDash::Y, { "Drücke ${picture(button_y)}", "" } } } }, { ELanguage::French, { { ELightDash::X, { "Appuyer sur ${picture(button_x)}", "" } }, { ELightDash::Y, { "Appuyer sur ${picture(button_y)}", "" } } } }, { ELanguage::Spanish, { { ELightDash::X, { "Pulsar ${picture(button_x)}", "" } }, { ELightDash::Y, { "Pulsar ${picture(button_y)}", "" } } } }, { ELanguage::Italian, { { ELightDash::X, { "Premi ${picture(button_x)}", "" } }, { ELightDash::Y, { "Premi ${picture(button_y)}", "" } } } } }; CONFIG_DEFINE_LOCALE(SlidingAttack) { { ELanguage::English, { "Sliding Attack", "Change how the Sliding Attack is activated for Sonic." } }, { ELanguage::Japanese, { "スライディングアタック", "ソニックのスライディングアタックの発動方法を変更します" } }, { ELanguage::German, { "Schlitterangriff", "Ändere wie der Schlitterangriff von Sonic aktiviert wird." } }, { ELanguage::French, { "Attaque-dérapage", "Modifier la façon dont l'Attaque-dérapage est activée pour Sonic." } }, { ELanguage::Spanish, { "Ataque derrape", "Cambia cómo se activa el ataque derrape para Sonic." } }, { ELanguage::Italian, { "Scivolata", "Cambia il modo in cui viene attivata la Scivolata per Sonic." } } }; CONFIG_DEFINE_ENUM_LOCALE(ESlidingAttack) { { ELanguage::English, { { ESlidingAttack::B, { "Hold ${picture(button_b)}", "" } }, { ESlidingAttack::X, { "Release ${picture(button_x)}", "" } } } }, { ELanguage::Japanese, { { ESlidingAttack::B, { "${picture(button_b)}を押し続ける", "" } }, { ESlidingAttack::X, { "${picture(button_x)}を離す", "" } } } }, { ELanguage::German, { { ESlidingAttack::B, { "Halte ${picture(button_b)} gedrückt", "" } }, { ESlidingAttack::X, { "Lasse ${picture(button_x)} los", "" } } } }, { ELanguage::French, { { ESlidingAttack::B, { "Maintenir ${picture(button_b)}", "" } }, { ESlidingAttack::X, { "Relâcher ${picture(button_x)}", "" } } } }, { ELanguage::Spanish, { { ESlidingAttack::B, { "Mantener ${picture(button_b)}", "" } }, { ESlidingAttack::X, { "Soltar ${picture(button_x)}", "" } } } }, { ELanguage::Italian, { { ESlidingAttack::B, { "Tieni premuto ${picture(button_b)}", "" } }, { ESlidingAttack::X, { "Rilascia ${picture(button_x)}", "" } } } } }; CONFIG_DEFINE_LOCALE(MasterVolume) { { ELanguage::English, { "Master Volume", "Adjust the overall volume." } }, { ELanguage::Japanese, { "マスターボリューム", "全体ボリュームの大きさを調整できます" } }, { ELanguage::German, { "Gesamtlautstärke", "Passe die Gesamtlautstärke an." } }, { ELanguage::French, { "Volume général", "Réglage du volume général." } }, { ELanguage::Spanish, { "Volumen maestro", "Ajusta el volumen general." } }, { ELanguage::Italian, { "Volume principale", "Regola il volume principale" } } }; CONFIG_DEFINE_LOCALE(MusicVolume) { { ELanguage::English, { "Music Volume", "Adjust the volume for the music." } }, { ELanguage::Japanese, { "BGMボリューム", "BGMボリュームの大きさを調整できます" } }, { ELanguage::German, { "Musiklautstärke", "Passe die Lautstärke der Musik an." } }, { ELanguage::French, { "Volume de la musique", "Réglage du volume de la musique." } }, { ELanguage::Spanish, { "Volumen de la música", "Ajusta el volumen de la música." } }, { ELanguage::Italian, { "Volume musica di sottofondo", "Regola il volume della musica di sottofondo." } } }; CONFIG_DEFINE_LOCALE(EffectsVolume) { { ELanguage::English, { "Effects Volume", "Adjust the volume for sound effects." } }, { ELanguage::Japanese, { "SEボリューム", "SEボリュームの大きさを調整できます" } }, { ELanguage::German, { "Soundeffektlautstärke", "Passe die Lautstärke der Soundeffekte an." } }, { ELanguage::French, { "Volume des effets sonores", "Réglage du volume des effets sonores." } }, { ELanguage::Spanish, { "Volumen de efectos", "Ajusta el volumen de los efectos de sonido." } }, { ELanguage::Italian, { "Volume effetti sonori", "Regola il volume degli effetti sonori." } } }; CONFIG_DEFINE_LOCALE(ChannelConfiguration) { { ELanguage::English, { "Channel Configuration", "Change the output mode for your audio device." } }, { ELanguage::Japanese, { "チャンネル設定", "オーディオデバイスの出力モードを変更できます" } }, { ELanguage::German, { "Kanalkonfiguration", "Ändere den Ausgabemodus für dein Audioausgabegerät." } }, { ELanguage::French, { "Configuration sortie audio", "Modifie le mode de sortie pour votre périphérique audio." } }, { ELanguage::Spanish, { "Configuración de canales", "Cambia el modo de salida para tu dispositivo de audio." } }, { ELanguage::Italian, { "Configurazione canali audio", "Modifica la modalità di output per il tuo dispositivo audio." } } }; CONFIG_DEFINE_ENUM_LOCALE(EChannelConfiguration) { { ELanguage::English, { { EChannelConfiguration::Stereo, { "Stereo", "" } }, { EChannelConfiguration::Surround, { "Surround", "" } } } }, { ELanguage::Japanese, { { EChannelConfiguration::Stereo, { "ステレオ", "" } }, { EChannelConfiguration::Surround, { "サラウンド", "" } } } }, { ELanguage::German, { { EChannelConfiguration::Stereo, { "Stereo", "" } }, { EChannelConfiguration::Surround, { "Surround", "" } } } }, { ELanguage::French, { { EChannelConfiguration::Stereo, { "Stéréo", "" } }, { EChannelConfiguration::Surround, { "Surround", "" } } } }, { ELanguage::Spanish, { { EChannelConfiguration::Stereo, { "Estéreo", "" } }, { EChannelConfiguration::Surround, { "Envolvente", "" } } } }, { ELanguage::Italian, { { EChannelConfiguration::Stereo, { "Stereo", "" } }, { EChannelConfiguration::Surround, { "Surround", "" } } } } }; CONFIG_DEFINE_LOCALE(MuteOnFocusLost) { { ELanguage::English, { "Mute on Focus Lost", "Mute the game's audio when the window is not in focus." } }, { ELanguage::Japanese, { "フォーカスロストミュート", "ウィンドウがフォーカスされていないときにゲームのオーディオをミュートします" } }, { ELanguage::German, { "Stummstellen wenn nicht fokussiert", "Stellt das Audio des Spiels stumm solange das Fenster nicht im Fokus ist." } }, { ELanguage::French, { "Muet si fenêtre inactive", "Coupe le son du jeu lorsque la fenêtre n'est pas active." } }, { ELanguage::Spanish, { "Silenciar al perder el foco", "Silencia el audio del juego cuando la ventana no esté en primer plano." } }, { ELanguage::Italian, { "Audio muto con finestra inattiva", "Disattiva l'audio del gioco quando la finestra non è attiva." } } }; CONFIG_DEFINE_LOCALE(MusicAttenuation) { { ELanguage::English, { "Music Attenuation", "Fade out the game's music when external media is playing." } }, { ELanguage::Japanese, { "BGM減衰", "外部メディアを再生するとゲームの音楽をフェードアウトします" } }, { ELanguage::German, { "Musikdämpfung", "Stelle die Musik des Spiels stumm während externe Medien abgespielt werden." } }, { ELanguage::French, { "Atténuation audio", "Abaisse le volume des musiques du jeu lorsqu'un média externe est en cours de lecture." } }, { ELanguage::Spanish, { "Atenuación de música", "Atenúa la música del juego cuando un reproductor multimedia se encuentra activo." } }, { ELanguage::Italian, { "Attenuazione musica", "Abbassa il volume della musica di sottofondo quando un'altra applicazione riproduce dei suoni." } } }; CONFIG_DEFINE_LOCALE(WindowSize) { { ELanguage::English, { "Window Size", "Adjust the size of the game window in windowed mode." } }, { ELanguage::Japanese, { "ウィンドウサイズ", "ウィンドウモードでのゲームのウィンドウサイズを調整できます" } }, { ELanguage::German, { "Fenstergröße", "Ändere die Größe des Spielfensters im Fenstermodus." } }, { ELanguage::French, { "Taille de la fenêtre", "Modifie la taille de la fenêtre de jeu en mode fenêtré." } }, { ELanguage::Spanish, { "Tamaño de ventana", "Ajusta el tamaño de la ventana de juego." } }, { ELanguage::Italian, { "Dimensioni finestra", "Regola le dimensioni della finestra del gioco in modalità finestra." } } }; CONFIG_DEFINE_LOCALE(Monitor) { { ELanguage::English, { "Monitor", "Change which monitor to display the game on." } }, { ELanguage::Japanese, { "モニター選択", "ゲームを表示するモニターを変更できます" } }, { ELanguage::German, { "Monitor", "Ändere auf welchem Monitor das Spiel angezeigt wird." } }, { ELanguage::French, { "Moniteur", "Change le moniteur sur lequel le jeu sera affiché." } }, { ELanguage::Spanish, { "Pantalla", "Cambia la pantalla en la cuál se muestra el juego." } }, { ELanguage::Italian, { "Schermo", "Cambia lo schermo su cui visualizzare il gioco." } } }; CONFIG_DEFINE_LOCALE(AspectRatio) { { ELanguage::English, { "Aspect Ratio", "Change the aspect ratio." } }, { ELanguage::Japanese, { "アスペクト比", "アスペクト比を変更できます" } }, { ELanguage::German, { "Seitenverhältnis", "Verändere das Seitenverhältnis." } }, { ELanguage::French, { "Format d'image", "Modifie le format d'image." } }, { ELanguage::Spanish, { "Relación de aspecto", "Cambia la relación de aspecto." } }, { ELanguage::Italian, { "Rapporto d'aspetto", "Modifica il rapporto d'aspetto." } } }; CONFIG_DEFINE_ENUM_LOCALE(EAspectRatio) { { ELanguage::English, { { EAspectRatio::Auto, { "Auto", "Auto: the aspect ratio will dynamically adjust to the window size." } }, { EAspectRatio::Original, { "Original", "Original: locks the game to a widescreen aspect ratio." } } } }, { ELanguage::Japanese, { { EAspectRatio::Auto, { "自動", "自動: アスペクト比はウィンドウサイズに合わせて調整されます" } }, { EAspectRatio::Original, { "オリジナル", "オリジナル: ワイドスクリーンのアスペクト比に固定されます" } } } }, { ELanguage::German, { { EAspectRatio::Auto, { "Auto", "Auto: Das Seitenverhältnis passt sich automatisch der Fenstergröße an." } }, { EAspectRatio::Original, { "Original", "Original: Stellt das Spiel in einem Breitbildschirm-Format dar." } } } }, { ELanguage::French, { { EAspectRatio::Auto, { "Auto", "Auto : le format d'image s'adapte automatiquement à la taille de la fenêtre." } }, { EAspectRatio::Original, { "Original", "Original : force le jeu sur un format d'image large." } } } }, { ELanguage::Spanish, { { EAspectRatio::Auto, { "Auto", "Auto: la relación de aspecto se ajusta de forma dinámica al tamaño de la ventana." } }, { EAspectRatio::Original, { "Original", "Original: muestra el juego en relación de aspecto de pantalla ancha." } } } }, { ELanguage::Italian, { { EAspectRatio::Auto, { "Auto", "Auto: il rapporto d'aspetto verra cambiato automaticamente in base alle dimensioni della finestra." } }, { EAspectRatio::Original, { "Originale", "Originale: blocca il gioco a un rapporto d'aspetto widescreen." } } } } }; CONFIG_DEFINE_LOCALE(ResolutionScale) { { ELanguage::English, { "Resolution Scale", "Adjust the internal resolution of the game." } }, { ELanguage::Japanese, { "解像度スケール", "ゲームの内部解像度を調整できます" } }, { ELanguage::German, { "Rendering-Auflösung", "Passe die Auflösung der internen Darstellung an." } }, { ELanguage::French, { "Échelle de rendu", "Modifie la résolution interne du jeu." } }, { ELanguage::Spanish, { "Escala de resolución", "Ajusta la resolución interna del juego." } }, { ELanguage::Italian, { "Scala risoluzione", "Regola la risoluzione interna del gioco." } } }; CONFIG_DEFINE_LOCALE(Fullscreen) { { ELanguage::English, { "Fullscreen", "Toggle between borderless fullscreen or windowed mode." } }, { ELanguage::Japanese, { "フルスクリーン", "ボーダーレスフルスクリーンかウィンドウモードを選択できます" } }, { ELanguage::German, { "Vollbild", "Wechsle zwischen dem randlosen Vollbildmodus und dem Fenstermodus." } }, { ELanguage::French, { "Plein écran", "Alterne entre le mode plein écran sans bordures et le mode fenêtré." } }, { ELanguage::Spanish, { "Pantalla completa", "Cambia entre modo de pantalla completa o ventana." } }, { ELanguage::Italian, { "Schermo pieno", "Attiva/disattiva tra modalità finestra senza cornice e modalità finestra." } } }; CONFIG_DEFINE_LOCALE(VSync) { { ELanguage::English, { "V-Sync", "Synchronize the game to the refresh rate of the display to prevent screen tearing." } }, { ELanguage::Japanese, { "垂直同期", "垂直同期の設定を変更できます" } }, { ELanguage::German, { "V-Sync", "Synchronisiere das Spiel mit der Bildwiederholrate deines Bildschirms um Bildverzerrungen zu vermeiden." } }, { ELanguage::French, { "V-Sync", "Synchronise le jeu avec la fréquence de rafraîchissement de l'écran pour éviter le screen tearing." } }, { ELanguage::Spanish, { "V-Sync", "Sincroniza el juego a la tasa de refresco de la pantalla para prevenir el rasgado de la imagen." } }, { ELanguage::Italian, { "V-Sync", "Sincronizza il gioco con la frequenza d'aggiornamento del display per evitare lo screen tearing." } } }; CONFIG_DEFINE_LOCALE(FPS) { { ELanguage::English, { "FPS", "Set the max frame rate the game can run at. WARNING: this may introduce glitches at frame rates other than 60 FPS." } }, { ELanguage::Japanese, { "フレームレート上限", "ゲームが実行できる最大フレームレートを設定します 警告:60FPS以外のフレームレートでは不具合が発生する可能性があります" } }, { ELanguage::German, { "FPS", "Bestimmt die maximale Bildwiederholrate. WARNUNG: es können Fehler bei einer Bildwiederholrate über 60 FPS auftreten." } }, { ELanguage::French, { "IPS", "Limiter le nombre d'images par seconde. ATTENTION : Cela peut provoquer des bugs à des fréquences autres que 60 IPS." } }, { ELanguage::Spanish, { "FPS", "Establece la tasa máxima de fotogramas a la que puede ejecutarse el juego. ADVERTENCIA: esto puede provocar fallos en velocidades distintas a 60 FPS." } }, { ELanguage::Italian, { "FPS", "Imposta il framerate massimo del gioco. ATTENZIONE: questa opzione può causare problemi con dei framerate rate superiori a 60 FPS." } } }; CONFIG_DEFINE_LOCALE(Brightness) { { ELanguage::English, { "Brightness", "Adjust the brightness level." } }, { ELanguage::Japanese, { "明るさの設定", "明るさレベルを調整します" } }, { ELanguage::German, { "Helligkeit", "Stelle die Helligkeit ein." } }, { ELanguage::French, { "Luminosité", "Modifier le niveau de luminosité." } }, { ELanguage::Spanish, { "Brillo", "Ajusta el nivel de brillo." } }, { ELanguage::Italian, { "Luminosità", "Regola il livello di luminosità." } } }; CONFIG_DEFINE_LOCALE(AntiAliasing) { { ELanguage::English, { "Anti-Aliasing", "Adjust the amount of smoothing applied to jagged edges." } }, { ELanguage::Japanese, { "アンチエイリアス", "アンチエイリアスの種類を選択できます" } }, { ELanguage::German, { "Kantenglättung", "Passe die Menge an Kantenglättung an." } }, { ELanguage::French, { "Anticrénelage", "Ajuste le niveau d'anticrénelage appliqué aux bords des objets." } }, { ELanguage::Spanish, { "Anti-Aliasing", "Ajusta el nivel de suavizado aplicado a los dientes de sierra." } }, { ELanguage::Italian, { "Anti-Aliasing", "Regola la quantità di smussamento applicata ai bordi." } } }; CONFIG_DEFINE_ENUM_LOCALE(EAntiAliasing) { { ELanguage::English, { { EAntiAliasing::Off, { "Off", "" } } } }, { ELanguage::Japanese, { { EAntiAliasing::Off, { "オフ", "" } }, } }, { ELanguage::German, { { EAntiAliasing::Off, { "Aus", "" } } } }, { ELanguage::French, { { EAntiAliasing::Off, { "Non", "" } } } }, { ELanguage::Spanish, { { EAntiAliasing::Off, { "No", "" } }, } }, { ELanguage::Italian, { { EAntiAliasing::Off, { "No", "" } } } } }; CONFIG_DEFINE_LOCALE(TransparencyAntiAliasing) { { ELanguage::English, { "Transparency Anti-Aliasing", "Apply anti-aliasing to alpha transparent textures." } }, { ELanguage::Japanese, { "透明度のアンチエイリアス", "透過テクスチャにアンチエイリアスを適用します" } }, { ELanguage::German, { "Transparenz-Kantenglättung", "Wende Kantenglättung auf Alpha-Transparenz-Texturen an." } }, { ELanguage::French, { "Anticrénelage de transparence", "Applique l'anticrénelage sur les textures transparentes." } }, { ELanguage::Spanish, { "Anti-Aliasing de transparencias", "Aplica antialiasing a las texturas transparentes." } }, { ELanguage::Italian, { "Anti-Aliasing su texture trasparenti", "Applica l'anti-aliasing alle texture trasparenti." } } }; CONFIG_DEFINE_LOCALE(ShadowResolution) { { ELanguage::English, { "Shadow Resolution", "Set the resolution of real-time shadows." } }, { ELanguage::Japanese, { "影の解像度", "影の解像度を設定できます" } }, { ELanguage::German, { "Schattenauflösung", "Stelle die Auflösung der Echtzeit-Schatten ein." } }, { ELanguage::French, { "Résolution des ombres", "Définit la résolution des ombres en temps réel." } }, { ELanguage::Spanish, { "Resolución de sombras", "Establece la resolución de las sombras en tiempo real." } }, { ELanguage::Italian, { "Risoluzione ombre", "Imposta la risoluzioni delle ombre in tempo reale." } } }; CONFIG_DEFINE_ENUM_LOCALE(EShadowResolution) {}; CONFIG_DEFINE_LOCALE(ReflectionResolution) { { ELanguage::English, { "Reflection Resolution", "Set the resolution of real-time reflections." } }, { ELanguage::Japanese, { "反射解像度", "リアルタイム反射の解像度を設定します" } }, { ELanguage::German, { "Reflektionsauflösung", "Bestimmt die Auflösung der Echtzeitreflektionen." } }, { ELanguage::French, { "Résolution des reflets", "Définir la résolution des reflets en temps réel." } }, { ELanguage::Spanish, { "Resolución de reflejos", "Establece la resolución de los reflejos en tiempo real." } }, { ELanguage::Italian, { "Risoluzione riflessi", "Imposta la risoluzione dei riflessi in tempo reale." } } }; CONFIG_DEFINE_ENUM_LOCALE(EReflectionResolution) { { ELanguage::English, { { EReflectionResolution::Eighth, { "12.5%", "" } }, { EReflectionResolution::Quarter, { "25%", "" } }, { EReflectionResolution::Half, { "50%", "" } }, { EReflectionResolution::Full, { "100%", "" } } } } }; CONFIG_DEFINE_LOCALE(RadialBlur) { { ELanguage::English, { "Radial Blur", "Change the quality of the radial blur." } }, { ELanguage::Japanese, { "放射状ぼかし", "放射状ぼかしの品質を変更します" } }, { ELanguage::German, { "Radiale Unschärfe", "Verändere die Qualität der radialen Unschärfe." } }, { ELanguage::French, { "Flou Radial", "Modifier la qualité du flou radial." } }, { ELanguage::Spanish, { "Desenfoque radial", "Cambia la calidad del desenfoque radial." } }, { ELanguage::Italian, { "Sfocatura radiale", "Modifica la qualità della sfocatura radiale." } } }; CONFIG_DEFINE_ENUM_LOCALE(ERadialBlur) { { ELanguage::English, { { ERadialBlur::Off, { "Off", "" } }, { ERadialBlur::Original, { "Original", "" } }, { ERadialBlur::Enhanced, { "Enhanced", "Enhanced: uses more samples for smoother radial blur." } } } }, { ELanguage::Japanese, { { ERadialBlur::Off, { "オフ", "" } }, { ERadialBlur::Original, { "オリジナル", "" } }, { ERadialBlur::Enhanced, { "エンハンスド", "エンハンスド:より多くのサンプルを使用してよりスムーズな放射状ぼかしを実現します" } } } }, { ELanguage::German, { { ERadialBlur::Off, { "Aus", "" } }, { ERadialBlur::Original, { "Original", "" } }, { ERadialBlur::Enhanced, { "Verbessert", "Verbessert: Benutzt mehr Samples um eine weichere radiale Unschärfe zu erzeugen." } } } }, { ELanguage::French, { { ERadialBlur::Off, { "Non", "" } }, { ERadialBlur::Original, { "Original", "" } }, { ERadialBlur::Enhanced, { "Amélioré", "Amélioré : utilise plus d'échantillons pour un flou radial plus lisse." } } } }, { ELanguage::Spanish, { { ERadialBlur::Off, { "No", "" } }, { ERadialBlur::Original, { "Original", "" } }, { ERadialBlur::Enhanced, { "Mejorado", "Mejorado: utiliza más muestras para un desenfoque radial más suave." } } } }, { ELanguage::Italian, { { ERadialBlur::Off, { "No", "" } }, { ERadialBlur::Original, { "Originale", "" } }, { ERadialBlur::Enhanced, { "Migliorato", "Migliorato: utilizza più campioni per una sfocatura radiale più uniforme." } } } } }; CONFIG_DEFINE_LOCALE(CutsceneAspectRatio) { { ELanguage::English, { "Cutscene Aspect Ratio", "Change the aspect ratio of the real-time cutscenes." } }, { ELanguage::Japanese, { "アスペクト比のカットシーン", "リアルタイムカットシーンのアスペクト比を変更できます" } }, { ELanguage::German, { "Zwischensequenz-Seitenverhältnis", "Verändere das Seitenverhältnis der Echtzeit-Zwischensequenzen." } }, { ELanguage::French, { "Format des cinématiques", "Modifie le format d'image des cinématiques en temps réel." } }, { ELanguage::Spanish, { "Relación de aspecto de cinemáticas", "Cambia la relación de aspecto de las cinemáticas en tiempo real." } }, { ELanguage::Italian, { "Rapporto d'aspetto dei filmati", "Cambia il rapporto d'aspetto dei filmati in tempo reale." } } }; CONFIG_DEFINE_ENUM_LOCALE(ECutsceneAspectRatio) { { ELanguage::English, { { ECutsceneAspectRatio::Original, { "Original", "Original: locks cutscenes to their original 16:9 aspect ratio." } }, { ECutsceneAspectRatio::Unlocked, { "Unlocked", "Unlocked: allows cutscenes to adjust their aspect ratio to the window size. WARNING: this will introduce visual oddities past the original 16:9 aspect ratio." } } } }, { ELanguage::Japanese, { { ECutsceneAspectRatio::Original, { "オリジナル", "オリジナル: カットシーンを元の16:9のアスペクト比に固定します" } }, { ECutsceneAspectRatio::Unlocked, { "解除", "解除: カットシーンのアスペクト比をウィンドウサイズに合わせて調整します 警告: 元の16:9のアスペクト比を超えると視覚的な異常が発生します" } }, } }, { ELanguage::German, { { ECutsceneAspectRatio::Original, { "Original", "Original: Behält die Zwischensequenzen in ihrem originalen 16:9 Seitenverhältnis." } }, { ECutsceneAspectRatio::Unlocked, { "Entsperrt", "Entsperrt: Erlaubt Zwischensequenzen ihr Seitenverhältnis der Fenstergröße anzupassen. WARNUNG: Diese Option kann zu visuellen Fehlern außerhalb der Grenzen des ursprünglichen Seitenverhältnisses führen." } } } }, { ELanguage::French, { { ECutsceneAspectRatio::Original, { "Original", "Original : force les cinématiques dans leur format 16:9 d'origine." } }, { ECutsceneAspectRatio::Unlocked, { "Libre", "Libre : permet aux cinématiques d'adapter leur format d'image à la taille de la fenêtre. ATTENTION : au delà du format 16:9 d'origine, des bugs visuels apparaitront." } }, } }, { ELanguage::Spanish, { { ECutsceneAspectRatio::Original, { "Original", "Original: muestra las cinemáticas en su relación de aspecto original de 16:9." } }, { ECutsceneAspectRatio::Unlocked, { "Desbloqueado", "Desbloqueado: permite que las cinemáticas ajusten su relación de aspecto al tamaño de la ventana. ADVERTENCIA: esto introducirá rarezas visuales más allá de la relación de aspecto original de 16:9." } }, } }, { ELanguage::Italian, { { ECutsceneAspectRatio::Original, { "Originale", "Originale: blocca il rapporto d'aspetto dei filmati a 16:9." } }, { ECutsceneAspectRatio::Unlocked, { "Sbloccato", "Sbloccato: il rapporto d'aspetto verrà regolato in base alle dimensioni della finestra. ATTENZIONE: questa opzione potrebbe causare dei problemi visivi se il rapporto d'aspetto è oltre 16:9." } } } } }; CONFIG_DEFINE_LOCALE(UIAlignmentMode) { { ELanguage::English, { "UI Alignment Mode", "Change how the UI aligns with the display." } }, { ELanguage::Japanese, { "UIアライメントモード", "UIとディスプレイの配置を変更できます" } }, { ELanguage::German, { "Benutzeroberflächenausrichtung", "Verändere wie die Benutzeroberfläche sich mit dem Bildschirm ausrichtet." } }, { ELanguage::French, { "Alignement de l'IU", "Modifie l'alignement de l'interface utilisateur sur l'écran." } }, { ELanguage::Spanish, { "Modo de alineamiento de UI", "Cambia la alineación de la interfaz de usuario con la pantalla." } }, { ELanguage::Italian, { "Modalità allineamento interfaccia", "Modifica come l'interfaccia si allinea con lo schermo." } } }; CONFIG_DEFINE_ENUM_LOCALE(EUIAlignmentMode) { { ELanguage::English, { { EUIAlignmentMode::Edge, { "Edge", "Edge: the UI will align with the edges of the display." } }, { EUIAlignmentMode::Centre, { "Center", "Center: the UI will align with the center of the display." } } } }, { ELanguage::Japanese, { { EUIAlignmentMode::Edge, { "エッジ", "エッジ: UIがディスプレイの端に揃います" } }, { EUIAlignmentMode::Centre, { "センター", "センター: UIがディスプレイの中央に揃います" } }, } }, { ELanguage::German, { { EUIAlignmentMode::Edge, { "Rand", "Rand: Die Benutzeroberfläche richtet sich zum Rand des Bildschirms aus." } }, { EUIAlignmentMode::Centre, { "Mitte", "Mitte: Die Benutzeroberfläche richtet sich zur Mitte des Bildschirms aus." } } } }, { ELanguage::French, { { EUIAlignmentMode::Edge, { "Bord", "Bord : l'interface utilisateur sera alignée sur les bords de l'écran." } }, { EUIAlignmentMode::Centre, { "Centrée", "Centrée : l'interface utilisateur sera alignée sur le centre de l'écran." } }, } }, { ELanguage::Spanish, { { EUIAlignmentMode::Edge, { "Borde", "Borde: la interfaz de usuario se alineará con los bordes de la pantalla." } }, { EUIAlignmentMode::Centre, { "Centro", "Centro: la interfaz de usuario se alineará con el centro de la pantalla." } }, } }, { ELanguage::Italian, { { EUIAlignmentMode::Edge, { "Bordi", "Bordi: l'interfaccia si allineerà con i bordi dello schermo." } }, { EUIAlignmentMode::Centre, { "Centro", "Centro: l'interfaccia si allineerà con il centro dello schermo." } } } } }; ================================================ FILE: MarathonRecomp/locale/locale.cpp ================================================ #include #include /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! LOCALISATION NOTES !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Ensure brand names are always presented on the same line. Correct: This is a string that contains a brand name like Xbox 360, which is one of the two consoles to have a port of SONIC THE HEDGEHOG. Incorrect: This is a string that contains a brand name like Xbox 360, which is one of the two consoles to have a port of SONIC THE HEDGEHOG. - Ensure your locale is added in the correct order following the language enum. Correct: { { ELanguage::English, "Example" }, { ELanguage::Japanese, "Example" }, { ELanguage::German, "Example" }, { ELanguage::French, "Example" }, { ELanguage::Spanish, "Example" }, { ELanguage::Italian, "Example" } } Incorrect: { { ELanguage::English, "Example" }, { ELanguage::French, "Example" }, { ELanguage::Spanish, "Example" }, { ELanguage::German, "Example" }, { ELanguage::Italian, "Example" }, { ELanguage::Japanese, "Example" } } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ std::unordered_map> g_locale = { { "Options_Header_Name", { { ELanguage::English, "OPTIONS" }, { ELanguage::Japanese, "OPTIONS" }, { ELanguage::German, "OPTIONEN" }, { ELanguage::French, "OPTIONS" }, { ELanguage::Spanish, "OPCIONES" }, { ELanguage::Italian, "OPZIONI" } } }, { "Options_Category_System", { { ELanguage::English, "System settings" }, { ELanguage::Japanese, "システム設定" }, { ELanguage::German, "System-Einstellungen" }, { ELanguage::French, "Paramètres système" }, { ELanguage::Spanish, "Configuración del sistema" }, { ELanguage::Italian, "Impostazioni sistema" } } }, { "Options_Desc_Category_System", { { ELanguage::English, "Adjust system settings." }, { ELanguage::Japanese, "システムの設定を変更します" }, { ELanguage::German, "Verändere System-Einstellungen." }, { ELanguage::French, "Modifier les paramètres système." }, { ELanguage::Spanish, "Ajustar la configuración del sistema." }, { ELanguage::Italian, "Modifica le impostazioni di sistema." } } }, { "Options_Category_Input", { { ELanguage::English, "Input settings" }, { ELanguage::Japanese, "入力設定" }, { ELanguage::German, "Eingabe-Einstellungen" }, { ELanguage::French, "Paramètres d'entrée" }, { ELanguage::Spanish, "Configuración de entrada" }, { ELanguage::Italian, "Impostazioni input" } } }, { "Options_Desc_Category_Input", { { ELanguage::English, "Adjust input settings." }, { ELanguage::Japanese, "入力の設定を変更します" }, { ELanguage::German, "Verändere Eingabe-Einstellungen." }, { ELanguage::French, "Modifier les paramètres d'entrée." }, { ELanguage::Spanish, "Ajustar la configuración de entrada." }, { ELanguage::Italian, "Modifica le impostazioni input." } } }, { "Options_Category_Audio", { { ELanguage::English, "Audio settings" }, { ELanguage::Japanese, "オーディオ設定" }, { ELanguage::German, "Audio-Einstellungen" }, { ELanguage::French, "Paramètres audio" }, { ELanguage::Spanish, "Configuración de audio" }, { ELanguage::Italian, "Impostazioni audio" } } }, { "Options_Desc_Category_Audio", { { ELanguage::English, "Adjust audio settings." }, { ELanguage::Japanese, "オーディオの設定を変更します" }, { ELanguage::German, "Verändere Audio-Einstellungen." }, { ELanguage::French, "Modifier les paramètres audio." }, { ELanguage::Spanish, "Ajustar la configuración de audio." }, { ELanguage::Italian, "Modifica le impostazioni audio." } } }, { "Options_Category_Video", { { ELanguage::English, "Video settings" }, { ELanguage::Japanese, "ビデオ設定" }, { ELanguage::German, "Video-Einstellungen" }, { ELanguage::French, "Paramètres vidéo" }, { ELanguage::Spanish, "Configuración de vídeo" }, { ELanguage::Italian, "Impostazioni video" } } }, { "Options_Desc_Category_Video", { { ELanguage::English, "Adjust video settings." }, { ELanguage::Japanese, "ビデオの設定を変更します" }, { ELanguage::German, "Verändere Video-Einstellungen." }, { ELanguage::French, "Modifier les paramètres vidéo." }, { ELanguage::Spanish, "Ajustar la configuración de vídeo." }, { ELanguage::Italian, "Modifica le impostazioni video." } } }, { "Options_Category_Debug", { { ELanguage::English, "Debug settings" }, { ELanguage::Japanese, "デバッグ設定" }, { ELanguage::German, "Debug-Einstellungen" }, { ELanguage::French, "Paramètres de débogage" }, { ELanguage::Spanish, "Configuración de depuración" }, { ELanguage::Italian, "Impostazioni debug" } } }, { "Options_Desc_Category_Debug", { { ELanguage::English, "Adjust debug settings." }, { ELanguage::Japanese, "デバッグの設定を変更します" }, { ELanguage::German, "Verändere Debug-Einstellungen." }, { ELanguage::French, "Modifier les paramètres de débogage." }, { ELanguage::Spanish, "Ajustar la configuración de depuración." }, { ELanguage::Italian, "Modifica le impostazioni debug." } } }, { // Notes: integer values in the options menu (e.g. FPS) when at their maximum value. "Options_Value_Max", { { ELanguage::English, "MAX" }, { ELanguage::Japanese, "MAX" }, { ELanguage::German, "MAX" }, { ELanguage::French, "MAX" }, { ELanguage::Spanish, "MÁX" }, { ELanguage::Italian, "MAX" } } }, { "Options_Name_WindowSize", { { ELanguage::English, "Window Size" }, { ELanguage::Japanese, "ウィンドウサイズ" }, { ELanguage::German, "Fenstergröße" }, { ELanguage::French, "Taille de la fenêtre" }, { ELanguage::Spanish, "Tamaño de ventana" }, { ELanguage::Italian, "Dimensioni della finestra" } } }, { "Options_Desc_WindowSize", { { ELanguage::English, "Adjust the size of the game window in windowed mode." }, { ELanguage::Japanese, "ゲームのウィンドウサイズを設定できます" }, { ELanguage::German, "Passe die Fenstergröße des Spiels im Fenstermodus an." }, { ELanguage::French, "Définir la résolution de jeu en mode fenêtré." }, { ELanguage::Spanish, "Ajusta el tamaño de la ventana de juego en modo ventana." }, { ELanguage::Italian, "Regola la dimensione della finestra del gioco in modalità finestra." } } }, { // Notes: description for options that cannot be accessed during gameplay (e.g. Language). "Options_Desc_NotAvailable", { { ELanguage::English, "This option is not available during gameplay." }, { ELanguage::Japanese, "このオプションはゲームプレイ中は使用できません" }, { ELanguage::German, "Diese Einstellung kann während des Spiels nicht verändert werden." }, { ELanguage::French, "Cette option est indisponible en cours de jeu." }, { ELanguage::Spanish, "Esta opción no está disponible durante la partida." }, { ELanguage::Italian, "Questa opzione non è disponibile durante il gioco." } } }, { // Notes: description for options that are not implemented yet in development builds. "Options_Desc_NotImplemented", { { ELanguage::English, "This option is not implemented yet." }, { ELanguage::Japanese, "このオプションはまだ実装されていません" }, { ELanguage::German, "Diese Einstellung wurde noch nicht implementiert." }, { ELanguage::French, "Cette option n'est pas encore implémentée." }, { ELanguage::Spanish, "Esta opción aún no está implementada." }, { ELanguage::Italian, "Questa opzione non è ancora implementata." } } }, { // Notes: currently the description for Window Size when in fullscreen. "Options_Desc_NotAvailableFullscreen", { { ELanguage::English, "This option is not available in fullscreen mode." }, { ELanguage::Japanese, "このオプションはフルスクリーンモードでは変更できません" }, { ELanguage::German, "Diese Option ist im Vollbildmodus nicht verfügbar." }, { ELanguage::French, "Cette option n'est pas disponible en mode plein écran." }, { ELanguage::Spanish, "Esta opción no está disponible en modo pantalla completa." }, { ELanguage::Italian, "Questa opzione non è disponibile in modalità schermo pieno." } } }, { // Notes: currently the description for Monitor when in fullscreen. "Options_Desc_NotAvailableWindowed", { { ELanguage::English, "This option is not available in windowed mode." }, { ELanguage::Japanese, "このオプションはウィンドウモードでは変更できません" }, { ELanguage::German, "Diese Option ist im Fenstermodus nicht verfügbar." }, { ELanguage::French, "Cette option n'est pas disponible en mode fenêtré." }, { ELanguage::Spanish, "Esta opción no está disponible en modo ventana." }, { ELanguage::Italian, "Questa opzione non è disponibile in modalità finestra." } } }, { // Notes: currently the description for Monitor when the user only has one display connected. "Options_Desc_NotAvailableHardware", { { ELanguage::English, "This option is not available with your current hardware configuration." }, { ELanguage::Japanese, "このオプションは現在のハードウェア構成で変更できません" }, { ELanguage::German, "Diese Option ist mit der momentanen Hardwarekonfiguration nicht verfügbar." }, { ELanguage::French, "Cette option n'est pas disponible avec votre configuration matérielle actuelle." }, { ELanguage::Spanish, "Esta opción no está disponible con tu configuración actual de hardware." }, { ELanguage::Italian, "Questa opzione non è disponibile con l'hardware in tuo possesso." } } }, { // Notes: description for Transparency Anti-Aliasing when MSAA is disabled. "Options_Desc_NotAvailableMSAA", { { ELanguage::English, "This option is not available without MSAA." }, { ELanguage::Japanese, "このオプションはMSAAなしで変更できません" }, { ELanguage::German, "Diese Option ist ohne MSAA nicht verfügbar." }, { ELanguage::French, "Cette option n'est pas disponible sans MSAA." }, { ELanguage::Spanish, "Esta opción no está disponible sin MSAA." }, { ELanguage::Italian, "Questa opzione non è disponibile senza MSAA." } } }, { // Notes: description for Music Attenuation when the user is not running a supported OS. "Options_Desc_OSNotSupported", { { ELanguage::English, "This option is not supported by your operating system." }, { ELanguage::Japanese, "このオプションは現在のOSで変更できません" }, { ELanguage::German, "Diese Option wird von diesem Betriebssystem nicht unterstützt." }, { ELanguage::French, "Cette option n'est pas prise en charge par votre système d'exploitation." }, { ELanguage::Spanish, "Esta opción no es compatible con tu sistema operativo." }, { ELanguage::Italian, "Questa opzione non è disponibile con il tuo sistema operativo." } } }, { "Options_Message_Restart", { { ELanguage::English, "The game needs to restart to apply\nthe following changes. OK?\n" }, { ELanguage::Japanese, "以下の変更を適用するには\nゲームを再起動する必要があります\nよろしいですか?\n" }, { ELanguage::German, "Das Spiel muss neu gestartet werden um die\nfolgenden Änderungen zu speichern. OK?\n" }, { ELanguage::French, "Le jeu doit redémarrer pour appliquer\nles modifications suivantes. OK ?\n" }, { ELanguage::Spanish, "Se necesita reiniciar el juego para\naplicar los siguientes cambios. ¿OK?\n" }, { ELanguage::Italian, "Il gioco deve essere riavviato per\napplicare le seguenti modifiche. OK?\n" } } }, { "MainMenu_GoldMedalResults_Name", { { ELanguage::English, "STATISTICS" }, { ELanguage::Japanese, "STATISTICS" }, { ELanguage::German, "STATISTIKEN" }, { ELanguage::French, "STATISTIQUES" }, { ELanguage::Spanish, "ESTADÍSTICAS" }, { ELanguage::Italian, "STATISTICHE" } } }, { "MainMenu_GoldMedalResults_Description", { { ELanguage::English, "Statistics: Displays lists of Gold Medals and Achievements" }, { ELanguage::Japanese, "スタティスティックス: ゴールドメダルと実績のリストを表示する" }, { ELanguage::German, "Statistiken: Zeigt eine Liste von Gold Medaillen und Erfolgen an" }, { ELanguage::French, "Statistiques : Affiche une liste des Médailles d'Or et des Accomplissements" }, { ELanguage::Spanish, "Estadísticas: Muestra los listados de las medallas y los logros" }, { ELanguage::Italian, "Statistiche: mostra gli elenchi delle medaglie d'oro e gli obiettivi" } } }, { "Achievements_Title", { { ELanguage::English, "Achievements" }, { ELanguage::Japanese, "実績" }, { ELanguage::German, "Erfolge" }, { ELanguage::French, "Accomplissements" }, { ELanguage::Spanish, "Logros" }, { ELanguage::Italian, "Obiettivi" } } }, { "Achievements_Title_Uppercase", { { ELanguage::English, "ACHIEVEMENTS" }, { ELanguage::Japanese, "実績" }, { ELanguage::German, "ERFOLGE" }, { ELanguage::French, "ACCOMPLISSEMENTS" }, { ELanguage::Spanish, "LOGROS" }, { ELanguage::Italian, "OBIETTIVI" } } }, { "Achievements_GoldMedals", { { ELanguage::English, "Gold Medals" }, { ELanguage::Japanese, "ゴールドメダル" }, { ELanguage::German, "Gold Medaillen" }, { ELanguage::French, "Médailles d'or" }, { ELanguage::Spanish, "Medallas de oro" }, { ELanguage::Italian, "Medaglie d'oro" } } }, { "Achievements_GoldMedals_Uppercase", { { ELanguage::English, "GOLD MEDALS" }, { ELanguage::Japanese, "ゴールドメダル" }, { ELanguage::German, "GOLD MEDAILLEN" }, { ELanguage::French, "MÉDAILLES D'OR" }, { ELanguage::Spanish, "MEDALLAS DE ORO" }, { ELanguage::Italian, "MEDAGLIE D'ORO" } } }, { "Achievements_Unlock", { { ELanguage::English, "Achievement Unlocked!" }, { ELanguage::Japanese, "実績のロックが解除されました" }, { ELanguage::German, "Erfolg Freigeschaltet!" }, { ELanguage::French, "Succès déverrouillé !" }, { ELanguage::Spanish, "¡Logro desbloqueado!" }, { ELanguage::Italian, "Obiettivo sbloccato!" } } }, { "Achievements_Progress", { { ELanguage::English, "PROGRESS %d/%d" }, { ELanguage::Japanese, "PROGRESS %d/%d" }, { ELanguage::German, "FORTSCHRITT %d/%d" }, { ELanguage::French, "PROGRESSION %d/%d" }, { ELanguage::Spanish, "PROGRESO %d/%d" }, { ELanguage::Italian, "PROGRESSI %d/%d" } } }, { // Locale required for font atlas generation. "Installer_MusicCredits", { { ELanguage::English, "♬ Result & Chill Lofi - Hotline Sehwani & SilverIceSound" }, }, }, { "Installer_Header_Installer", { { ELanguage::English, "INSTALLER" }, { ELanguage::Japanese, "INSTALL" }, { ELanguage::German, "INSTALLATION" }, { ELanguage::French, "INSTALLATEUR" }, { ELanguage::Spanish, "INSTALADOR" }, { ELanguage::Italian, "INSTALLATORE" }, }, }, { "Installer_Header_Installing", { { ELanguage::English, "INSTALLING" }, { ELanguage::Japanese, "INSTALL" }, { ELanguage::German, "INSTALLATION" }, { ELanguage::French, "INSTALLATION" }, { ELanguage::Spanish, "INSTALANDO" }, { ELanguage::Italian, "INSTALLANDO" }, } }, { "Installer_Page_SelectLanguage", { { ELanguage::English, "Please select a language." }, { ELanguage::Japanese, "言語を選択してください" }, { ELanguage::German, "Bitte eine Sprache auswählen." }, { ELanguage::French, "Choisissez une langue." }, { ELanguage::Spanish, "Selecciona un idioma." }, { ELanguage::Italian, "Seleziona una lingua." } } }, { "Installer_Page_Introduction", { { ELanguage::English, "Welcome to Marathon Recompiled!\n\nYou'll need an Xbox 360 copy of\nSONIC THE HEDGEHOG in order to proceed with the installation." }, { ELanguage::Japanese, "Marathon Recompiledへようこそ!\n\nインストールにはXbox 360版の\n「ソニック・ザ・ヘッジホッグ」\nが必要です" }, { ELanguage::German, "Willkommen zu Marathon Recompiled!\n\nEs wird eine Xbox 360 Kopie von\nSONIC THE HEDGEHOG benötigt um mit der Installation fortfahren zu können." }, { ELanguage::French, "Bienvenue sur Marathon Recompiled !\n\nVous aurez besoin d'une copie de\nSONIC THE HEDGEHOG pour\nXbox 360 pour procéder à l'installation." }, { ELanguage::Spanish, "¡Bienvenido a Marathon Recompiled!\n\nNecesitas una copia de\nSONIC THE HEDGEHOG de\nXbox 360 para continuar con la instalación." }, { ELanguage::Italian, "Benvenuto a Marathon Recompiled!\n\nDovrai avere una copia di\nSONIC THE HEDGEHOG per la\nXbox 360 per proseguire con l'installazione." } } }, { "Installer_Page_SelectGame", { { ELanguage::English, "Add the sources for the game." }, { ELanguage::Japanese, "ゲームのソースを追加" }, { ELanguage::German, "Füge die Quellen für das Spiel." }, { ELanguage::French, "Ajouter les fichiers du jeu." }, { ELanguage::Spanish, "Añade las fuentes para el juego." }, { ELanguage::Italian, "Aggiungi le fonti per il gioco." } } }, { "Installer_Page_SelectDLC", { { ELanguage::English, "Add the sources for the DLC." }, { ELanguage::Japanese, "DLCのソースを追加" }, { ELanguage::German, "Füge die Quellen für die Erweiterungen des Spiels hinzu." }, { ELanguage::French, "Ajouter les fichiers pour les DLCs." }, { ELanguage::Spanish, "Añade las fuentes para el DLC." }, { ELanguage::Italian, "Aggiungi le fonti per i DLC." } } }, { "Installer_Page_CheckSpace", { { ELanguage::English, "The content will be installed to the program's folder.\n\n" }, { ELanguage::Japanese, "コンテンツはプログラムのフォルダに\nインストールされます\n\n" }, { ELanguage::German, "Der Inhalt wird in dem Ordner des Programms installiert.\n\n" }, { ELanguage::French, "Le contenu sera installé dans le même dossier que le programme.\n\n" }, { ELanguage::Spanish, "El contenido será instalado a la carpeta del programa.\n\n" }, { ELanguage::Italian, "Il contenuto verrà installato nella cartella di questo programma.\n\n" } } }, { "Installer_Page_Installing", { { ELanguage::English, "Please wait while the content is being installed..." }, { ELanguage::Japanese, "コンテンツのインストール中はお待ち\nください" }, { ELanguage::German, "Bitte warten. Der Inhalt wird installiert..." }, { ELanguage::French, "Veuillez patienter pendant l'installation du contenu..." }, { ELanguage::Spanish, "Por favor, espera mientras el contenido se instala... " }, { ELanguage::Italian, "Attendi mentre il contenuto viene installato... " } } }, { "Installer_Page_InstallSucceeded", { { ELanguage::English, "Installation complete!\n\nThis project is brought to you by:" }, { ELanguage::Japanese, "インストール完了!\n\nプロジェクト制作:" }, { ELanguage::German, "Installation abgeschlossen!\n\nDieses Projekt wird präsentiert von:" }, { ELanguage::French, "Installation terminée !\n\nCe projet vous est présenté par :" }, { ELanguage::Spanish, "¡Instalación completada!\n\nEste proyecto ha sido posible gracias a:" }, { ELanguage::Italian, "Installazione completata!\n\nQuesto progetto è stato creato da:" } } }, { "Installer_Page_InstallFailed", { { ELanguage::English, "Installation failed.\n\n" }, { ELanguage::Japanese, "インストールに失敗しました\n\n" }, { ELanguage::German, "Installation fehlgeschlagen.\n\n" }, { ELanguage::French, "L'installation a échoué.\n\n" }, { ELanguage::Spanish, "La instalación falló.\n\n" }, { ELanguage::Italian, "Installazione fallita.\n\n" } } }, { "Installer_Step_Game", { { ELanguage::English, "Game Data" }, { ELanguage::Japanese, "ゲームデータ" }, { ELanguage::German, "Spieldaten" }, { ELanguage::French, "Fichiers du jeu" }, { ELanguage::Spanish, "Archivos del juego" }, { ELanguage::Italian, "Dati del gioco" } } }, { "Installer_Step_RequiredSpace", { { ELanguage::English, "Required space: %2.2f GiB" }, { ELanguage::Japanese, "必要な容量: %2.2f GiB" }, { ELanguage::German, "Benötigter Speicherplatz:\n%2.2f GiB\n" }, { ELanguage::French, "Espace nécessaire : %2.2f Gio" }, { ELanguage::Spanish, "Espacio necesario: %2.2f GiB" }, { ELanguage::Italian, "Spazio necessario: %2.2f GiB" } } }, { "Installer_Step_AvailableSpace", { { ELanguage::English, "Available space: %2.2f GiB" }, { ELanguage::Japanese, "使用可能な容量: %2.2f GiB" }, { ELanguage::German, "Verfügbarer Speicherplatz:\n%2.2f GiB\n" }, { ELanguage::French, "Espace disponible : %2.2f Gio" }, { ELanguage::Spanish, "Espacio disponible: %2.2f GiB" }, { ELanguage::Italian, "Spazio disponibile: %2.2f GiB" } } }, { "Installer_Button_Next", { { ELanguage::English, "Next" }, { ELanguage::Japanese, "次へ" }, { ELanguage::German, "Weiter" }, { ELanguage::French, "Suivant" }, { ELanguage::Spanish, "Siguiente" }, { ELanguage::Italian, "Continua" } } }, { "Installer_Button_Skip", { { ELanguage::English, "Skip" }, { ELanguage::Japanese, "スキップ" }, { ELanguage::German, "Überspringen" }, { ELanguage::French, "Ignorer" }, { ELanguage::Spanish, "Saltar" }, { ELanguage::Italian, "Salta" } } }, { "Installer_Button_AddFiles", { { ELanguage::English, "Add Files" }, { ELanguage::Japanese, "ファイルを追加" }, { ELanguage::German, "Dateien hinzufügen" }, { ELanguage::French, "Ajouter des fichiers" }, { ELanguage::Spanish, "Añadir archivos" }, { ELanguage::Italian, "Aggiungi file" } } }, { "Installer_Button_AddFolder", { { ELanguage::English, "Add Folder" }, { ELanguage::Japanese, "フォルダを追加" }, { ELanguage::German, "Ordner hinzufügen" }, { ELanguage::French, "Ajouter un dossier" }, { ELanguage::Spanish, "Añadir carpeta" }, { ELanguage::Italian, "Aggiungi cartella" } } }, { // Notes: message appears when using the "Add Files" option and choosing any file that is not an Xbox 360 game dump. "Installer_Message_InvalidFilesList", { { ELanguage::English, "The following selected files are invalid:" }, { ELanguage::Japanese, "選択した次のファイルは無効です:" }, { ELanguage::German, "Die folgenden Dateien sind ungültig:" }, { ELanguage::French, "Les fichiers suivants ne sont pas valides :" }, { ELanguage::Spanish, "Los siguientes archivos no son válidos:" }, { ELanguage::Italian, "I seguenti file non sono validi:" } } }, { // Notes: message appears in the event there are some invalid files after adding the DLC and moving onto the next step. "Installer_Message_InvalidFiles", { { ELanguage::English, "Some of the files that have been provided are not valid. Please make sure all the specified files are correct and try again." }, { ELanguage::Japanese, "提供されたファイルの一部が有効ではありません指定されたファイルがすべて正しいことを確認してもう一度お試しください" }, { ELanguage::German, "Einige Dateien, die bereitgestellt wurden sind ungültig. Bitte stelle sicher, dass die angegebenen Dateien korrekt sind und versuche es erneut." }, { ELanguage::French, "Certains fichiers fournis ne sont pas valides. Veuillez vous assurer que tous les fichiers spécifiés sont corrects et réessayez." }, { ELanguage::Spanish, "Algunos de los archivos seleccionados no son válidos. Por favor, asegúrate de que todos los archivos son correctos e inténtalo de nuevo." }, { ELanguage::Italian, "Alcuni dei file che sono stati selezionati non sono validi. Assicurati che tutti i file sono quelli corretti e riprova." } } }, { // Notes: message appears when clicking the "Add Files" option for the first time. "Installer_Message_FilePickerTutorial", { { ELanguage::English, "Select a digital dump with content from the game.\n\nThese files can be obtained from your Xbox 360 hard drive by following the instructions on the GitHub page.\n\nFor choosing a folder with extracted and unmodified game files, use the \"Add Folder\" option instead." }, { ELanguage::Japanese, "ゲームのコンテンツを含む デジタルダンプを選択してください\n\nこれらのファイルは GitHubページの指示に従って\nXbox 360ハードドライブから取得できます\n\n抽出された変更されていないゲームファイルを含むフォルダーを選択するには代わりに「フォルダの追加」オプションを使用してください" }, { ELanguage::German, "Wähle einen digitalen Dump von dem Spiel.\n\nDie Dateien können über die Festplatte deiner\nXbox 360 erlangt werden. Folge hierfür den Anweisungen auf der GitHub Seite.\n\nUm einen Ordner mit unmodifizierten Spieldateien auszuwählen, benutze die \"Ordner hinzufügen\" Option stattdessen." }, { ELanguage::French, "Sélectionnez une copie dématérialisée avec le contenu du jeu de base.\n\nCes fichiers peuvent être obtenus à partir du disque dur de votre Xbox 360 en suivant les instructions de la page GitHub.\n\nPour choisir un dossier contenant les fichiers de jeu extraits et non modifiés, utilisez plutôt l'option \"Ajouter un dossier\"." }, { ELanguage::Spanish, "Selecciona una copia digital con contenido del juego.\n\nPuedes obtener los archivos de tu disco duro de\nXbox 360 siguiendo las instrucciones de la página de GitHub.\n\nPara elegir una carpeta con archivos extraídos sin modificar, utiliza la opción \"Añadir carpeta\"." }, { ELanguage::Italian, "Seleziona una copia digitale con i contenuti del gioco.\n\nQuesti file possono essere ottenuti dall'hard drive della tua Xbox 360 seguendo le istruzioni sulla pagina GitHub.\n\nPer selezionare una cartella con file estratti e non modificati usa l'opzione \"Aggiungi cartella\"." } } }, { // Notes: message appears when clicking the "Add Folder" option for the first time. "Installer_Message_FolderPickerTutorial", { { ELanguage::English, "Select a folder that contains the unmodified files that have been extracted from the game.\n\nThese files can be obtained from your Xbox 360 hard drive by following the instructions on the GitHub page.\n\nFor choosing a digital dump, use the \"Add Files\" option instead." }, { ELanguage::Japanese, "ゲームから抽出された変更されていないファイルを含むフォルダを選択してください\n\nこれらのファイルは GitHubページの指示に従って\nXbox 360ハードドライブから取得できます\n\nデジタルダンプを選択するには\n代わりに「ファイルの追加」オプションを使用してください" }, { ELanguage::German, "Wähle einen Ordner, der unmodifizierte Dateien, die vom Spiel extrahiert wurden enthält.\n\nDie Dateien können über die Festplatte deiner\nXbox 360 erlangt werden. Folge hierfür den Anweisungen auf der GitHub Seite.\n\nUm einen digitalen Dump auszuwählen, benutze die \"Dateien hinzufügen\" Option stattdessen." }, { ELanguage::French, "Sélectionnez un dossier contenant les fichiers extraits du jeu de base.\n\nCes fichiers peuvent être obtenus à partir du disque dur de votre Xbox 360 en suivant les instructions de la page GitHub.\n\nPour choisir une copie dématérialisée, utilisez plutôt l'option \"Ajouter des fichiers\"." }, { ELanguage::Spanish, "Selecciona una carpeta que contenga los archivos sin modificar extraídos del juego.\n\nPuedes obtener los archivos de tu disco duro de\nXbox 360 siguiendo las instrucciones de la página de GitHub.\n\nPara elegir una copia digital, utiliza la opción \"Añadir archivos\"." }, { ELanguage::Italian, "Seleziona una cartella che contiene i file non modificati che sono stati estratti dal gioco.\n\nQuesti file possono essere ottenuti dall'hard drive della tua Xbox 360 seguendo le istruzioni sulla pagina GitHub.\n\nPer selezionare una copia digitale usa l'opzione \"Aggiungi file\"." } } }, { // Notes: message appears when choosing the Install option at the title screen when the user is missing DLC content. // TODO: adjust line breaks for new message window. "Installer_Message_TitleMissingDLC", { { ELanguage::English, "This will restart the game to\nallow you to install any DLC\nthat you are missing.\n\nWould you like to install missing\ncontent?" }, { ELanguage::Japanese, "これによりゲームが再起動し不足しているDLCを\nインストールできるようになります\n\n不足しているコンテンツを\nインストールしますか?" }, { ELanguage::German, "Das Spiel wird neu gestartet\num die Installation einer fehlenden\nErweiterung zu ermöglichen.\n\nMöchtest du den fehlenden\nInhalt installieren?" }, { ELanguage::French, "Cela redémarrera le jeu pour vous\npermettre d'installer les DLC\nmanquants.\n\nSouhaitez-vous installer le\ncontenu manquant ?" }, { ELanguage::Spanish, "Esta opción reiniciará el juego\npara permitirte instalar los DLC\nque falten.\n\n¿Quieres instalar el contenido\nque falta?" }, { ELanguage::Italian, "Questa opzione riavviera il gioco\nper farti installare qualsiasi DLC\nche non hai installato.\n\nVuoi installare i DLC mancanti?" } } }, { // Notes: message appears when choosing the Install option at the title screen when the user is not missing any content. // TODO: adjust line breaks for new message window. "Installer_Message_Title", { { ELanguage::English, "This restarts the game to\nallow you to install any DLC\nthat you may be missing.\n\nYou are not currently\nmissing any DLC.\n\nWould you like to proceed\nanyway?" }, { ELanguage::Japanese, "これによりゲームが再起動され\n不足しているDLCを\nインストールできるようになります\n\n現在 不足しているDLCはありません\n\nそれでも続行しますか?" }, { ELanguage::German, "Das Spiel wird neu gestartet\num die Installation einer fehlenden\nErweiterung zu ermöglichen.\n\nEs kann keine weitere Erweiterung\ninstalliert werden.\n\nMöchtest du trotzdem fortfahren?" }, { ELanguage::French, "Cela redémarrera le jeu pour vous\npermettre d'installer les DLC\nmanquants.\n\nIl ne vous manque aucun DLC.\n\nVoulez-vous quand même continuer ?" }, { ELanguage::Spanish, "Esto reiniciará el juego\npara permitirte instalar\nlos DLC que falten.\n\nActualmente, no falta ningún\nDLC por instalarse.\n\n¿Quieres continuar de todos\nmodos?" }, { ELanguage::Italian, "Questa opzione riavviera il gioco\nper farti installare qualsiasi DLC\nche non hai installato.\n\nHai già installato tutti i DLC.\n\nVuoi procedere comunque?" } } }, { // Notes: message appears when user chooses "Quit" on the first available installation screen. "Installer_Message_Quit", { { ELanguage::English, "Exit the installer.\nOK?" }, { ELanguage::Japanese, "インストーラーを終了します\nよろしいですか?" }, { ELanguage::German, "Die Installation verlassen.\nOK?" }, { ELanguage::French, "Quitter l'installateur.\nOK ?" }, { ELanguage::Spanish, "¿Salir del instalador?" }, { ELanguage::Italian, "Esci dall'installatore.\nOK?" } } }, { // Notes: message appears when user chooses "Cancel" during installation. "Installer_Message_Cancel", { { ELanguage::English, "Cancel the installation.\nOK?" }, { ELanguage::Japanese, "インストールをキャンセルします\nよろしいですか?" }, { ELanguage::German, "Die Installation abbrechen.\nOK?" }, { ELanguage::French, "Annuler l'installation.\nOK ?" }, { ELanguage::Spanish, "¿Cancelar la instalación?" }, { ELanguage::Italian, "Annulla l'installazione.\nOK?" } } }, { // Notes: message appears when pressing B at the title screen. "Title_Message_Quit", { { ELanguage::English, "Exit the game.\nOK?" }, { ELanguage::Japanese, "ゲームを終了します\nよろしいですか?" }, { ELanguage::German, "Das Spiel verlassen.\nOK?" }, { ELanguage::French, "Quitter le jeu.\nOK ?" }, { ELanguage::Spanish, "¿Salir del juego?" }, { ELanguage::Italian, "Esci dal gioco.\nOK?" } } }, { // Notes: message appears when SonicNextAchievementData.bin is corrupted (mismatching file size, bad signature, incorrect version or invalid checksum) upon loading save data. // To make this occur, open the file in any editor and just remove a large chunk of data. "Title_Message_LoadAchievementDataCorrupt", { { ELanguage::English, "Load failed. Achievement data is corrupted.\nIf you continue your achievement progress will\nbe lost." }, { ELanguage::Japanese, "ロード失敗。業績のデータが破損している。\nセーブしていない進行状況は失われます" }, { ELanguage::German, "Laden fehlgeschlagen. Erfolgs dateien sind\nkorrupt. Wenn du fortfährst wird dein Erfolgs\nfortschritt gelöscht." }, { ELanguage::French, "Chargement échoué. Les données d'Accomplissements\nsont corrompus. Si vous continuez, vos\naccomplissements seront perdus." }, { ELanguage::Spanish, "Error al cargar. Los datos de los logros están\ndañados. Si continúas, se perderá el progreso\nde tus logros." }, { ELanguage::Italian, "Caricamento fallito. I dati degli obiettivi\nsono danneggiati. Se continui perderai tutti\ni tuoi obiettivi." } } }, { // Notes: message appears when SonicNextAchievementData.bin cannot be loaded upon loading save data. // To make this occur, lock the SonicNextAchievementData.bin file using an external program so that it cannot be accessed by the game. "Title_Message_LoadAchievementDataIOError", { { ELanguage::English, "Load failed. Achievement data cannot be loaded.\nIf you continue you will not be able to save\nyour achievement progress." }, { ELanguage::Japanese, "ロード失敗。業績のデータがロードできません。\n進行状況は失われますセーブしません" }, { ELanguage::German, "Laden fehlgeschlagen. Erfolgs dateien können nicht\ngeladen werden. Wenn du fortfährst wirst du deinen\nErfolgs fortschritt nicht speichern können." }, { ELanguage::French, "Chargement échoué. Les données d'Accomplissements\nn'ont pas pu être chargé. Si vous continuez, vous ne\npourrez pas sauvegarder vos accomplissements." }, { ELanguage::Spanish, "Error al cargar. No se pueden cargar los datos\nde los logros. Si continúas, no podrás guardar\nel progreso de tus logros." }, { ELanguage::Italian, "Caricamento fallito. Impossibile caricare i dati\ndegli obiettivi. Se continui non potrai salvare\ni tuoi obiettivi." } } }, { // Notes: message appears when SonicNextAchievementData.bin cannot be saved upon saving save data. // To make this occur, lock the SonicNextAchievementData.bin file using an external program so that it cannot be accessed by the game. "Title_Message_SaveAchievementDataIOError", { { ELanguage::English, "Save failed. Achievement data cannot be saved.\nIf you continue you will not be able to save\nyour achievement progress." }, { ELanguage::Japanese, "セーブ失敗。業績のデータがセーブできません。\n進行状況は失われますセーブしません" }, { ELanguage::German, "Speichern fehlgeschlagen. Erfolgs dateien können\nnicht gespeichert werden. Wenn du fortfährst wirst\ndu deinen Erfolgs fortschritt nicht speichern können." }, { ELanguage::French, "Sauvegarde échoué. Les données d'Accomplissements\nn'ont pas pu être sauvegardé. Si vous continuez, vous ne\npourrez pas sauvegarder vos accomplissements." }, { ELanguage::Spanish, "Error al guardar. No se pueden guardar los datos\nde los logros. Si continúas, no podrás guardar\ntu progreso en los logros." }, { ELanguage::Italian, "Salvataggio fallito. Impossibile salvare i dati\ndegli obiettivi. Se continui non potrai salvare\ni tuoi obiettivi." } } }, { "Title_Message_UpdateAvailable", { { ELanguage::English, "An update is available!\n\nWould you like to visit the\nreleases page to download it?" }, { ELanguage::Japanese, "アップデートが利用可能です\n\nリリースページにアクセスして\nダウンロードしますか?" }, { ELanguage::German, "Ein Update ist verfügbar!\n\nMöchtest du die Release-Seite\nbesuchen um es herunterzuladen?" }, { ELanguage::French, "Une mise à jour est disponible !\n\nVoulez-vous visiter la page\ndes mises à jour pour la\ntélécharger ?" }, { ELanguage::Spanish, "¡Hay una actualización disponible!\n\n¿Quieres ir a la página\npara descargarla?" }, { ELanguage::Italian, "È disponibile un aggiornamento!\n\nVuoi visitare la pagina releases\nper scaricarlo?" } } }, { "Video_BackendError", { { ELanguage::English, "Unable to create a D3D12 (Windows) or Vulkan backend.\n\nPlease make sure that:\n\n- Your system meets the minimum requirements.\n- Your GPU drivers are up to date.\n- Your operating system is on the latest version available." }, { ELanguage::Japanese, "D3D12 (Windows)または\nVulkanバックエンドを作成できません\n\n次の点を確認してください:\n\n※システムが最小要件を満たしている\n※GPUドライバーが最新である\n※オペレーティングシステムが最新バージョンである" }, { ELanguage::German, "Es ist nicht möglich, ein D3D12 (Windows) oder Vulkan-Backend zu erstellen.\n\nBitte stelle sicher, dass:\n\n- Dein System die Mindestanforderungen erfüllt.\n- Deine GPU-Treiber auf dem neuesten Stand sind.\n- Dein Betriebssystem auf der neuesten verfügbaren Version ist." }, { ELanguage::French, "Impossible de créer un backend D3D12 (Windows) ou Vulkan.\n\nVeuillez vous assurer que :\n\n- Votre système répond aux critères minimums requis.\n- Les pilotes de votre processeur graphique sont à jour.\n- Votre système d'exploitation est à jour." }, { ELanguage::Spanish, "No se puede crear un entorno de D3D12 (Windows) o de Vulkan.\n\nPor favor, asegúrate de que:\n\n- Tu equipo cumple con los requisitos mínimos.\n- Los drivers de tu tarjeta gráfica están actualizados.\n- Tu sistema operativo está actualizado a la última versión.\n" }, { ELanguage::Italian, "Impossibile creare un backend D3D12 (Windows) o Vulkan.\n\nAssicurati che:\n\n- Il tuo sistema soddisfi i requisiti minimi.\n- I driver della scheda grafica siano aggiornati.\n- Il tuo sistema operativo sia aggiornato." } } }, { "System_Win32_MissingDLLs", { { ELanguage::English, "The module \"%s\" could not be found.\n\nPlease make sure that:\n\n- You extracted this copy of Marathon Recompiled fully and not just the *.exe file.\n- You are not running Marathon Recompiled from a *.zip file." }, { ELanguage::Japanese, "モジュール\"%s\"が見つかりませんでした\n\n次の点を確認してください:\n\n※Marathon Recompiledの*.exeファイルだけを抽出していなく、 コピーを完全に抽出してること\n※Marathon Recompiledを*.zipファイルから実行していないこと" }, { ELanguage::German, "Das Modul \"%s\" konnte nicht gefunden werden.\n\nBitte stelle sicher, dass:\n\n- Diese Kopie von Marathon Recompiled vollständig entpackt wurde und nicht nur die *.exe-Datei.\n- Marathon Recompiled nicht direkt aus einer *.zip-Datei ausgeführt wird." }, { ELanguage::French, "Le module \"%s\" n'a pas pu être trouvé.\n\nVeuillez vous assurer que :\n\n- Vous avez extrait Marathon Recompiled dans son entièreté et pas seulement le fichier *.exe.\n- Vous n'exécutez pas Marathon Recompiled à partir d'un fichier *.zip." }, { ELanguage::Spanish, "No se pudo encontrar el módulo \"%s\".\n\nAsegúrese de que:\n\n- Ha extraido esta copia de Marathon Recompiled por completo y no solo el archivo *.exe.\n- No está ejecutando Marathon Recompiled desde un archivo *.zip." }, { ELanguage::Italian, "Impossibile trovare il modulo \"%s\".\n\nAssicurati che:\n\n- Hai estratto questa copia di Marathon Recompiled correttamente e non solo il file *.exe.\n- Non stai eseguendo Marathon Recompiled da un file *.zip." } } }, { "System_MemoryAllocationFailed", { { ELanguage::English, "Failed to allocate game memory.\n\nPlease make sure that:\n\n- You meet the minimum system requirements (8 GB).\n- Your page file is configured with at least 4-8 GB of virtual memory." }, { ELanguage::Japanese, "ゲームメモリの割り当てに失敗しました\n\n次の点を確認してください:\n\n※最小システム要件(8 GB)を満たしていること\n※ページファイルに少なくとも4~8 GBの仮想メモリが設定されていること" }, { ELanguage::German, "Fehler beim Zuweisen des Spielspeichers.\n\nBitte stelle sicher, dass:\n\n- Die Mindestanforderungen für das System erfüllt sind (8 GB).\n- Die Auslagerungsdatei mit mindestens 4-8 GB virtuellem Speicher konfiguriert ist." }, { ELanguage::French, "Échec d'allocation de la mémoire du jeu.\n\nVeuillez vous assurer que :\n\n- Vous disposez de la configuration minimale requise (8 Go).\n- Votre fichier d'échange est configuré avec au moins 4 à 8 Go de mémoire virtuelle." }, { ELanguage::Spanish, "Fallo al asignar memoria del juego.\n\nPor favor, asegúrate de que:\n\n- Cumples los requisitos mínimos del sistema (8 GB).\n- Tu archivo de páginación está configurado con al menos 4 u 8 GB de memoria virtual." }, { ELanguage::Italian, "Impossibile allocare la memoria per il gioco.\n\nAssicurati che:\n\n- Soddisfi i requisiti minimi di sistema (8 GB).\n- Il tuo file di paging sia configurato con almeno 4 o 8 GB di memoria virtuale." } } }, { "IntegrityCheck_Success", { { ELanguage::English, "Installation check has finished.\n\nAll files seem to be correct.\n\nThe game will now close. Remove the launch argument to play the game." }, { ELanguage::Japanese, "インストールチェックが終了しました\n\nすべてのファイルは正しいようです\n\nゲームは終了します、ゲームをプレイするには起動引数を削除してください" }, { ELanguage::German, "Die Installation wurde überprüft.\n\nAlle Dateien scheinen korrekt zu sein.\n\nDas Spiel wird nun geschlossen. Entferne die Startoption, um das Spiel zu spielen." }, { ELanguage::French, "La vérification de l'installation est terminée.\n\nTous les fichiers semblent corrects.\n\nL'application va maintenant se fermer. Retirez l'argument de lancement pour pouvoir lancer le jeu." }, { ELanguage::Spanish, "La verificación de la instalación ha terminado.\n\nTodos los archivos parecen correctos.\n\nEl juego se cerrará ahora. Elimina el argumento de lanzamiento para jugar al juego." }, { ELanguage::Italian, "La verifica dei file d'installazione è terminata.\n\nTutti i file sembrano corretti.\n\nIl gioco si chiuderà. Rimuovi l'argomento di avvio per poter giocare." } } }, { "IntegrityCheck_Failed", { { ELanguage::English, "Installation check has failed.\n\nError: %s\n\nThe game will now close. Try reinstalling the game by using the --install launch argument." }, { ELanguage::Japanese, "インストールチェックに失敗しました\n\nエラー:%s\n\nゲームは終了します、--install 起動引数を使用してゲームを再インストールしてください" }, { ELanguage::German, "Die Installationsprüfung ist fehlgeschlagen.\n\nFehler: %s\n\nDas Spiel wird nun geschlossen. Versuche das Spiel durch Verwendung der Startoption --install neu zu installieren." }, { ELanguage::French, "La vérification de l'installation a échoué.\n\nErreur : %s\n\nL'application va maintenant se fermer. Essayez de réinstaller le jeu en utilisant l'argument de lancement --install." }, { ELanguage::Spanish, "La verificación de la instalación ha fallado.\n\nError: %s\n\nEl juego se cerrará ahora. Intenta reinstalar el juego utilizando el argumento de lanzamiento --install." }, { ELanguage::Italian, "La verifica dei file d'installazione non è andata a buon fine.\n\nErrore: %s\n\nIl gioco si chiuderà. Prova a reinstallare il gioco utilizzando l'argomento di avvio --install." } } }, { "Common_OK", { { ELanguage::English, "OK" }, { ELanguage::Japanese, "OK" }, { ELanguage::German, "OK" }, { ELanguage::French, "OK" }, { ELanguage::Spanish, "OK" }, { ELanguage::Italian, "OK" } } }, { "Common_On", { { ELanguage::English, "On" }, { ELanguage::Japanese, "オン" }, { ELanguage::German, "An" }, { ELanguage::French, "Oui" }, { ELanguage::Spanish, "Act." }, { ELanguage::Italian, "Sì" } } }, { "Common_Off", { { ELanguage::English, "Off" }, { ELanguage::Japanese, "オフ" }, { ELanguage::German, "Aus" }, { ELanguage::French, "Non" }, { ELanguage::Spanish, "Desact." }, { ELanguage::Italian, "No" } } }, { "Common_Yes", { { ELanguage::English, "Yes" }, { ELanguage::Japanese, "はい" }, { ELanguage::German, "Ja" }, { ELanguage::French, "Oui" }, { ELanguage::Spanish, "Sí" }, { ELanguage::Italian, "Sì" } } }, { "Common_No", { { ELanguage::English, "No" }, { ELanguage::Japanese, "いいえ" }, { ELanguage::German, "Nein" }, { ELanguage::French, "Non" }, { ELanguage::Spanish, "No" }, { ELanguage::Italian, "No" } } }, { "Common_Next", { { ELanguage::English, "Next" }, { ELanguage::Japanese, "次へ" }, { ELanguage::German, "Weiter" }, { ELanguage::French, "Suivant" }, { ELanguage::Spanish, "Siguiente" }, { ELanguage::Italian, "Avanti" } } }, { "Common_Select", { { ELanguage::English, "Select" }, { ELanguage::Japanese, "決定" }, { ELanguage::German, "Auswählen" }, { ELanguage::French, "Sélectionner" }, { ELanguage::Spanish, "Seleccionar" }, { ELanguage::Italian, "Seleziona" } } }, { "Common_Back", { { ELanguage::English, "Back" }, { ELanguage::Japanese, "戻る" }, { ELanguage::German, "Zurück" }, { ELanguage::French, "Retour" }, { ELanguage::Spanish, "Atrás" }, { ELanguage::Italian, "Indietro" } } }, { "Common_Quit", { { ELanguage::English, "Quit" }, { ELanguage::Japanese, "やめる" }, { ELanguage::German, "Abbrechen" }, { ELanguage::French, "Annuler" }, { ELanguage::Spanish, "Cancelar" }, { ELanguage::Italian, "Annulla" } } }, { "Common_Cancel", { { ELanguage::English, "Cancel" }, { ELanguage::Japanese, "キャンセル" }, { ELanguage::German, "Abbrechen" }, { ELanguage::French, "Annuler" }, { ELanguage::Spanish, "Cancelar" }, { ELanguage::Italian, "Annulla" } } }, { "Common_Reset", { { ELanguage::English, "Reset" }, { ELanguage::Japanese, "リセット" }, { ELanguage::German, "Zurücksetzen" }, { ELanguage::French, "Par défaut" }, { ELanguage::Spanish, "Restablecer" }, { ELanguage::Italian, "Ripristina" } } }, { "Common_Switch", { { ELanguage::English, "Switch" }, { ELanguage::Japanese, "きりかえ" }, { ELanguage::German, "Wechseln" }, { ELanguage::French, "Changer" }, { ELanguage::Spanish, "Cambiar" }, { ELanguage::Italian, "Cambia" } } }, { "Common_Retry", { { ELanguage::English, "Retry" }, { ELanguage::Japanese, "リトライ" }, { ELanguage::German, "Wiederholen" }, { ELanguage::French, "Réessayer" }, { ELanguage::Spanish, "Reintentar" }, { ELanguage::Italian, "Riprova" } } }, { "Common_ContinueWithoutSaving", { { ELanguage::English, "Continue without saving." }, { ELanguage::Japanese, "セーブせずにゲームを続けます" }, { ELanguage::German, "Fortsetzen ohne zu speichern." }, { ELanguage::French, "Continuer sans sauvegarder." }, { ELanguage::Spanish, "Continuar sin guardar." }, { ELanguage::Italian, "Continua senza salvare." } } }, { "Button_Cancel", { { ELanguage::English, "${picture(button_b)}${locale(Common_Cancel)}" }, { ELanguage::Spanish, "${picture(button_b)} ${locale(Common_Cancel)}" } } }, { "Button_Back", { { ELanguage::English, "${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::Spanish, "${picture(button_b)} ${locale(Common_Back)}" } } }, { "Button_Select", { { ELanguage::English, "${picture(button_a)}${locale(Common_Select)}" }, { ELanguage::Spanish, "${picture(button_a)} ${locale(Common_Select)}" }, } }, { "Button_SelectQuit", { { ELanguage::English, "${picture(button_a)}${locale(Common_Select)} ${picture(button_b)}${locale(Common_Quit)}" }, { ELanguage::German, "${picture(button_a)}${locale(Common_Select)}  ${picture(button_b)}${locale(Common_Quit)}" }, { ELanguage::Spanish, "${picture(button_a)} ${locale(Common_Select)} ${picture(button_b)} ${locale(Common_Quit)}" }, } }, { "Button_SelectBack", { { ELanguage::English, "${picture(button_a)}${locale(Common_Select)} ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::German, "${picture(button_a)}${locale(Common_Select)}  ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::Spanish, "${picture(button_a)} ${locale(Common_Select)} ${picture(button_b)} ${locale(Common_Back)}" }, } }, { "Button_ResetSelectBack", { { ELanguage::English, "${picture(button_x)}${locale(Common_Reset)} ${picture(button_a)}${locale(Common_Select)} ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::German, "${picture(button_x)}${locale(Common_Reset)}  ${picture(button_a)}${locale(Common_Select)}  ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::Spanish, "${picture(button_x)} ${locale(Common_Reset)} ${picture(button_a)} ${locale(Common_Select)} ${picture(button_b)} ${locale(Common_Back)}" }, } }, { "Button_GoldMedalsBack", { { ELanguage::English, "${picture(button_y)}${locale(Achievements_GoldMedals)} ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::German, "${picture(button_y)}${locale(Achievements_GoldMedals)}  ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::Spanish, "${picture(button_y)} ${locale(Achievements_GoldMedals)} ${picture(button_b)} ${locale(Common_Back)}" } } }, { "Button_AchievementsBack", { { ELanguage::English, "${picture(button_y)}${locale(Achievements_Title)} ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::German, "${picture(button_y)}${locale(Achievements_Title)}  ${picture(button_b)}${locale(Common_Back)}" }, { ELanguage::Spanish, "${picture(button_y)} ${locale(Achievements_Title)} ${picture(button_b)} ${locale(Common_Back)}" } } } }; std::string& Localise(const std::string_view& key) { auto localeFindResult = g_locale.find(key); if (localeFindResult != g_locale.end()) { auto languageFindResult = localeFindResult->second.find(Config::Language); if (languageFindResult == localeFindResult->second.end()) languageFindResult = localeFindResult->second.find(ELanguage::English); if (languageFindResult != localeFindResult->second.end()) return languageFindResult->second; } return g_localeMissing; } ================================================ FILE: MarathonRecomp/locale/locale.h ================================================ #pragma once enum class ELanguage : uint32_t { Unknown, English, Japanese, German, French, Spanish, Italian }; inline std::string g_localeMissing = ""; extern std::unordered_map> g_locale; std::string& Localise(const std::string_view& key); ================================================ FILE: MarathonRecomp/main.cpp ================================================ #include #include #ifdef __x86_64__ #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 #ifdef _WIN32 #include #endif #if defined(_WIN32) && defined(MARATHON_RECOMP_D3D12) static std::array g_D3D12RequiredModules = { "D3D12/D3D12Core.dll", "dxcompiler.dll", "dxil.dll" }; #endif const size_t XMAIOBegin = 0x7FEA0000; const size_t XMAIOEnd = XMAIOBegin + 0x0000FFFF; Memory g_memory; Heap g_userHeap; XDBFWrapper g_xdbfWrapper; std::unordered_map g_xdbfTextureCache; void HostStartup() { #ifdef _WIN32 CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif hid::Init(); } // Name inspired from nt's entry point void KiSystemStartup() { if (g_memory.base == nullptr) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("System_MemoryAllocationFailed").c_str(), GameWindow::s_pWindow); std::_Exit(1); } g_userHeap.Init(); const auto gameContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Game"); const std::string gamePath = (const char*)(GetGamePath() / "game").u8string().c_str(); BuildPathCache(gamePath); XamRegisterContent(gameContent, gamePath); const auto saveFilePath = GetSaveFilePath(true); bool saveFileExists = std::filesystem::exists(saveFilePath); if (!saveFileExists) { // Copy base save data to modded save as fallback. std::error_code ec; std::filesystem::create_directories(saveFilePath.parent_path(), ec); if (!ec) { std::filesystem::copy_file(GetSaveFilePath(false), saveFilePath, ec); saveFileExists = !ec; } } if (saveFileExists) { std::u8string savePathU8 = saveFilePath.parent_path().u8string(); XamRegisterContent(XamMakeContent(XCONTENTTYPE_SAVEDATA, "SonicNextSaveData.bin"), (const char*)(savePathU8.c_str())); } // Mount game XamContentCreateEx(0, "game", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr); // OS mounts game data to D: XamContentCreateEx(0, "D", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr); std::error_code ec; for (auto& file : std::filesystem::directory_iterator(GetGamePath() / "dlc", ec)) { if (file.is_directory()) { std::u8string fileNameU8 = file.path().filename().u8string(); std::u8string filePathU8 = file.path().u8string(); XamRegisterContent(XamMakeContent(XCONTENTTYPE_DLC, (const char*)(fileNameU8.c_str())), (const char*)(filePathU8.c_str())); } } XAudioInitializeSystem(); } uint32_t LdrLoadModule(const std::filesystem::path &path) { const auto loadResult = LoadFile(path); if (loadResult.empty()) { assert("Failed to load module" && false); return 0; } const auto image = Image::ParseImage(loadResult.data(), loadResult.size()); memcpy(g_memory.Translate(image.base), image.data.get(), image.size); g_xdbfWrapper = XDBFWrapper(static_cast(g_memory.Translate(image.resource_offset)), image.resource_size); return image.entry_point; } #ifdef __x86_64__ __attribute__((constructor(101), target("no-avx,no-avx2"), noinline)) void init() { uint32_t eax, ebx, ecx, edx; // Execute CPUID for processor info and feature bits. __get_cpuid(1, &eax, &ebx, &ecx, &edx); // Check for AVX support. if ((ecx & (1 << 28)) == 0) { printf("[*] CPU does not support the AVX instruction set.\n"); #ifdef _WIN32 MessageBoxA(nullptr, "Your CPU does not meet the minimum system requirements.", "Marathon Recompiled", MB_ICONERROR); #endif std::_Exit(1); } } #endif int main(int argc, char *argv[]) { #ifdef _WIN32 timeBeginPeriod(1); #endif os::process::CheckConsole(); if (!os::registry::Init()) LOGN_WARNING("OS does not support registry."); os::logger::Init(); PreloadContext preloadContext; preloadContext.PreloadExecutable(); bool forceInstaller = false; bool forceDLCInstaller = false; bool useDefaultWorkingDirectory = false; bool forceInstallationCheck = false; bool graphicsApiRetry = false; const char *sdlVideoDriver = nullptr; for (uint32_t i = 1; i < argc; i++) { forceInstaller = forceInstaller || (strcmp(argv[i], "--install") == 0); forceDLCInstaller = forceDLCInstaller || (strcmp(argv[i], "--install-dlc") == 0); useDefaultWorkingDirectory = useDefaultWorkingDirectory || (strcmp(argv[i], "--use-cwd") == 0); forceInstallationCheck = forceInstallationCheck || (strcmp(argv[i], "--install-check") == 0); graphicsApiRetry = graphicsApiRetry || (strcmp(argv[i], "--graphics-api-retry") == 0); App::s_isSkipLogos = App::s_isSkipLogos || (strcmp(argv[i], "--skip-logos") == 0); if (strcmp(argv[i], "--sdl-video-driver") == 0) { if ((i + 1) < argc) sdlVideoDriver = argv[++i]; else LOGN_WARNING("No argument was specified for --sdl-video-driver. Option will be ignored."); } } if (!useDefaultWorkingDirectory) { // Set the current working directory to the executable's path. std::error_code ec; std::filesystem::current_path(os::process::GetExecutablePath().parent_path(), ec); } Config::Load(); if (forceInstallationCheck) { // Create the console to show progress to the user, otherwise it will seem as if the game didn't boot at all. os::process::ShowConsole(); Journal journal; double lastProgressMiB = 0.0; double lastTotalMib = 0.0; Installer::checkInstallIntegrity(GAME_INSTALL_DIRECTORY, journal, [&]() { constexpr double MiBDivisor = 1024.0 * 1024.0; constexpr double MiBProgressThreshold = 128.0; double progressMiB = double(journal.progressCounter) / MiBDivisor; double totalMiB = double(journal.progressTotal) / MiBDivisor; if (journal.progressCounter > 0) { if ((progressMiB - lastProgressMiB) > MiBProgressThreshold) { fprintf(stdout, "Checking files: %0.2f MiB / %0.2f MiB\n", progressMiB, totalMiB); lastProgressMiB = progressMiB; } } else { if ((totalMiB - lastTotalMib) > MiBProgressThreshold) { fprintf(stdout, "Scanning files: %0.2f MiB\n", totalMiB); lastTotalMib = totalMiB; } } return true; }); char resultText[512]; uint32_t messageBoxStyle; if (journal.lastResult == Journal::Result::Success) { snprintf(resultText, sizeof(resultText), "%s", Localise("IntegrityCheck_Success").c_str()); fprintf(stdout, "%s\n", resultText); messageBoxStyle = SDL_MESSAGEBOX_INFORMATION; } else { snprintf(resultText, sizeof(resultText), Localise("IntegrityCheck_Failed").c_str(), journal.lastErrorMessage.c_str()); fprintf(stderr, "%s\n", resultText); messageBoxStyle = SDL_MESSAGEBOX_ERROR; } SDL_ShowSimpleMessageBox(messageBoxStyle, GameWindow::GetTitle(), resultText, GameWindow::s_pWindow); std::_Exit(int(journal.lastResult)); } #if defined(_WIN32) && defined(MARATHON_RECOMP_D3D12) for (auto& dll : g_D3D12RequiredModules) { if (!std::filesystem::exists(g_executableRoot / dll)) { char text[512]; snprintf(text, sizeof(text), Localise("System_Win32_MissingDLLs").c_str(), dll.data()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), text, GameWindow::s_pWindow); std::_Exit(1); } } #endif // Check the time since the last time an update was checked. Store the new time if the difference is more than six hours. constexpr double TimeBetweenUpdateChecksInSeconds = 6 * 60 * 60; time_t timeNow = std::time(nullptr); double timeDifferenceSeconds = difftime(timeNow, Config::LastChecked); if (timeDifferenceSeconds > TimeBetweenUpdateChecksInSeconds) { UpdateChecker::initialize(); UpdateChecker::start(); Config::LastChecked = timeNow; Config::Save(); } if (Config::ShowConsole) os::process::ShowConsole(); LOGN_WARNING("Host Startup"); HostStartup(); std::filesystem::path modulePath; bool isGameInstalled = Installer::checkGameInstall(GetGamePath(), modulePath); bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; if (runInstallerWizard) { if (!Video::CreateHostDevice(sdlVideoDriver, graphicsApiRetry)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow); std::_Exit(1); } if (!InstallerWizard::Run(GetGamePath(), isGameInstalled && forceDLCInstaller)) std::_Exit(0); } // ModLoader::Init(); KiSystemStartup(); uint32_t entry = LdrLoadModule(modulePath); if (!runInstallerWizard) { if (!Video::CreateHostDevice(sdlVideoDriver, graphicsApiRetry)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow); std::_Exit(1); } } LOGN_WARNING("Start Guest Thread"); LOGN_WARNING(modulePath.string()); // Video::StartPipelinePrecompilation(); GuestThread::Start({ entry, 0, 0 }); return 0; } GUEST_FUNCTION_STUB(__imp__vsprintf); GUEST_FUNCTION_STUB(__imp___vsnprintf); GUEST_FUNCTION_STUB(__imp__sprintf); GUEST_FUNCTION_STUB(__imp___snprintf); GUEST_FUNCTION_STUB(__imp___snwprintf); GUEST_FUNCTION_STUB(__imp__vswprintf); GUEST_FUNCTION_STUB(__imp___vscwprintf); GUEST_FUNCTION_STUB(__imp__swprintf); ================================================ FILE: MarathonRecomp/misc_impl.cpp ================================================ #include "stdafx.h" #include #include uint32_t QueryPerformanceCounterImpl(LARGE_INTEGER* lpPerformanceCount) { lpPerformanceCount->QuadPart = ByteSwap(std::chrono::steady_clock::now().time_since_epoch().count()); return TRUE; } uint32_t QueryPerformanceFrequencyImpl(LARGE_INTEGER* lpFrequency) { constexpr auto Frequency = std::chrono::steady_clock::period::den / std::chrono::steady_clock::period::num; lpFrequency->QuadPart = ByteSwap(Frequency); return TRUE; } uint32_t GetTickCountImpl() { return uint32_t(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); } void GlobalMemoryStatusImpl(XLPMEMORYSTATUS lpMemoryStatus) { lpMemoryStatus->dwLength = sizeof(XMEMORYSTATUS); lpMemoryStatus->dwMemoryLoad = 0; lpMemoryStatus->dwTotalPhys = 0x20000000; lpMemoryStatus->dwAvailPhys = 0x20000000; lpMemoryStatus->dwTotalPageFile = 0x20000000; lpMemoryStatus->dwAvailPageFile = 0x20000000; lpMemoryStatus->dwTotalVirtual = 0x20000000; lpMemoryStatus->dwAvailVirtual = 0x20000000; } #ifndef _WIN32 int memcpy_s(void* dest, size_t dest_size, const void* src, size_t count) { if (dest == nullptr || src == nullptr) { return EINVAL; } if (dest_size < count) { return ERANGE; } memcpy(dest, src, count); return 0; } #endif GUEST_FUNCTION_HOOK(sub_826DF680, memcpy); // GUEST_FUNCTION_HOOK(sub_831CCB98, memcpy); // GUEST_FUNCTION_HOOK(sub_831CEAE0, memcpy); // GUEST_FUNCTION_HOOK(sub_831CEE04, memcpy); // GUEST_FUNCTION_HOOK(sub_831CF2D0, memcpy); // GUEST_FUNCTION_HOOK(sub_831CF660, memcpy); // GUEST_FUNCTION_HOOK(sub_826DFAA0, memcpy); GUEST_FUNCTION_HOOK(sub_826DE940, memmove); GUEST_FUNCTION_HOOK(sub_826DFD40, memset); GUEST_FUNCTION_HOOK(sub_826DEA00, memcpy_s); // GUEST_FUNCTION_HOOK(sub_831CCAA0, memset); #ifdef _WIN32 GUEST_FUNCTION_HOOK(sub_82537770, OutputDebugStringA); #else GUEST_FUNCTION_STUB(sub_82537770); #endif GUEST_FUNCTION_HOOK(sub_826FCE58, QueryPerformanceCounterImpl); // replaced GUEST_FUNCTION_HOOK(sub_826FC3C8, QueryPerformanceFrequencyImpl); // repalced GUEST_FUNCTION_HOOK(sub_826FD790, GetTickCountImpl); // replaced // GUEST_FUNCTION_HOOK(sub_82BD4BC0, GlobalMemoryStatusImpl); // sprintf // PPC_FUNC(sub_82BD4AE8) // { // sub_831B1630(ctx, base); // } ================================================ FILE: MarathonRecomp/mod/ini_file.h ================================================ #pragma once #include class IniFile { protected: struct Property { std::string name; std::string value; }; struct Section { std::string name; xxHashMap properties; }; xxHashMap
m_sections; static size_t hashStr(const std::string_view& str); static bool isWhitespace(char value); static bool isNewLine(char value); public: bool read(const std::filesystem::path& filePath); std::string getString(const std::string_view& sectionName, const std::string_view& propertyName, std::string defaultValue) const; bool getBool(const std::string_view& sectionName, const std::string_view& propertyName, bool defaultValue) const; template T get(const std::string_view& sectionName, const std::string_view& propertyName, T defaultValue) const; template void enumerate(const T& function) const; template void enumerate(const std::string_view& sectionName, const T& function) const; bool contains(const std::string_view& sectionName) const; }; #include "ini_file.inl" ================================================ FILE: MarathonRecomp/mod/ini_file.inl ================================================ inline size_t IniFile::hashStr(const std::string_view& str) { return XXH3_64bits(str.data(), str.size()); } inline bool IniFile::isWhitespace(char value) { return value == ' ' || value == '\t'; } inline bool IniFile::isNewLine(char value) { return value == '\n' || value == '\r'; } inline bool IniFile::read(const std::filesystem::path& filePath) { std::ifstream file(filePath, std::ios::binary); if (!file.good()) return false; file.seekg(0, std::ios::end); const size_t dataSize = static_cast(file.tellg()); const auto data = std::make_unique(dataSize + 1); data[dataSize] = '\0'; file.seekg(0, std::ios::beg); file.read(data.get(), dataSize); file.close(); Section* section = nullptr; const char* dataPtr = data.get(); while (dataPtr < data.get() + dataSize) { if (*dataPtr == ';') { while (*dataPtr != '\0' && !isNewLine(*dataPtr)) ++dataPtr; } else if (*dataPtr == '[') { ++dataPtr; const char* endPtr = dataPtr; while (*endPtr != '\0' && !isNewLine(*endPtr) && *endPtr != ']') ++endPtr; if (*endPtr != ']') return false; std::string sectionName(dataPtr, endPtr - dataPtr); section = &m_sections[hashStr(sectionName)]; section->name = std::move(sectionName); dataPtr = endPtr + 1; } else if (!isWhitespace(*dataPtr) && !isNewLine(*dataPtr)) { if (section == nullptr) return false; const char* endPtr; if (*dataPtr == '"') { ++dataPtr; endPtr = dataPtr; while (*endPtr != '\0' && !isNewLine(*endPtr) && *endPtr != '"') ++endPtr; if (*endPtr != '"') return false; } else { endPtr = dataPtr; while (*endPtr != '\0' && !isNewLine(*endPtr) && !isWhitespace(*endPtr) && *endPtr != '=') ++endPtr; if (!isNewLine(*endPtr) && !isWhitespace(*endPtr) && *endPtr != '=') return false; } std::string propertyName(dataPtr, endPtr - dataPtr); auto& property = section->properties[hashStr(propertyName)]; property.name = std::move(propertyName); dataPtr = endPtr; while (*dataPtr != '\0' && !isNewLine(*dataPtr) && *dataPtr != '=') ++dataPtr; if (*dataPtr == '=') { ++dataPtr; while (*dataPtr != '\0' && isWhitespace(*dataPtr)) ++dataPtr; if (*dataPtr == '"') { ++dataPtr; endPtr = dataPtr; while (*endPtr != '\0' && !isNewLine(*endPtr) && *endPtr != '"') ++endPtr; if (*endPtr != '"') return false; } else { endPtr = dataPtr; while (*endPtr != '\0' && !isNewLine(*endPtr) && !isWhitespace(*endPtr)) ++endPtr; } property.value = std::string(dataPtr, endPtr - dataPtr); dataPtr = endPtr + 1; } } else { ++dataPtr; } } return true; } inline std::string IniFile::getString(const std::string_view& sectionName, const std::string_view& propertyName, std::string defaultValue) const { const auto sectionPair = m_sections.find(hashStr(sectionName)); if (sectionPair != m_sections.end()) { const auto propertyPair = sectionPair->second.properties.find(hashStr(propertyName)); if (propertyPair != sectionPair->second.properties.end()) return propertyPair->second.value; } return defaultValue; } inline bool IniFile::getBool(const std::string_view& sectionName, const std::string_view& propertyName, bool defaultValue) const { const auto sectionPair = m_sections.find(hashStr(sectionName)); if (sectionPair != m_sections.end()) { const auto propertyPair = sectionPair->second.properties.find(hashStr(propertyName)); if (propertyPair != sectionPair->second.properties.end() && !propertyPair->second.value.empty()) { const char firstChar = propertyPair->second.value[0]; return firstChar == 't' || firstChar == 'T' || firstChar == 'y' || firstChar == 'Y' || firstChar == '1'; } } return defaultValue; } inline bool IniFile::contains(const std::string_view& sectionName) const { return m_sections.contains(hashStr(sectionName)); } template T IniFile::get(const std::string_view& sectionName, const std::string_view& propertyName, T defaultValue) const { const auto sectionPair = m_sections.find(hashStr(sectionName)); if (sectionPair != m_sections.end()) { const auto propertyPair = sectionPair->second.properties.find(hashStr(propertyName)); if (propertyPair != sectionPair->second.properties.end()) { T value{}; const auto result = std::from_chars(propertyPair->second.value.data(), propertyPair->second.value.data() + propertyPair->second.value.size(), value); if (result.ec == std::errc{}) return value; } } return defaultValue; } template inline void IniFile::enumerate(const T& function) const { for (const auto& [_, section] : m_sections) { for (auto& property : section.properties) function(section.name, property.second.name, property.second.value); } } template void IniFile::enumerate(const std::string_view& sectionName, const T& function) const { const auto sectionPair = m_sections.find(hashStr(sectionName)); if (sectionPair != m_sections.end()) { for (const auto& property : sectionPair->second.properties) function(property.second.name, property.second.value); } } ================================================ FILE: MarathonRecomp/mod/mod_loader.cpp ================================================ #include "mod_loader.h" #include "ini_file.h" #include #include #include #include #include #include #include #include enum class ModType { HMM, UMM }; struct Mod { ModType type{}; std::vector includeDirs; bool merge = false; ankerl::unordered_dense::set readOnly; }; static std::vector g_mods; std::filesystem::path ModLoader::ResolvePath(std::string_view path) { std::string_view root; size_t sepIndex = path.find(":\\"); if (sepIndex != std::string_view::npos) { root = path.substr(0, sepIndex); path.remove_prefix(sepIndex + 2); } if (root == "save") { if (!ModLoader::s_saveFilePath.empty()) { if (path == "SYS-DATA") return ModLoader::s_saveFilePath; else return ModLoader::s_saveFilePath.parent_path() / path; } return {}; } if (g_mods.empty()) return {}; thread_local xxHashMap s_cache; XXH64_hash_t hash = XXH3_64bits(path.data(), path.size()); auto findResult = s_cache.find(hash); if (findResult != s_cache.end()) return findResult->second; std::string pathStr(path); std::replace(pathStr.begin(), pathStr.end(), '\\', '/'); std::filesystem::path fsPath(std::move(pathStr)); bool canBeMerged = path.find(".arl") == (path.size() - 4) || path.find(".ar.") == (path.size() - 6) || path.find(".ar") == (path.size() - 3); for (auto& mod : g_mods) { if (mod.type == ModType::UMM && mod.merge && canBeMerged && !mod.readOnly.contains(fsPath)) continue; for (auto& includeDir : mod.includeDirs) { std::filesystem::path modPath = includeDir / fsPath; if (std::filesystem::exists(modPath)) return s_cache.emplace(hash, modPath).first->second; } } return s_cache.emplace(hash, std::filesystem::path{}).first->second; } std::vector* ModLoader::GetIncludeDirectories(size_t modIndex) { return modIndex < g_mods.size() ? &g_mods[modIndex].includeDirs : nullptr; } void ModLoader::Init() { const std::filesystem::path& userPath = GetUserPath(); IniFile configIni; if (!configIni.read(userPath / "cpkredir.ini")) { configIni = {}; if (!configIni.read(GetGamePath() / "cpkredir.ini")) return; } if (!configIni.getBool("CPKREDIR", "Enabled", true)) return; if (configIni.getBool("CPKREDIR", "EnableSaveFileRedirection", false)) { std::string saveFilePathU8 = configIni.getString("CPKREDIR", "SaveFileFallback", ""); if (!saveFilePathU8.empty()) ModLoader::s_saveFilePath = std::u8string_view((const char8_t*)saveFilePathU8.c_str()); else ModLoader::s_saveFilePath = userPath / "mlsave"; ModLoader::s_saveFilePath /= "SYS-DATA"; } if (configIni.getString("CPKREDIR", "LogType", std::string()) == "console") { os::process::ShowConsole(); s_isLogTypeConsole = true; } std::string modsDbIniFilePathU8 = configIni.getString("CPKREDIR", "ModsDbIni", ""); if (modsDbIniFilePathU8.empty()) return; IniFile modsDbIni; if (!modsDbIni.read(std::u8string_view((const char8_t*)modsDbIniFilePathU8.c_str()))) return; bool foundModSaveFilePath = false; size_t activeModCount = modsDbIni.get("Main", "ActiveModCount", 0); for (size_t i = 0; i < activeModCount; ++i) { std::string modId = modsDbIni.getString("Main", fmt::format("ActiveMod{}", i), ""); if (modId.empty()) continue; std::string modIniFilePathU8 = modsDbIni.getString("Mods", modId, ""); if (modIniFilePathU8.empty()) continue; std::filesystem::path modIniFilePath(std::u8string_view((const char8_t*)modIniFilePathU8.c_str())); IniFile modIni; if (!modIni.read(modIniFilePath)) continue; auto modDirectoryPath = modIniFilePath.parent_path(); std::string modSaveFilePathU8; Mod mod; if (modIni.contains("Details") || modIni.contains("Filesystem")) // UMM { mod.type = ModType::UMM; mod.includeDirs.emplace_back(modDirectoryPath); mod.merge = modIni.getBool("Details", "Merge", modIni.getBool("Filesystem", "Merge", false)); std::string readOnly = modIni.getString("Details", "Read-only", modIni.getString("Filesystem", "Read-only", std::string())); std::replace(readOnly.begin(), readOnly.end(), '\\', '/'); std::string_view readOnlySplit = readOnly; while (!readOnlySplit.empty()) { size_t index = readOnlySplit.find(','); if (index == std::string_view::npos) { mod.readOnly.emplace(readOnlySplit); break; } mod.readOnly.emplace(readOnlySplit.substr(0, index)); readOnlySplit.remove_prefix(index + 1); } if (!foundModSaveFilePath) modSaveFilePathU8 = modIni.getString("Details", "Save", modIni.getString("Filesystem", "Save", std::string())); } else // HMM { mod.type = ModType::HMM; size_t includeDirCount = modIni.get("Main", "IncludeDirCount", 0); for (size_t j = 0; j < includeDirCount; j++) { std::string includeDirU8 = modIni.getString("Main", fmt::format("IncludeDir{}", j), ""); if (!includeDirU8.empty()) { std::replace(includeDirU8.begin(), includeDirU8.end(), '\\', '/'); mod.includeDirs.emplace_back(modDirectoryPath / std::u8string_view((const char8_t*)includeDirU8.c_str())); } } if (!foundModSaveFilePath) modSaveFilePathU8 = modIni.getString("Main", "SaveFile", std::string()); } if (!modSaveFilePathU8.empty()) { std::replace(modSaveFilePathU8.begin(), modSaveFilePathU8.end(), '\\', '/'); ModLoader::s_saveFilePath = modDirectoryPath / std::u8string_view((const char8_t*)modSaveFilePathU8.c_str()); // Save file paths in HMM mods are treated as folders. if (mod.type == ModType::HMM) ModLoader::s_saveFilePath /= "SYS-DATA"; foundModSaveFilePath = true; } if (!mod.includeDirs.empty()) g_mods.emplace_back(std::move(mod)); } auto codeCount = modsDbIni.get("Codes", "CodeCount", 0); if (codeCount) { std::vector codes{}; for (size_t i = 0; i < codeCount; i++) { auto name = modsDbIni.getString("Codes", fmt::format("Code{}", i), ""); if (name.empty()) continue; codes.push_back(name); } for (auto& def : g_configDefinitions) { if (!def->IsHidden() || def->GetSection() != "Codes") continue; /* NOTE: this is inefficient, but it happens once on boot for a handful of codes at release and is temporary until we support real code mods. */ for (size_t i = 0; i < codes.size(); i++) { if (def->GetName() == codes[i]) { LOGF_IMPL(Utility, "Mod Loader", "Loading code: \"{}\"", codes[i]); *(bool*)def->GetValue() = true; break; } } } } } static constexpr uint32_t LZX_SIGNATURE = 0xFF512EE; static std::span decompressLzx(PPCContext& ctx, uint8_t* base, const uint8_t* compressedData, size_t compressedDataSize, be* scratchSpace) { assert(g_memory.IsInMemoryRange(compressedData)); bool shouldFreeScratchSpace = false; if (scratchSpace == nullptr) { scratchSpace = reinterpret_cast*>(g_userHeap.Alloc(sizeof(uint32_t) * 2)); shouldFreeScratchSpace = true; } // Initialize decompressor ctx.r3.u32 = 1; ctx.r4.u32 = uint32_t((compressedData + 0xC) - base); ctx.r5.u32 = *reinterpret_cast*>(compressedData + 0x8); ctx.r6.u32 = uint32_t(reinterpret_cast(scratchSpace) - base); // sub_831CE1A0(ctx, base); uint64_t decompressedDataSize = *reinterpret_cast*>(compressedData + 0x18); uint8_t* decompressedData = reinterpret_cast(g_userHeap.Alloc(decompressedDataSize)); uint32_t blockSize = *reinterpret_cast*>(compressedData + 0x28); size_t decompressedDataOffset = 0; size_t compressedDataOffset = 0x30; while (decompressedDataOffset < decompressedDataSize) { size_t decompressedBlockSize = decompressedDataSize - decompressedDataOffset; if (decompressedBlockSize > blockSize) decompressedBlockSize = blockSize; *(scratchSpace + 1) = decompressedBlockSize; uint32_t compressedBlockSize = *reinterpret_cast*>(compressedData + compressedDataOffset); // Decompress ctx.r3.u32 = *scratchSpace; ctx.r4.u32 = uint32_t((decompressedData + decompressedDataOffset) - base); ctx.r5.u32 = uint32_t(reinterpret_cast(scratchSpace + 1) - base); ctx.r6.u32 = uint32_t((compressedData + compressedDataOffset + 0x4) - base); ctx.r7.u32 = compressedBlockSize; // sub_831CE0D0(ctx, base); decompressedDataOffset += *(scratchSpace + 1); compressedDataOffset += 0x4 + compressedBlockSize; } // Deinitialize decompressor ctx.r3.u32 = *scratchSpace; // sub_831CE150(ctx, base); if (shouldFreeScratchSpace) g_userHeap.Free(scratchSpace); return { decompressedData, decompressedDataSize }; } // Hedgehog::Database::CDatabaseLoader::ReadArchiveList // PPC_FUNC_IMPL(__imp__sub_82E0D3E8); // PPC_FUNC(sub_82E0D3E8) // { // if (g_mods.empty()) // { // __imp__sub_82E0D3E8(ctx, base); // return; // } // thread_local ankerl::unordered_dense::set s_fileNames; // s_fileNames.clear(); // auto parseArlFileData = [&](const uint8_t* arlFileData, size_t arlFileSize) // { // struct ArlHeader // { // uint32_t signature; // uint32_t splitCount; // }; // auto* arlHeader = reinterpret_cast(arlFileData); // size_t arlHeaderSize = sizeof(ArlHeader) + arlHeader->splitCount * sizeof(uint32_t); // const uint8_t* arlFileNames = arlFileData + arlHeaderSize; // while (arlFileNames < arlFileData + arlFileSize) // { // uint8_t fileNameSize = *arlFileNames; // ++arlFileNames; // s_fileNames.emplace(reinterpret_cast(arlFileNames), fileNameSize); // arlFileNames += fileNameSize; // } // return arlHeaderSize; // }; // auto parseArFileData = [&](const uint8_t* arFileData, size_t arFileSize) // { // struct ArEntry // { // uint32_t entrySize; // uint32_t dataSize; // uint32_t dataOffset; // uint32_t fileDateLow; // uint32_t fileDateHigh; // }; // for (size_t i = 16; i < arFileSize; ) // { // auto entry = reinterpret_cast(arFileData + i); // s_fileNames.emplace(reinterpret_cast(entry + 1)); // i += entry->entrySize; // } // }; // auto r3 = ctx.r3; // auto r4 = ctx.r4; // auto r5 = ctx.r5; // auto r6 = ctx.r6; // auto loadFile = [&](const std::filesystem::path& filePath, const TFunction& function) // { // std::ifstream stream(filePath, std::ios::binary); // if (stream.good()) // { // if (ModLoader::s_isLogTypeConsole) // LOGF_IMPL(Utility, "Mod Loader", "Loading file: \"{}\"", reinterpret_cast(filePath.u8string().c_str())); // be signature{}; // stream.read(reinterpret_cast(&signature), sizeof(signature)); // stream.seekg(0, std::ios::end); // size_t arlFileSize = stream.tellg(); // stream.seekg(0, std::ios::beg); // if (signature == LZX_SIGNATURE) // { // void* compressedFileData = g_userHeap.Alloc(arlFileSize); // stream.read(reinterpret_cast(compressedFileData), arlFileSize); // stream.close(); // auto fileData = decompressLzx(ctx, base, reinterpret_cast(compressedFileData), arlFileSize, nullptr); // g_userHeap.Free(compressedFileData); // function(fileData.data(), fileData.size()); // g_userHeap.Free(fileData.data()); // } // else // { // thread_local std::vector s_fileData; // s_fileData.resize(arlFileSize); // stream.read(reinterpret_cast(s_fileData.data()), arlFileSize); // stream.close(); // function(s_fileData.data(), arlFileSize); // } // return true; // } // return false; // }; // thread_local xxHashMap>> s_cache; // std::u8string_view arlFilePathU8(reinterpret_cast(base + PPC_LOAD_U32(ctx.r4.u32))); // XXH64_hash_t hash = XXH3_64bits(arlFilePathU8.data(), arlFilePathU8.size()); // auto findResult = s_cache.find(hash); // if (findResult != s_cache.end()) // { // for (const auto& [arlFilePath, isArchiveList] : findResult->second) // { // if (isArchiveList) // loadFile(arlFilePath, parseArlFileData); // else // loadFile(arlFilePath, parseArFileData); // } // } // else // { // std::vector> arlFilePaths; // std::filesystem::path arlFilePath; // std::filesystem::path arFilePath; // std::filesystem::path appendArlFilePath; // for (auto& mod : g_mods) // { // for (auto& includeDir : mod.includeDirs) // { // auto loadUncachedFile = [&](const std::filesystem::path& filePath, bool isArchiveList) // { // if (mod.type == ModType::UMM && mod.readOnly.contains(filePath)) // return false; // std::filesystem::path combinedFilePath = includeDir / filePath; // bool success; // if (isArchiveList) // success = loadFile(combinedFilePath, parseArlFileData); // else // success = loadFile(combinedFilePath, parseArFileData); // if (success) // arlFilePaths.emplace_back(std::move(combinedFilePath), isArchiveList); // return success; // }; // if (mod.type == ModType::UMM) // { // if (mod.merge) // { // if (arlFilePath.empty()) // { // arlFilePath = arlFilePathU8; // arlFilePath += ".arl"; // } // if (!loadUncachedFile(arlFilePath, true)) // { // if (arFilePath.empty()) // { // arFilePath = arlFilePathU8; // arFilePath += ".ar"; // } // if (!loadUncachedFile(arFilePath, false)) // { // thread_local std::filesystem::path s_tempPath; // for (uint32_t i = 0; ; i++) // { // s_tempPath = arFilePath; // s_tempPath += fmt::format(".{:02}", i); // if (!loadUncachedFile(s_tempPath, false)) // break; // } // } // } // } // } // else if (mod.type == ModType::HMM) // { // if (appendArlFilePath.empty()) // { // if (arlFilePath.empty()) // { // arlFilePath = arlFilePathU8; // arlFilePath += ".arl"; // } // appendArlFilePath = arlFilePath.parent_path(); // appendArlFilePath /= "+"; // appendArlFilePath += arlFilePath.filename(); // } // loadUncachedFile(appendArlFilePath, true); // } // } // } // s_cache.emplace(hash, std::move(arlFilePaths)); // } // ctx.r3 = r3; // ctx.r4 = r4; // ctx.r5 = r5; // ctx.r6 = r6; // if (s_fileNames.empty()) // { // __imp__sub_82E0D3E8(ctx, base); // return; // } // size_t arlHeaderSize = parseArlFileData(base + ctx.r5.u32, ctx.r6.u32); // size_t arlFileSize = arlHeaderSize; // for (auto& fileName : s_fileNames) // { // arlFileSize += 1; // arlFileSize += fileName.size(); // } // uint8_t* newArlFileData = reinterpret_cast(g_userHeap.Alloc(arlFileSize)); // memcpy(newArlFileData, base + ctx.r5.u32, arlHeaderSize); // uint8_t* arlFileNames = newArlFileData + arlHeaderSize; // for (auto& fileName : s_fileNames) // { // *arlFileNames = uint8_t(fileName.size()); // ++arlFileNames; // memcpy(arlFileNames, fileName.data(), fileName.size()); // arlFileNames += fileName.size(); // } // ctx.r5.u32 = uint32_t(newArlFileData - base); // ctx.r6.u32 = uint32_t(arlFileSize); // __imp__sub_82E0D3E8(ctx, base); // g_userHeap.Free(newArlFileData); // } // Load elements have an unused "pretty name" field. We will use this field to store the archive file path, // prefixed with a magic string. When the first load detects this string, it will load append archives // and then clear the field to prevent remaining splits from loading the append archives again. // We cannot rely on .ar.00 being the first split to be loaded, so this approach is necessary. static thread_local uint32_t g_prefixedArFilePath = NULL; // Hedgehog::Database::CDatabaseLoader::LoadArchives // PPC_FUNC_IMPL(__imp__sub_82E0CC38); // PPC_FUNC(sub_82E0CC38) // { // if (g_mods.empty()) // { // __imp__sub_82E0CC38(ctx, base); // return; // } // auto r3 = ctx.r3; // auto r4 = ctx.r4; // auto r5 = ctx.r5; // auto r6 = ctx.r6; // auto r7 = ctx.r7; // auto r8 = ctx.r8; // const char* arFilePath = reinterpret_cast(base + PPC_LOAD_U32(r5.u32)); // // __HH_ALLOC // ctx.r3.u32 = 22 + strlen(arFilePath); // sub_822C0988(ctx, base); // char* prefixedArFilePath = reinterpret_cast(base + ctx.r3.u32); // *reinterpret_cast*>(prefixedArFilePath) = 1; // strcpy(prefixedArFilePath + 0x4, "/UnleashedRecomp/"); // strcpy(prefixedArFilePath + 0x15, arFilePath); // ctx.r1.u32 -= 0x10; // uint32_t stackSpace = ctx.r1.u32; // PPC_STORE_U32(stackSpace, static_cast(reinterpret_cast(prefixedArFilePath) - base) + 0x4); // g_prefixedArFilePath = stackSpace; // ctx.r3 = r3; // ctx.r4 = r4; // ctx.r5 = r5; // ctx.r6 = r6; // ctx.r7 = r7; // ctx.r8 = r8; // __imp__sub_82E0CC38(ctx, base); // // Hedgehog::Base::CSharedString::~CSharedString // ctx.r3.u32 = stackSpace; // sub_82DFB148(ctx, base); // g_prefixedArFilePath = NULL; // ctx.r1.u32 += 0x10; // } // Hedgehog::Database::SLoadElement::SLoadElement // PPC_FUNC_IMPL(__imp__sub_82E140D8); // PPC_FUNC(sub_82E140D8) // { // // Store the prefixed archive file path as the pretty name. It's unused for archives we want to append to. // if (!g_mods.empty() && PPC_LOAD_U32(ctx.r5.u32) == 0x8200A621 && g_prefixedArFilePath != NULL) // ctx.r5.u32 = g_prefixedArFilePath; // __imp__sub_82E140D8(ctx, base); // } // Hedgehog::Database::CDatabaseLoader::CCreateFromArchive::CreateCallback // PPC_FUNC_IMPL(__imp__sub_82E0B500); // PPC_FUNC(sub_82E0B500) // { // if (g_mods.empty()) // { // __imp__sub_82E0B500(ctx, base); // return; // } // uint32_t prefixedArFilePath = PPC_LOAD_U32(ctx.r5.u32); // std::u8string_view arFilePathU8(reinterpret_cast(base + prefixedArFilePath)); // if (!arFilePathU8.starts_with(u8"/UnleashedRecomp/")) // { // __imp__sub_82E0B500(ctx, base); // return; // } // // Immediately clear the string, so the remaining splits don't load append archives again. // PPC_STORE_U8(prefixedArFilePath, 0x00); // arFilePathU8.remove_prefix(0x11); // auto r3 = ctx.r3; // Callback // auto r4 = ctx.r4; // Database // auto r5 = ctx.r5; // Name // auto r6 = ctx.r6; // Data // auto r7 = ctx.r7; // Size // auto r8 = ctx.r8; // Callback data // auto loadArchive = [&](const std::filesystem::path& arFilePath) // { // std::ifstream stream(arFilePath, std::ios::binary); // if (stream.good()) // { // if (ModLoader::s_isLogTypeConsole) // LOGF_IMPL(Utility, "Mod Loader", "Loading file: \"{}\"", reinterpret_cast(arFilePath.u8string().c_str())); // stream.seekg(0, std::ios::end); // size_t arFileSize = stream.tellg(); // void* arFileData = g_userHeap.Alloc(arFileSize); // stream.seekg(0, std::ios::beg); // stream.read(reinterpret_cast(arFileData), arFileSize); // stream.close(); // auto arFileDataHolder = reinterpret_cast*>(g_userHeap.Alloc(sizeof(uint32_t) * 2)); // if (*reinterpret_cast*>(arFileData) == LZX_SIGNATURE) // { // auto fileData = decompressLzx(ctx, base, reinterpret_cast(arFileData), arFileSize, arFileDataHolder); // g_userHeap.Free(arFileData); // arFileData = fileData.data(); // arFileSize = fileData.size(); // } // arFileDataHolder[0] = g_memory.MapVirtual(arFileData); // arFileDataHolder[1] = NULL; // ctx.r3 = r3; // ctx.r4 = r4; // ctx.r5 = r5; // ctx.r6.u32 = g_memory.MapVirtual(arFileDataHolder); // ctx.r7.u32 = uint32_t(arFileSize); // ctx.r8 = r8; // __imp__sub_82E0B500(ctx, base); // g_userHeap.Free(arFileDataHolder); // g_userHeap.Free(arFileData); // return true; // } // return false; // }; // thread_local xxHashMap> s_cache; // XXH64_hash_t hash = XXH3_64bits(arFilePathU8.data(), arFilePathU8.size()); // auto findResult = s_cache.find(hash); // if (findResult != s_cache.end()) // { // for (const auto& arFilePath : findResult->second) // loadArchive(arFilePath); // } // else // { // std::vector arFilePaths; // std::filesystem::path arFilePath; // std::filesystem::path appendArFilePath; // for (auto& mod : g_mods) // { // for (auto& includeDir : mod.includeDirs) // { // auto loadUncachedArchive = [&](const std::filesystem::path& arFilePath) // { // if (mod.type == ModType::UMM && mod.readOnly.contains(arFilePath)) // return false; // std::filesystem::path combinedFilePath = includeDir / arFilePath; // bool success = loadArchive(combinedFilePath); // if (success) // arFilePaths.emplace_back(std::move(combinedFilePath)); // return success; // }; // auto loadArchives = [&](const std::filesystem::path& arFilePath) // { // thread_local std::filesystem::path s_tempPath; // s_tempPath = arFilePath; // s_tempPath += "l"; // if (mod.type == ModType::UMM && mod.readOnly.contains(s_tempPath)) // return; // std::ifstream stream(includeDir / s_tempPath, std::ios::binary); // if (stream.good()) // { // be signature{}; // uint32_t splitCount{}; // stream.read(reinterpret_cast(&signature), sizeof(signature)); // if (signature == LZX_SIGNATURE) // { // stream.seekg(0, std::ios::end); // size_t arlFileSize = stream.tellg(); // stream.seekg(0, std::ios::beg); // void* compressedFileData = g_userHeap.Alloc(arlFileSize); // stream.read(reinterpret_cast(compressedFileData), arlFileSize); // stream.close(); // auto fileData = decompressLzx(ctx, base, reinterpret_cast(compressedFileData), arlFileSize, nullptr); // g_userHeap.Free(compressedFileData); // splitCount = *reinterpret_cast(fileData.data() + 0x4); // g_userHeap.Free(fileData.data()); // } // else // { // stream.read(reinterpret_cast(&splitCount), sizeof(splitCount)); // stream.close(); // } // if (splitCount == 0) // { // loadUncachedArchive(arFilePath); // } // else // { // for (uint32_t i = 0; i < splitCount; i++) // { // s_tempPath = arFilePath; // s_tempPath += fmt::format(".{:02}", i); // loadUncachedArchive(s_tempPath); // } // } // } // else if (mod.type == ModType::UMM) // { // if (!loadUncachedArchive(arFilePath)) // { // for (uint32_t i = 0; ; i++) // { // s_tempPath = arFilePath; // s_tempPath += fmt::format(".{:02}", i); // if (!loadUncachedArchive(s_tempPath)) // break; // } // } // } // }; // if (mod.type == ModType::UMM) // { // if (mod.merge) // { // if (arFilePath.empty()) // arFilePath = arFilePathU8; // loadArchives(arFilePath); // } // } // else if (mod.type == ModType::HMM) // { // if (appendArFilePath.empty()) // { // if (arFilePath.empty()) // arFilePath = arFilePathU8; // appendArFilePath = arFilePath.parent_path(); // appendArFilePath /= "+"; // appendArFilePath += arFilePath.filename(); // } // loadArchives(appendArFilePath); // } // } // } // s_cache.emplace(hash, std::move(arFilePaths)); // } // ctx.r3 = r3; // ctx.r4 = r4; // ctx.r5 = r5; // ctx.r6 = r6; // ctx.r7 = r7; // ctx.r8 = r8; // __imp__sub_82E0B500(ctx, base); // } // CriAuObjLoc::AttachCueSheet // PPC_FUNC_IMPL(__imp__sub_8314A310); // PPC_FUNC(sub_8314A310) // { // // allocator: 0x4 // // capacity: 0x24 // // count: 0x28 // // data: 0x2C // uint32_t capacity = PPC_LOAD_U32(ctx.r3.u32 + 0x24); // if (capacity == PPC_LOAD_U32(ctx.r3.u32 + 0x28)) // { // auto r3 = ctx.r3; // auto r4 = ctx.r4; // auto r5 = ctx.r5; // // Allocate // ctx.r3.u32 = PPC_LOAD_U32(r3.u32 + 0x4); // ctx.r4.u32 = (capacity * 2) * sizeof(uint32_t); // ctx.r5.u32 = 0x82195248; // AuObjCueSheet // ctx.r6.u32 = 0x4; // sub_83167FD8(ctx, base); // // Copy // uint32_t oldData = PPC_LOAD_U32(r3.u32 + 0x2C); // uint32_t newData = ctx.r3.u32; // memcpy(base + newData, base + oldData, capacity * sizeof(uint32_t)); // memset(base + newData + (capacity * sizeof(uint32_t)), 0, capacity * sizeof(uint32_t)); // PPC_STORE_U32(r3.u32 + 0x24, capacity * 2); // PPC_STORE_U32(r3.u32 + 0x2C, newData); // // Deallocate // ctx.r3.u32 = PPC_LOAD_U32(r3.u32 + 0x4); // ctx.r4.u32 = oldData; // sub_83168100(ctx, base); // ctx.r3 = r3; // ctx.r4 = r4; // ctx.r5 = r5; // } // __imp__sub_8314A310(ctx, base); // } ================================================ FILE: MarathonRecomp/mod/mod_loader.h ================================================ #pragma once struct ModLoader { static inline bool s_isLogTypeConsole; static inline std::filesystem::path s_saveFilePath; static std::filesystem::path ResolvePath(std::string_view path); static std::vector* GetIncludeDirectories(size_t modIndex); static void Init(); }; ================================================ FILE: MarathonRecomp/mutex.h ================================================ #pragma once #ifdef _WIN32 struct Mutex : CRITICAL_SECTION { Mutex() { InitializeCriticalSection(this); } ~Mutex() { DeleteCriticalSection(this); } void lock() { EnterCriticalSection(this); } void unlock() { LeaveCriticalSection(this); } }; #else using Mutex = std::mutex; #endif ================================================ FILE: MarathonRecomp/natvis.natvis ================================================ {get()} get() {get()} get() {get()} get() ================================================ FILE: MarathonRecomp/os/.gitignore ================================================ ![Ww][Ii][Nn]32/ ================================================ FILE: MarathonRecomp/os/linux/logger_linux.cpp ================================================ #include void os::logger::Init() { } void os::logger::Log(const std::string_view str, ELogType type, const char* func) { if (func) { fmt::println("[{}] {}", func, str); } else { fmt::println("{}", str); } } ================================================ FILE: MarathonRecomp/os/linux/media_linux.cpp ================================================ #include #include #include #include #include #include #include #include #include #include enum class PlaybackStatus { Stopped, Playing, Paused }; static const char* DBusInterface = "org.freedesktop.DBus"; static const char* DBusPropertiesInterface = "org.freedesktop.DBus.Properties"; static const char* DBusPath = "/org/freedesktop/DBus"; static const char* MPRIS2Interface = "org.mpris.MediaPlayer2"; static const char* MPRIS2PlayerInterface = "org.mpris.MediaPlayer2.Player"; static const char* MPRIS2Path = "/org/mpris/MediaPlayer2"; static std::optional g_dbusThread; static std::unordered_map g_playerStatus; static std::atomic g_isPlaying = false; static PlaybackStatus PlaybackStatusFromString(const char* str) { if (g_str_equal(str, "Playing")) return PlaybackStatus::Playing; else if (g_str_equal(str, "Paused")) return PlaybackStatus::Paused; else return PlaybackStatus::Stopped; } static void UpdateActiveStatus() { g_isPlaying = std::ranges::any_of( g_playerStatus | std::views::values, [](PlaybackStatus status) { return status == PlaybackStatus::Playing; } ); } static void UpdateActivePlayers(const char* name, PlaybackStatus status) { g_playerStatus.insert_or_assign(name, status); UpdateActiveStatus(); } static PlaybackStatus MPRISGetPlaybackStatus(GDBusConnection* connection, const gchar* name) { GError* error; GVariant* response; GVariant* tupleChild; GVariant* value; PlaybackStatus status; error = NULL; response = g_dbus_connection_call_sync( connection, name, MPRIS2Path, DBusPropertiesInterface, "Get", g_variant_new("(ss)", MPRIS2PlayerInterface, "PlaybackStatus"), G_VARIANT_TYPE("(v)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error ); if (!response) { LOGF_ERROR("Failed to process D-Bus Get: {}", error->message); g_clear_error(&error); return PlaybackStatus::Stopped; } tupleChild = g_variant_get_child_value(response, 0); value = g_variant_get_variant(tupleChild); if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) { LOG_ERROR("Failed to process D-Bus Get"); g_variant_unref(tupleChild); return PlaybackStatus::Stopped; } status = PlaybackStatusFromString(g_variant_get_string(value, NULL)); g_variant_unref(value); g_variant_unref(tupleChild); g_variant_unref(response); return status; } // Something is very wrong with the system if this happens static void DBusConnectionClosed(GDBusConnection* connection, gboolean remotePeerVanished, GError* error, gpointer userData) { LOG_ERROR("D-Bus connection closed"); g_isPlaying = false; g_main_loop_quit((GMainLoop*)userData); } static void DBusNameOwnerChanged(GDBusConnection* connection, const gchar* senderName, const gchar* objectPath, const gchar* interfaceName, const gchar* signalName, GVariant* parameters, gpointer userData) { const char* name; const char* oldOwner; const char* newOwner; g_variant_get(parameters, "(&s&s&s)", &name, &oldOwner, &newOwner); if (g_str_has_prefix(name, MPRIS2Interface)) { if (oldOwner[0]) { g_playerStatus.erase(oldOwner); } UpdateActiveStatus(); } } static void MPRISPropertiesChanged(GDBusConnection* connection, const gchar* senderName, const gchar* objectPath, const gchar* interfaceName, const gchar* signalName, GVariant* parameters, gpointer userData) { const char* interface; GVariant* changed; GVariantIter iter; const char* key; GVariant* value; PlaybackStatus playbackStatus; g_variant_get_child(parameters, 0, "&s", &interface); g_variant_get_child(parameters, 1, "@a{sv}", &changed); g_variant_iter_init(&iter, changed); while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) { if (g_str_equal(key, "PlaybackStatus")) { playbackStatus = PlaybackStatusFromString(g_variant_get_string(value, NULL)); UpdateActivePlayers(senderName, playbackStatus); g_variant_unref(value); break; } g_variant_unref(value); } g_variant_unref(changed); } /* Called upon CONNECT to discover already active MPRIS2 players by looking for well-known bus names that begin with the MPRIS2 path. g_playerStatus stores unique connection names, not their well-known ones, as the PropertiesChanged signal only provides the former. */ static void DBusListNamesReceived(GObject* object, GAsyncResult* res, gpointer userData) { GDBusConnection* connection; GError* error; GVariant* response; GVariant* tupleChild; GVariantIter iter; const gchar* name; connection = G_DBUS_CONNECTION(object); error = NULL; response = g_dbus_connection_call_finish(connection, res, &error); if (!response) { LOGF_ERROR("Failed to process D-Bus ListNames: {}", error->message); g_clear_error(&error); return; } tupleChild = g_variant_get_child_value(response, 0); g_variant_iter_init(&iter, tupleChild); while (g_variant_iter_next(&iter, "&s", &name)) { GVariant* ownerResponse; const gchar* ownerName; PlaybackStatus status; if (!g_str_has_prefix(name, MPRIS2Interface)) continue; ownerResponse = g_dbus_connection_call_sync( connection, DBusInterface, DBusPath, DBusInterface, "GetNameOwner", g_variant_new("(s)", name), G_VARIANT_TYPE("(s)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error ); if (!ownerResponse) { LOGF_ERROR("Failed to process D-Bus GetNameOwner: {}", error->message); g_clear_error(&error); g_variant_unref(tupleChild); g_variant_unref(response); return; } g_variant_get(ownerResponse, "(&s)", &ownerName); status = MPRISGetPlaybackStatus(connection, ownerName); g_playerStatus.insert_or_assign(ownerName, status); g_variant_unref(ownerResponse); } UpdateActiveStatus(); g_variant_unref(tupleChild); g_variant_unref(response); } static void DBusThreadProc() { GMainContext* mainContext; GMainLoop* mainLoop; GError* error; GDBusConnection* connection; mainContext = g_main_context_new(); g_main_context_push_thread_default(mainContext); mainLoop = g_main_loop_new(mainContext, FALSE); error = NULL; connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); if (!connection) { LOGF_ERROR("Failed to connect to D-Bus: {}", error->message); g_clear_error(&error); g_main_context_unref(mainContext); g_main_loop_unref(mainLoop); return; } g_dbus_connection_set_exit_on_close(connection, FALSE); g_signal_connect(connection, "closed", G_CALLBACK(DBusConnectionClosed), mainLoop); // Listen for player connection changes g_dbus_connection_signal_subscribe( connection, DBusInterface, DBusInterface, "NameOwnerChanged", DBusPath, NULL, G_DBUS_SIGNAL_FLAGS_NONE, DBusNameOwnerChanged, NULL, NULL ); // Listen for player status changes g_dbus_connection_signal_subscribe( connection, NULL, DBusPropertiesInterface, "PropertiesChanged", MPRIS2Path, NULL, G_DBUS_SIGNAL_FLAGS_NONE, MPRISPropertiesChanged, NULL, NULL ); // Request list of current players g_dbus_connection_call( connection, DBusInterface, DBusPath, DBusInterface, "ListNames", NULL, G_VARIANT_TYPE("(as)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, DBusListNamesReceived, NULL ); g_main_loop_run(mainLoop); } bool os::media::IsExternalMediaPlaying() { if (!g_dbusThread) { g_dbusThread.emplace(DBusThreadProc); g_dbusThread->detach(); } return g_isPlaying; } ================================================ FILE: MarathonRecomp/os/linux/process_linux.cpp ================================================ #include #include std::filesystem::path os::process::GetExecutablePath() { char exePath[PATH_MAX] = {}; if (readlink("/proc/self/exe", exePath, PATH_MAX) > 0) { return std::filesystem::path(std::u8string_view((const char8_t*)(exePath))); } else { return std::filesystem::path(); } } std::filesystem::path os::process::GetExecutableRoot() { return GetExecutablePath().remove_filename(); } std::filesystem::path os::process::GetWorkingDirectory() { char cwd[PATH_MAX] = {}; char *res = getcwd(cwd, sizeof(cwd)); if (res != nullptr) { return std::filesystem::path(std::u8string_view((const char8_t*)(cwd))); } else { return std::filesystem::path(); } } bool os::process::SetWorkingDirectory(const std::filesystem::path& path) { return chdir(path.c_str()) == 0; } bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) { pid_t pid = fork(); if (pid < 0) return false; if (pid == 0) { setsid(); std::u8string workU8 = work.u8string(); chdir((const char*)(workU8.c_str())); std::u8string pathU8 = path.u8string(); std::vector argStrs; argStrs.push_back((char*)(pathU8.c_str())); for (const std::string& arg : args) argStrs.push_back((char *)(arg.c_str())); argStrs.push_back(nullptr); execvp((const char*)(pathU8.c_str()), argStrs.data()); raise(SIGKILL); } return true; } void os::process::CheckConsole() { // Always visible on Linux. g_consoleVisible = true; } void os::process::ShowConsole() { // Unnecessary on Linux. } ================================================ FILE: MarathonRecomp/os/linux/registry_linux.inl ================================================ #include // TODO: Implement inline bool os::registry::Init() { return false; } // TODO: read from file? template bool os::registry::ReadValue(const std::string_view& name, T& data) { return false; } // TODO: write to file? template bool os::registry::WriteValue(const std::string_view& name, const T& data) { return false; } ================================================ FILE: MarathonRecomp/os/linux/user_linux.cpp ================================================ #include bool os::user::IsDarkTheme() { return false; } ================================================ FILE: MarathonRecomp/os/linux/version_linux.cpp ================================================ #include os::version::OSVersion os::version::GetOSVersion() { assert(false && "Unimplemented."); return os::version::OSVersion(); } ================================================ FILE: MarathonRecomp/os/logger.h ================================================ #pragma once #include #define LOG_IMPL(type, func, str) os::logger::Log(str, os::logger::ELogType::type, func) #define LOGF_IMPL(type, func, str, ...) os::logger::Log(fmt::format(str, __VA_ARGS__), os::logger::ELogType::type, func) // Function-specific logging. #define LOG(str) LOG_IMPL(None, __func__, str) #define LOG_WARNING(str) LOG_IMPL(Warning, __func__, str) #define LOG_ERROR(str) LOG_IMPL(Error, __func__, str) #if _DEBUG #define LOG_UTILITY(str) LOG_IMPL(Utility, __func__, str) #else #define LOG_UTILITY(str) LOG_IMPL(Utility, __func__, str) #endif #define LOGF(str, ...) LOGF_IMPL(None, __func__, str, __VA_ARGS__) #define LOGF_WARNING(str, ...) LOGF_IMPL(Warning, __func__, str, __VA_ARGS__) #define LOGF_ERROR(str, ...) LOGF_IMPL(Error, __func__, str, __VA_ARGS__) #if _DEBUG #define LOGF_UTILITY(str, ...) LOGF_IMPL(Utility, __func__, str, __VA_ARGS__) #else #define LOGF_UTILITY(str, ...) LOGF_IMPL(Utility, __func__, str, __VA_ARGS__) #endif // Non-function-specific logging. #define LOGN(str) LOG_IMPL(None, "*", str) #define LOGN_WARNING(str) LOG_IMPL(Warning, "*", str) #define LOGN_ERROR(str) LOG_IMPL(Error, "*", str) #if _DEBUG #define LOGN_UTILITY(str) LOG_IMPL(Utility, "*", str) #else #define LOGN_UTILITY(str) LOG_IMPL(Utility, "*", str) #endif #define LOGFN(str, ...) LOGF_IMPL(None, "*", str, __VA_ARGS__) #define LOGFN_WARNING(str, ...) LOGF_IMPL(Warning, "*", str, __VA_ARGS__) #define LOGFN_ERROR(str, ...) LOGF_IMPL(Error, "*", str, __VA_ARGS__) #if _DEBUG #define LOGFN_UTILITY(str, ...) LOGF_IMPL(Utility, "*", str, __VA_ARGS__) #else #define LOGFN_UTILITY(str, ...) LOGF_IMPL(Utility, "*", str, __VA_ARGS__) #endif namespace os::logger { enum class ELogType { None, Utility, Warning, Error }; void Init(); void Log(const std::string_view str, ELogType type = ELogType::None, const char* func = nullptr); } ================================================ FILE: MarathonRecomp/os/macos/logger_macos.cpp ================================================ #include void os::logger::Init() { } void os::logger::Log(const std::string_view str, ELogType type, const char* func) { if (func) { fmt::println("[{}] {}", func, str); } else { fmt::println("{}", str); } } ================================================ FILE: MarathonRecomp/os/macos/media_macos.cpp ================================================ #include bool os::media::IsExternalMediaPlaying() { // This functionality is not supported in macOS. return false; } ================================================ FILE: MarathonRecomp/os/macos/process_macos.cpp ================================================ #include #include #include #include #include #include #include std::filesystem::path os::process::GetExecutablePath() { uint32_t exePathSize = PATH_MAX; char exePath[PATH_MAX] = {}; if (_NSGetExecutablePath(exePath, &exePathSize) == 0) { return std::filesystem::path(std::u8string_view((const char8_t*)(exePath))); } else { return std::filesystem::path(); } } std::filesystem::path os::process::GetExecutableRoot() { std::filesystem::path resultPath = GetExecutablePath().remove_filename(); if (CFBundleRef bundleRef = CFBundleGetMainBundle()) { if (CFURLRef bundleUrlRef = CFBundleCopyBundleURL(bundleRef)) { char appBundlePath[MAXPATHLEN]; if (CFURLGetFileSystemRepresentation(bundleUrlRef, true, (uint8_t*)(appBundlePath), sizeof(appBundlePath))) { resultPath = std::filesystem::path(appBundlePath).parent_path(); } CFRelease(bundleUrlRef); } } return resultPath; } std::filesystem::path os::process::GetWorkingDirectory() { char cwd[PATH_MAX] = {}; char *res = getcwd(cwd, sizeof(cwd)); if (res != nullptr) { return std::filesystem::path(std::u8string_view((const char8_t*)(cwd))); } else { return std::filesystem::path(); } } bool os::process::SetWorkingDirectory(const std::filesystem::path& path) { return chdir(path.c_str()) == 0; } bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) { pid_t pid = fork(); if (pid < 0) return false; if (pid == 0) { setsid(); std::u8string workU8 = work.u8string(); chdir((const char*)(workU8.c_str())); std::u8string pathU8 = path.u8string(); std::vector argStrs; argStrs.push_back((char*)(pathU8.c_str())); for (const std::string& arg : args) argStrs.push_back((char *)(arg.c_str())); argStrs.push_back(nullptr); execvp((const char*)(pathU8.c_str()), argStrs.data()); raise(SIGKILL); } return true; } void os::process::CheckConsole() { // Always visible on macOS. g_consoleVisible = true; } void os::process::ShowConsole() { // Unnecessary on macOS. } ================================================ FILE: MarathonRecomp/os/macos/registry_macos.inl ================================================ #include // TODO: Implement inline bool os::registry::Init() { return false; } // TODO: read from file? template bool os::registry::ReadValue(const std::string_view& name, T& data) { return false; } // TODO: write to file? template bool os::registry::WriteValue(const std::string_view& name, const T& data) { return false; } ================================================ FILE: MarathonRecomp/os/macos/user_macos.cpp ================================================ #include bool os::user::IsDarkTheme() { return false; } ================================================ FILE: MarathonRecomp/os/macos/version_macos.cpp ================================================ #include os::version::OSVersion os::version::GetOSVersion() { assert(false && "Unimplemented."); return os::version::OSVersion(); } ================================================ FILE: MarathonRecomp/os/media.h ================================================ #pragma once namespace os::media { bool IsExternalMediaPlaying(); } ================================================ FILE: MarathonRecomp/os/process.h ================================================ #pragma once namespace os::process { inline bool g_consoleVisible; std::filesystem::path GetExecutablePath(); std::filesystem::path GetExecutableRoot(); std::filesystem::path GetWorkingDirectory(); bool SetWorkingDirectory(const std::filesystem::path& path); bool StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work = {}); void CheckConsole(); void ShowConsole(); } ================================================ FILE: MarathonRecomp/os/registry.h ================================================ #pragma once namespace os::registry { bool Init(); template bool ReadValue(const std::string_view& name, T& data); template bool WriteValue(const std::string_view& name, const T& data); } #if _WIN32 #include #elif defined(__linux__) #include #elif defined(__APPLE__) #include #endif ================================================ FILE: MarathonRecomp/os/user.h ================================================ #pragma once namespace os::user { bool IsDarkTheme(); } ================================================ FILE: MarathonRecomp/os/version.h ================================================ #pragma once namespace os::version { struct OSVersion { uint32_t Major{}; uint32_t Minor{}; uint32_t Build{}; }; OSVersion GetOSVersion(); } ================================================ FILE: MarathonRecomp/os/win32/logger_win32.cpp ================================================ #include #include #define FOREGROUND_WHITE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) #define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN) static HANDLE g_hStandardOutput; void os::logger::Init() { g_hStandardOutput = GetStdHandle(STD_OUTPUT_HANDLE); } void os::logger::Log(const std::string_view str, ELogType type, const char* func) { if (!os::process::g_consoleVisible) return; switch (type) { case ELogType::Utility: SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_GREEN | FOREGROUND_INTENSITY); break; case ELogType::Warning: SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_YELLOW | FOREGROUND_INTENSITY); break; case ELogType::Error: SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_RED | FOREGROUND_INTENSITY); break; default: SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_WHITE); break; } if (func) { fmt::println("[{}] {}", func, str); } else { fmt::println("{}", str); } SetConsoleTextAttribute(g_hStandardOutput, FOREGROUND_WHITE); } ================================================ FILE: MarathonRecomp/os/win32/media_win32.cpp ================================================ #include #include #include #include using namespace winrt; using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Media::Control; static GlobalSystemMediaTransportControlsSessionManager g_sessionManager = nullptr; static GlobalSystemMediaTransportControlsSessionManager GetSessionManager() { if (g_sessionManager) return g_sessionManager; try { init_apartment(); return g_sessionManager = GlobalSystemMediaTransportControlsSessionManager::RequestAsync().get(); } catch (...) { LOGF_ERROR("Failed to retrieve GSMTC session manager: 0x{:X}", to_hresult().value); return nullptr; } } static GlobalSystemMediaTransportControlsSession GetCurrentSession() { auto sessionManager = GetSessionManager(); if (!sessionManager) return nullptr; try { return sessionManager.GetCurrentSession(); } catch (...) { LOGF_ERROR("Failed to retrieve current GSMTC session: 0x{:X}", to_hresult().value); return nullptr; } } static GlobalSystemMediaTransportControlsSessionPlaybackInfo GetPlaybackInfo() { auto session = GetCurrentSession(); if (!session) return nullptr; try { return session.GetPlaybackInfo(); } catch (...) { LOGF_ERROR("Failed to retrieve GSMTC playback info: 0x{:X}", to_hresult().value); return nullptr; } } bool os::media::IsExternalMediaPlaying() { auto playbackInfo = GetPlaybackInfo(); if (!playbackInfo) return false; try { return playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Playing; } catch (...) { LOGF_ERROR("Failed to retrieve GSMTC playback status: 0x{:X}", to_hresult().value); return false; } } ================================================ FILE: MarathonRecomp/os/win32/process_win32.cpp ================================================ #include std::filesystem::path os::process::GetExecutablePath() { WCHAR exePath[MAX_PATH]; if (!GetModuleFileNameW(nullptr, exePath, MAX_PATH)) return std::filesystem::path(); return std::filesystem::path(exePath); } std::filesystem::path os::process::GetExecutableRoot() { return GetExecutablePath().remove_filename(); } std::filesystem::path os::process::GetWorkingDirectory() { WCHAR workPath[MAX_PATH]; if (!GetCurrentDirectoryW(MAX_PATH, workPath)) return std::filesystem::path(); return std::filesystem::path(workPath); } bool os::process::SetWorkingDirectory(const std::filesystem::path& path) { return SetCurrentDirectoryW(path.c_str()); } bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) { if (path.empty()) return false; if (work.empty()) work = path.parent_path(); auto cli = path.wstring(); // NOTE: We assume the CLI arguments only contain ASCII characters. for (auto& arg : args) cli += L" " + std::wstring(arg.begin(), arg.end()); STARTUPINFOW startInfo{ sizeof(STARTUPINFOW) }; PROCESS_INFORMATION procInfo{}; std::wstring pathW = path.wstring(); std::wstring workW = work.wstring(); if (!CreateProcessW(pathW.c_str(), cli.data(), nullptr, nullptr, false, 0, nullptr, workW.c_str(), &startInfo, &procInfo)) return false; CloseHandle(procInfo.hProcess); CloseHandle(procInfo.hThread); return true; } void os::process::CheckConsole() { g_consoleVisible = (GetConsoleWindow() != nullptr); } void os::process::ShowConsole() { if (GetConsoleWindow() == nullptr) { AllocConsole(); freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stderr); freopen("CONOUT$", "w", stdout); g_consoleVisible = true; } } ================================================ FILE: MarathonRecomp/os/win32/registry_win32.inl ================================================ #include inline const wchar_t* g_registryRoot = L"Software\\MarathonRecomp"; inline bool os::registry::Init() { return true; } template bool os::registry::ReadValue(const std::string_view& name, T& data) { HKEY hKey; if (RegOpenKeyExW(HKEY_CURRENT_USER, g_registryRoot, 0, KEY_READ, &hKey) != ERROR_SUCCESS) return false; wchar_t wideName[128]; int wideNameSize = MultiByteToWideChar(CP_UTF8, 0, name.data(), name.size(), wideName, sizeof(wideName)); if (wideNameSize == 0) { return false; } wideName[wideNameSize] = 0; DWORD bufferSize = 0; DWORD dataType = 0; auto result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_ANY, &dataType, nullptr, &bufferSize); if (result != ERROR_SUCCESS) { RegCloseKey(hKey); return false; } result = ERROR_INVALID_FUNCTION; if constexpr (std::is_same_v) { if (dataType == REG_SZ) { std::vector buffer{}; buffer.reserve(bufferSize); result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_REG_SZ, nullptr, buffer.data(), &bufferSize); if (result == ERROR_SUCCESS) { int valueSize = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)buffer.data(), (bufferSize / sizeof(wchar_t)) - 1, nullptr, 0, nullptr, nullptr); data.resize(valueSize); WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)buffer.data(), (bufferSize / sizeof(wchar_t)) - 1, data.data(), valueSize, nullptr, nullptr); } } } else if constexpr (std::is_same_v) { if (dataType == REG_SZ) { std::vector buffer{}; buffer.reserve(bufferSize); result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_REG_SZ, nullptr, buffer.data(), &bufferSize); if (result == ERROR_SUCCESS) { data = reinterpret_cast(buffer.data()); } } } else if constexpr (std::is_same_v) { result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_DWORD, nullptr, (BYTE*)&data, &bufferSize); } else if constexpr (std::is_same_v) { result = RegGetValueW(hKey, nullptr, wideName, RRF_RT_QWORD, nullptr, (BYTE*)&data, &bufferSize); } else { static_assert(false, "Unsupported data type."); } RegCloseKey(hKey); return result == ERROR_SUCCESS; } template bool os::registry::WriteValue(const std::string_view& name, const T& data) { HKEY hKey; if (RegCreateKeyExW(HKEY_CURRENT_USER, g_registryRoot, 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS) return false; BYTE* pData = nullptr; DWORD dataSize = 0; DWORD dataType = 0; bool wideString = false; if constexpr (std::is_same_v) { pData = (BYTE*)data.c_str(); dataSize = data.size() + 1; dataType = REG_SZ; } else if constexpr (std::is_same_v) { pData = &data; dataSize = sizeof(T); dataType = REG_DWORD; } else if constexpr (std::is_same_v) { pData = &data; dataSize = sizeof(T); dataType = REG_QWORD; } else if constexpr (std::is_same_v) { pData = (BYTE*)data.c_str(); dataSize = (wcslen((const wchar_t*)pData) + 1) * sizeof(wchar_t); dataType = REG_SZ; wideString = true; } else { static_assert(false, "Unsupported data type."); } LSTATUS result = ERROR_INVALID_FUNCTION; if (wideString) { wchar_t wideName[128]; int wideNameSize = MultiByteToWideChar(CP_UTF8, 0, name.data(), name.size(), wideName, sizeof(wideName)); if (wideNameSize == 0) { return false; } wideName[wideNameSize] = 0; result = RegSetValueExW(hKey, wideName, 0, dataType, pData, dataSize); } else { result = RegSetValueExA(hKey, name.data(), 0, dataType, pData, dataSize); } RegCloseKey(hKey); if (result != ERROR_SUCCESS) return false; return true; } ================================================ FILE: MarathonRecomp/os/win32/user_win32.cpp ================================================ #include bool os::user::IsDarkTheme() { HKEY hKey; if (RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { DWORD value = 0; DWORD valueSize = sizeof(value); if (RegQueryValueExA(hKey, "AppsUseLightTheme", nullptr, nullptr, (LPBYTE)&value, &valueSize) == ERROR_SUCCESS) { RegCloseKey(hKey); return value == 0; } RegCloseKey(hKey); } return false; } ================================================ FILE: MarathonRecomp/os/win32/version_win32.cpp ================================================ #include LIB_FUNCTION(LONG, "ntdll.dll", RtlGetVersion, PRTL_OSVERSIONINFOW); os::version::OSVersion os::version::GetOSVersion() { auto result = os::version::OSVersion{}; OSVERSIONINFOEXW osvi = { 0 }; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); if (RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi) != 0) return result; result.Major = osvi.dwMajorVersion; result.Minor = osvi.dwMinorVersion; result.Build = osvi.dwBuildNumber; return result; } ================================================ FILE: MarathonRecomp/patches/MainMenuTask_patches.cpp ================================================ #include "MainMenuTask_patches.h" #include #include #include #include // Sonicteam::MainMenuTask::Update PPC_FUNC_IMPL(__imp__sub_824FFCF8); PPC_FUNC(sub_824FFCF8) { auto pMainMenuTask = (Sonicteam::MainMenuTask*)(base + ctx.r3.u32); auto pHUDMainMenu = pMainMenuTask->m_pHUDMainMenu; #ifdef MARATHON_RECOMP_OPTIONS_MENU if (pHUDMainMenu) { // Hide original "OPTIONS" title. if (auto pOptionsSelect = pHUDMainMenu->m_pHudTextRoot->Find("options_select", "options")) pOptionsSelect->m_Priority = -1.0f; } if (pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_MainMenu && pMainMenuTask->m_MainMenuSelectedIndex == 3) { if (!OptionsMenu::s_isVisible && (pMainMenuTask->m_PressedButtons.get() & 0x10) != 0) { OptionsMenu::s_pMainMenuTask = pMainMenuTask; OptionsMenu::Open(); Game_PlaySound("main_deside"); pMainMenuTask->m_State = Sonicteam::MainMenuTask::MainMenuState_MainMenu; pMainMenuTask->m_PressedButtons = 0; guest_stack_var msgSetCursor ( Sonicteam::HUDMainMenu::HUDMainMenuState_MainCursorOutro, pMainMenuTask->m_MainMenuSelectedIndex ); // Play cursor outro animation. pHUDMainMenu->ProcessMessage(msgSetCursor.get()); guest_stack_var msgTransition ( Sonicteam::HUDMainMenu::HUDMainMenuState_OptionsOutro, 3 ); // Play main menu -> options transition. pHUDMainMenu->ProcessMessage(msgTransition.get()); } } static bool s_isReturningFromOptionsMenu{}; if (OptionsMenu::s_isVisible) { pMainMenuTask->m_PressedButtons = 0; if (OptionsMenu::s_state == OptionsMenuState::Closing) s_isReturningFromOptionsMenu = true; } else if (s_isReturningFromOptionsMenu) { if ((pHUDMainMenu->m_CursorFlags.get() & 2) != 0) { // Prevent inputs leaking from the // options menu to the main menu. pMainMenuTask->m_PressedButtons = 0; } else { s_isReturningFromOptionsMenu = false; } } #endif #ifdef MARATHON_RECOMP_ACHIEVEMENT_MENU if (pHUDMainMenu) { // Hide original "GOLD MEDAL RESULTS" title. if (auto pGoldMedalResultSelect = pHUDMainMenu->m_pHudTextRoot->Find("goldmedalresult_select", "goldmedalresult")) pGoldMedalResultSelect->m_Priority = -1.0f; } if (pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_GoldMedalResultsOpen) AchievementMenu::Open(pMainMenuTask); #endif static float s_buttonWindowTextOffsetY{}; auto& rButtonWindowTextOffsetY = pMainMenuTask->m_pButtonWindowTask->m_pHUDButtonWindow->m_pHudTextParts->m_OffsetY; if (MainMenuTaskPatches::s_hideButtonWindow) { static constexpr double HIDE_TEXT_OFFSET = -100000.0f; if (rButtonWindowTextOffsetY != HIDE_TEXT_OFFSET) { // Move original button window text very far off screen to hide it. s_buttonWindowTextOffsetY = rButtonWindowTextOffsetY; rButtonWindowTextOffsetY = HIDE_TEXT_OFFSET; } } else { // Restore original button window text offset. rButtonWindowTextOffsetY = s_buttonWindowTextOffsetY; } MainMenuTaskPatches::s_state = (Sonicteam::MainMenuTask::MainMenuState)pMainMenuTask->m_State.get(); for (auto& event : MainMenuTaskPatches::s_events) event->Update(pMainMenuTask, ctx.f1.f64); __imp__sub_824FFCF8(ctx, base); } bool HUDGoldMedal_ShouldDestroyTable() { return AchievementMenu::s_state == AchievementMenuState::ClosingGoldMedals || AchievementMenu::s_state == AchievementMenuState::ClosingAchievements; } bool MainMenuTask_GoldMedalResults_SkipOutro() { return AchievementMenu::s_state == AchievementMenuState::ClosingAchievements; } ================================================ FILE: MarathonRecomp/patches/MainMenuTask_patches.h ================================================ #pragma once #include #include class MainMenuTaskPatches { public: static inline bool s_hideButtonWindow{}; static inline Sonicteam::MainMenuTask::MainMenuState s_state{}; static inline std::vector*> s_events{}; }; ================================================ FILE: MarathonRecomp/patches/SaveDataTask_patches.cpp ================================================ #include #include #include #include enum { ACH_ERROR_SUCCESS, ACH_ERROR_READ, ACH_ERROR_WRITE }; static Sonicteam::SaveDataTaskXENON::SaveDataOperation g_currentAlert{}; static int g_achError{ ACH_ERROR_SUCCESS }; static int g_achErrorIgnored{ ACH_ERROR_SUCCESS }; // Sonicteam::SaveDataTaskXENON::RunOperation (speculatory) PPC_FUNC_IMPL(__imp__sub_8238CB18); PPC_FUNC(sub_8238CB18) { // Redirect storage device lost alert to load failed alert. // The user should never see this alert, but if they do, it should make a bit more sense. if (ctx.r4.u32 == Sonicteam::SaveDataTaskXENON::SaveDataOperation_AlertSelectDevice) ctx.r4.u32 = Sonicteam::SaveDataTaskXENON::SaveDataOperation_AlertLoadFailed; // Update current alert to operations that only display // messages so we can keep track of the last message displayed. if (ctx.r4.u32 < 3 && ctx.r4.u32 >= 6 && ctx.r4.u32 < 8) g_currentAlert = (Sonicteam::SaveDataTaskXENON::SaveDataOperation)ctx.r4.u32; // Redirect overwrite alert to just save the game, if requested. if (Config::Autosave && ctx.r4.u32 == Sonicteam::SaveDataTaskXENON::SaveDataOperation_AlertOverwrite) ctx.r4.u32 = Sonicteam::SaveDataTaskXENON::SaveDataOperation_WriteSaveData; // Redirect storage device select operation to access the save data. // This option is replaced with a "Retry" option, so it should attempt to access it again. if (ctx.r4.u32 == Sonicteam::SaveDataTaskXENON::SaveDataOperation_SelectStorageDevice) { if (g_currentAlert == Sonicteam::SaveDataTaskXENON::SaveDataOperation_AlertLoadFailed) { ctx.r4.u32 = Sonicteam::SaveDataTaskXENON::SaveDataOperation_ReadSaveData; } else { ctx.r4.u32 = Sonicteam::SaveDataTaskXENON::SaveDataOperation_WriteSaveData; } } // Attempt to load achievement data on save read. if (ctx.r4.u32 == Sonicteam::SaveDataTaskXENON::SaveDataOperation_ReadSaveData) { if (!AchievementManager::LoadBinary()) { // This condition will always be met if the achievement data is corrupted (!= IOError), // or as long as "Continue without saving." has not been chosen on load in this session. if (AchievementManager::BinStatus != EAchBinStatus::IOError || g_achErrorIgnored != ACH_ERROR_READ) { g_achError = ACH_ERROR_READ; return; } } } // Attempt to save achievement data on save write. if (ctx.r4.u32 == Sonicteam::SaveDataTaskXENON::SaveDataOperation_WriteSaveData) { // This condition will be met if the achievement data failed to save (any error status), // and as long as "Continue without saving." has not been chosen on save in this session. if (!AchievementManager::SaveBinary() && g_achErrorIgnored != ACH_ERROR_WRITE) { g_achError = ACH_ERROR_WRITE; return; } } __imp__sub_8238CB18(ctx, base); } // Sonicteam::SaveDataTaskXENON::Update PPC_FUNC_IMPL(__imp__sub_8238D5C8); PPC_FUNC(sub_8238D5C8) { auto pSaveDataTaskXENON = (Sonicteam::SaveDataTaskXENON*)(base + ctx.r3.u32); if (g_achError != ACH_ERROR_SUCCESS) { static auto s_achErrorMessageResult = -1; auto& message = Localise("Title_Message_LoadAchievementDataIOError"); std::vector options = { Localise("Common_Retry"), Localise("Common_ContinueWithoutSaving") }; switch (g_achError) { case ACH_ERROR_READ: { // Achievement data is unrecoverable, force user to overwrite it. if (AchievementManager::BinStatus != EAchBinStatus::IOError) { message = Localise("Title_Message_LoadAchievementDataCorrupt"); options[0] = Localise("Common_OK"); options.pop_back(); } break; } case ACH_ERROR_WRITE: message = Localise("Title_Message_SaveAchievementDataIOError"); break; } if (MessageWindow::Open(message, &s_achErrorMessageResult, options, 0) == MSG_CLOSED) { auto operation = g_achError == ACH_ERROR_READ ? Sonicteam::SaveDataTaskXENON::SaveDataOperation_ReadSaveData : Sonicteam::SaveDataTaskXENON::SaveDataOperation_WriteSaveData; switch (s_achErrorMessageResult) { // Retry / OK case 0: { // Create new achievement data file. AchievementManager::SaveBinary(true); // Display any future errors for new files. g_achErrorIgnored = ACH_ERROR_SUCCESS; break; } // Continue without saving. // Ignore any future errors unrelated to corruption for this file. case 1: g_achErrorIgnored = g_achError; break; } g_achError = ACH_ERROR_SUCCESS; s_achErrorMessageResult = -1; GuestToHostFunction(sub_8238CB18, pSaveDataTaskXENON, operation); } } __imp__sub_8238D5C8(ctx, base); } void SaveAlertThreeOptionRemoveDeviceSelect(PPCRegister& r5) { auto options = (uint64_t*)g_memory.Translate(r5.u32 + 8); // The "Select storage device." option is always the // second index for the three option alert windows. options[2] = 0; } ================================================ FILE: MarathonRecomp/patches/TitleTask_patches.cpp ================================================ #include #include #include #include #include #include #include #include #include #include constexpr auto TITLE_OPTION_OUTRO_FRAMES = (1.0 / 60.0) * 45.0; constexpr auto TITLE_OUTRO_FRAMES = (1.0 / 60.0) * 25.0; constexpr auto TITLE_OPTION_OUTRO_TOTAL_FRAMES = TITLE_OPTION_OUTRO_FRAMES + TITLE_OUTRO_FRAMES; static double g_titleProceedOutroTime{}; static double g_titleExitOutroTime{}; static bool g_quitMessageOpen{}; static bool g_saveDataExists{}; static bool g_isSecretDone{}; static uint32_t g_secretFlags{}; enum { SECRET_NONE, SECRET_UP = 1 << 0, SECRET_DOWN = 1 << 1, SECRET_LEFT = 1 << 2, SECRET_RIGHT = 1 << 3, SECRET_ALL = SECRET_UP | SECRET_DOWN | SECRET_LEFT | SECRET_RIGHT }; bool ProcessQuitMessage(Sonicteam::TitleTask* pTitleTask) { static auto s_quitMessageResult = -1; static std::atomic s_faderBegun = false; if (!g_quitMessageOpen) return false; if ((App::s_time - g_titleExitOutroTime) < TITLE_OUTRO_FRAMES) return true; static std::array s_options = { Localise("Common_Yes"), Localise("Common_No") }; if (MessageWindow::Open(Localise("Title_Message_Quit"), &s_quitMessageResult, s_options, 1) == MSG_CLOSED) { if (s_quitMessageResult == 0) { Fader::FadeOut(1, []() { App::Exit(); }); s_faderBegun = true; } else { // Play Title_Open. pTitleTask->m_MovieWaitTime = Sonicteam::TitleTask::ms_DefaultMovieWaitTime; GuestToHostFunction(sub_825119D8, pTitleTask, 0, 0); } g_quitMessageOpen = false; s_quitMessageResult = -1; } return true; } void TitleTask_SetDefaultOption(PPCRegister& r3, PPCRegister& r4) { if (!g_saveDataExists) return; // Set default option to CONTINUE if save data exists. reinterpret_cast(g_memory.Translate(r3.u32))->m_SelectedIndex = 1; r4.u32 = 5; } bool TitleTask_RedirectStateTransitionToOutroAnim(PPCRegister& r31) { if (!g_saveDataExists || Config::DisableTitleInputDelay) return false; // Play Title_Close_02. GuestToHostFunction(sub_825119D8, r31.u32, 8, 0); return true; } // Sonicteam::TitleTask::Update PPC_FUNC_IMPL(__imp__sub_825126A0); PPC_FUNC(sub_825126A0) { auto pTitleTask = (Sonicteam::TitleTask*)(base + ctx.r3.u32); auto deltaTime = ctx.f1.f64; switch (pTitleTask->m_State) { case Sonicteam::TitleTask::TitleState_Open: { if (!Config::DisableTitleInputDelay) break; // Skip open animation. GuestToHostFunction(sub_82511CA0, pTitleTask, (int)Sonicteam::TitleTask::TitleState_Wait); break; } case Sonicteam::TitleTask::TitleState_Wait: { if (g_isSecretDone) break; if (auto& spInputManager = App::s_pApp->m_pDoc->m_vspInputManager[0]) { auto& rPadState = spInputManager->m_PadState; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_DPadUp)) g_secretFlags = SECRET_UP; if ((g_secretFlags & SECRET_UP) != 0 && rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_DPadDown)) g_secretFlags |= SECRET_DOWN; if ((g_secretFlags & SECRET_DOWN) != 0 && rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_DPadLeft)) g_secretFlags |= SECRET_LEFT; if ((g_secretFlags & SECRET_LEFT) != 0 && rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_DPadRight)) g_secretFlags |= SECRET_RIGHT; } if (g_secretFlags == SECRET_ALL) { Game_PlaySound("totalring_count"); g_isSecretDone = true; } break; } case Sonicteam::TitleTask::TitleState_OptionsWait: { if (auto& spInputManager = App::s_pApp->m_pDoc->m_vspInputManager[0]) { auto& rPadState = spInputManager->m_PadState; if (g_secretFlags == SECRET_ALL && rPadState.IsDown(Sonicteam::SoX::Input::KeyState_A) && rPadState.IsDown(Sonicteam::SoX::Input::KeyState_Start)) { OptionsMenu::s_isDebugUnlocked = true; } } break; } case Sonicteam::TitleTask::TitleState_OptionsProceed: { // Reset achievements on new game. if (pTitleTask->m_SelectedIndex == 0) { LOGN("Resetting achievements..."); AchievementManager::Reset(); } if (!Config::DisableTitleInputDelay) { g_titleProceedOutroTime += deltaTime; // Wait for outro animation to complete before entering menu. if (g_titleProceedOutroTime > TITLE_OPTION_OUTRO_TOTAL_FRAMES) GuestToHostFunction(sub_82511CA0, pTitleTask, 9); } break; } case Sonicteam::TitleTask::TitleState_Proceed: { g_saveDataExists = std::filesystem::exists(GetSaveFilePath(false)); // Redirect PRESS START proceed to options open. if (g_saveDataExists) GuestToHostFunction(sub_82511CA0, pTitleTask, (int)Sonicteam::TitleTask::TitleState_OptionsOpen); break; } } if (pTitleTask->m_State != Sonicteam::TitleTask::TitleState_OptionsProceed) g_titleProceedOutroTime = 0.0; auto skipStateUpdate = ProcessQuitMessage(pTitleTask); if (skipStateUpdate) { GuestToHostFunction(sub_82511B10, pTitleTask, deltaTime); pTitleTask->m_MovieWaitTime = pTitleTask->m_MovieWaitTime - deltaTime; pTitleTask->m_Field60 = 0; if (pTitleTask->m_MovieWaitTime < 0.0f) pTitleTask->m_MovieWaitTime = 0.0f; } else { if (auto& spInputManager = App::s_pApp->m_pDoc->m_vspInputManager[0]) { auto& rPadState = spInputManager->m_PadState; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_B)) { if (pTitleTask->m_State == Sonicteam::TitleTask::TitleState_Wait) { // Play Title_Close_03. Game_PlaySound("deside"); GuestToHostFunction(sub_825119D8, pTitleTask, 9, 0); g_titleExitOutroTime = App::s_time; g_quitMessageOpen = true; } else if (pTitleTask->m_State == Sonicteam::TitleTask::TitleState_OptionsWait) { // Return to title wait. Game_PlaySound("window_close"); GuestToHostFunction(sub_825119D8, pTitleTask, 1, 1); GuestToHostFunction(sub_82511CA0, pTitleTask, (int)Sonicteam::TitleTask::TitleState_Wait); } } } __imp__sub_825126A0(ctx, base); } } PPC_FUNC_IMPL(__imp__sub_82511540); PPC_FUNC(sub_82511540) { if (Config::DisableTitleInputDelay) { ctx.r3.u32 = 1; return; } __imp__sub_82511540(ctx, base); } ================================================ FILE: MarathonRecomp/patches/aspect_ratio_patches.cpp ================================================ #include "aspect_ratio_patches.h" #include #include #include #include #include #include #include #include #include #include #include #include // #define CORNER_DEBUG constexpr float CHEVRON_INTRO_DURATION = 0.083f; constexpr float CHEVRON_OUTRO_DURATION = 2.01666666666667f; static Mutex g_pathMutex; static std::map g_paths{}; static std::optional g_sceneModifier{}; static std::optional g_castNodeModifier{}; static std::optional g_castModifier{}; static float g_fontPictureWidth{}; static float g_fontPictureHeight{}; static float g_corners[8]{}; static bool g_cornerExtract{}; static float g_radarMapX{}; static float g_radarMapY{}; static float g_radarMapCoverWidth{}; static float g_radarMapCoverHeight{}; static float g_podBaseRightX{}; static float g_bgArrowsEnd{}; static float g_fgArrowsEnd{}; static float g_chevronLoopTime{}; struct ChevronAnim { uint8_t EndAlpha{}; float IntroDuration{}; float IntroStartTime{}; float OutroDuration{}; float OutroStartTime{}; float TimeElapsed{}; uint8_t CurrentAlpha{}; float CurrentOffsetX{}; void Reset() { TimeElapsed = 0.0f; CurrentAlpha = 0; } void Update(float deltaTime) { TimeElapsed += deltaTime; if (TimeElapsed < IntroStartTime && TimeElapsed < OutroStartTime) { CurrentAlpha = std::lerp(0, EndAlpha, std::clamp((TimeElapsed - IntroStartTime) / IntroDuration, 0.0f, 1.0f)); } else { CurrentAlpha = std::lerp(EndAlpha, 0, std::clamp((TimeElapsed - OutroStartTime) / OutroDuration, 0.0f, 1.0f)); } CurrentOffsetX = std::lerp(0.0f, 5.0f, std::clamp(TimeElapsed / g_chevronLoopTime, 0.0f, 1.0f)); if (TimeElapsed < g_chevronLoopTime) return; Reset(); } }; static std::unordered_map g_bgArrows{}; static std::unordered_map g_fgArrows{}; static class LoadingPillarboxEvent : public HookEvent { public: void Update(float deltaTime) override { if (g_aspectRatio > WIDE_ASPECT_RATIO) BlackBar::Show(); } } g_loadingPillarboxEvent{}; static class ChevronAnimResetEvent : public ContextHookEvent { float m_chevronAspectRatio{}; public: void Update(Sonicteam::MainMenuTask* pThis, float deltaTime) override { if (g_aspectRatio <= WIDE_ASPECT_RATIO) return; auto aspectRatioChanged = false; if (g_aspectRatio != m_chevronAspectRatio) aspectRatioChanged = true; m_chevronAspectRatio = g_aspectRatio; if (pThis->m_State == Sonicteam::MainMenuTask::MainMenuState_MainMenuBack || pThis->m_State == Sonicteam::MainMenuTask::MainMenuState_AudioRoom || pThis->m_State == Sonicteam::MainMenuTask::MainMenuState_TheaterRoom || aspectRatioChanged) { g_bgArrows.clear(); g_fgArrows.clear(); } for (auto& arrow : g_bgArrows) arrow.second.Update(deltaTime); for (auto& arrow : g_fgArrows) arrow.second.Update(deltaTime); } } g_chevronAnimResetEvent{}; float ComputeScale(float aspectRatio) { return ((aspectRatio * 720.0f) / 1280.0f) / sqrt((aspectRatio * 720.0f) / 1280.0f); } void AspectRatioPatches::Init() { LoadingPatches::Events.push_back(&g_loadingPillarboxEvent); MainMenuTaskPatches::s_events.push_back(&g_chevronAnimResetEvent); } void AspectRatioPatches::ComputeOffsets() { float width = Video::s_viewportWidth; float height = Video::s_viewportHeight; g_aspectRatio = width / height; g_aspectRatioGameplayScale = 1.0f; if (g_aspectRatio >= NARROW_ASPECT_RATIO) { g_aspectRatioOffsetX = (width - height * WIDE_ASPECT_RATIO) / 2.0f; g_aspectRatioOffsetY = 0.0f; g_aspectRatioScale = height / 720.0f; // keep same scale above Steam Deck aspect ratio if (g_aspectRatio < WIDE_ASPECT_RATIO) { // interpolate to original 4:3 scale float steamDeckScale = g_aspectRatio / WIDE_ASPECT_RATIO; float narrowScale = ComputeScale(NARROW_ASPECT_RATIO); float lerpFactor = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (STEAM_DECK_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); g_aspectRatioGameplayScale = narrowScale + (steamDeckScale - narrowScale) * lerpFactor; } } else { // 4:3 crop g_aspectRatioOffsetX = (width - width * NARROW_ASPECT_RATIO) / 2.0f; g_aspectRatioOffsetY = (height - width / NARROW_ASPECT_RATIO) / 2.0f; g_aspectRatioScale = width / 960.0f; g_aspectRatioGameplayScale = ComputeScale(NARROW_ASPECT_RATIO); } g_aspectRatioMultiplayerOffsetX = g_aspectRatioOffsetX / 2.0f; g_aspectRatioNarrowScale = std::clamp((g_aspectRatio - NARROW_ASPECT_RATIO) / (WIDE_ASPECT_RATIO - NARROW_ASPECT_RATIO), 0.0f, 1.0f); g_aspectRatioNarrowMargin = std::lerp(50.0f, 0.0f, g_aspectRatioNarrowScale); g_horzCentre = g_aspectRatioOffsetX + 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; g_vertCentre = g_aspectRatioOffsetY + 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; g_radarMapScale = 256 * g_aspectRatioScale * g_aspectRatioGameplayScale; } void EmplacePath(const void* key, const std::string_view& value) { std::lock_guard lock(g_pathMutex); g_paths.emplace(key, HashStr(value)); } void TraverseCast(Chao::CSD::Scene* scene, uint32_t castNodeIndex, Chao::CSD::CastNode* castNode, uint32_t castIndex, const std::string& parentPath) { if (castIndex == ~0) return; TraverseCast(scene, castNodeIndex, castNode, castNode->pCastLinks[castIndex].SiblingCastIndex, parentPath); std::string path = parentPath; for (size_t i = 0; i < scene->CastCount; i++) { auto& index = scene->pCastIndices[i]; if (index.CastNodeIndex == castNodeIndex && index.CastIndex == castIndex) { path += index.pCastName; break; } } EmplacePath(castNode->pCasts[castIndex].get(), path); if (castNode->RootCastIndex == castIndex) EmplacePath(castNode, path); path += "/"; TraverseCast(scene, castNodeIndex, castNode, castNode->pCastLinks[castIndex].ChildCastIndex, path); // LOGFN_UTILITY("CSD hierarchy: {}", path); } void TraverseScene(Chao::CSD::Scene* scene, std::string path) { EmplacePath(scene, path); path += "/"; for (size_t i = 0; i < scene->CastNodeCount; i++) { auto& castNode = scene->pCastNodes[i]; TraverseCast(scene, i, &castNode, castNode.RootCastIndex, path); } } void TraverseSceneNode(Chao::CSD::SceneNode* sceneNode, std::string path) { EmplacePath(sceneNode, path); path += "/"; for (size_t i = 0; i < sceneNode->SceneCount; i++) { auto& sceneIndex = sceneNode->pSceneIndices[i]; TraverseScene(sceneNode->pScenes[sceneIndex.SceneIndex], path + sceneIndex.pSceneName.get()); } for (size_t i = 0; i < sceneNode->SceneNodeCount; i++) { auto& sceneNodeIndex = sceneNode->pSceneNodeIndices[i]; TraverseSceneNode(&sceneNode->pSceneNodes[sceneNodeIndex.SceneNodeIndex], path + sceneNodeIndex.pSceneNodeName.get()); } } // Sonicteam::CsdResource::MakeResource PPC_FUNC_IMPL(__imp__sub_82617570); PPC_FUNC(sub_82617570) { auto pName = reinterpret_cast(base + PPC_LOAD_U32(ctx.r4.u32 + 4)); __imp__sub_82617570(ctx, base); if (!ctx.r3.u32) return; auto ppCsdObject = PPC_LOAD_U32(ctx.r3.u32); if (!ppCsdObject) return; auto pCsdObject = reinterpret_cast(base + ppCsdObject); if (!pCsdObject || !pCsdObject->m_pCsdProject) return; LOGFN_UTILITY("CSD loaded: {} (0x{:08X})", pName, (uint64_t)pCsdObject->m_pCsdProject.get()); static const char* s_languages[7] = { nullptr, // Maps to ELanguages enum. "_English", "_Japanese", "_German", "_French", "_Spanish", "_Italian" }; auto pSuffix = s_languages[(int)Config::Language.Value]; auto nameLen = strlen(pName); auto suffixLen = strlen(pSuffix); // Truncate language names to redirect CSD modifiers. if (suffixLen < nameLen) { if (strcmpIgnoreCase(pName + nameLen - suffixLen, pSuffix)) { pName[nameLen - suffixLen] = '\0'; LOGFN_UTILITY("CSD modifier(s) redirected: {}", pName); } } TraverseSceneNode(pCsdObject->m_pCsdProject->m_pResource->pRootNode, pName); } // Chao::CSD::CMemoryAlloc::Free PPC_FUNC_IMPL(__imp__sub_82656650); PPC_FUNC(sub_82656650) { if (ctx.r4.u32 != NULL && PPC_LOAD_U32(ctx.r4.u32) == 0x4649584E && PPC_LOAD_U32(ctx.r4.u32 + 0x20) == 0x6E43504A) // NXIF, nCPJ { uint32_t fileSize = PPC_LOAD_U32(ctx.r4.u32 + 0x14); std::lock_guard lock(g_pathMutex); const uint8_t* key = base + ctx.r4.u32; auto lower = g_paths.lower_bound(key); auto upper = g_paths.lower_bound(key + fileSize); g_paths.erase(lower, upper); LOGFN_UTILITY("CSD freed: 0x{:08X}", (uint64_t)key); } __imp__sub_82656650(ctx, base); } // Chao::CSD::Scene::Render PPC_FUNC_IMPL(__imp__sub_828C8F60); PPC_FUNC(sub_828C8F60) { auto pScene = (Chao::CSD::Scene*)(base + ctx.r3.u32); g_sceneModifier = FindCsdModifier(ctx.r3.u32); if (g_sceneModifier.has_value()) { if ((g_sceneModifier->Flags & CSD_MODIFIER_ULTRAWIDE_ONLY) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) g_sceneModifier->Flags &= (~g_sceneModifier->Flags) | CSD_MODIFIER_ULTRAWIDE_ONLY; if ((g_sceneModifier->Flags & CSD_SCENE_DISABLE_MOTION) != 0) pScene->FPS = 0; if (g_aspectRatio > WIDE_ASPECT_RATIO) { if ((g_sceneModifier->Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT | CSD_CORNER_EXTRACT)) != 0) { auto r3 = ctx.r3; auto r4 = ctx.r4; auto r5 = ctx.r5; auto r6 = ctx.r6; // Queue draw calls, but don't actually draw anything. We just want to extract the corner. g_cornerExtract = true; __imp__sub_828C8F60(ctx, base); g_cornerExtract = false; #ifdef CORNER_DEBUG if (g_sceneModifier->CornerMax == FLT_MAX) { fmt::print("Corners: "); for (auto corner : g_corners) fmt::print("{} ", corner); fmt::println(""); } #endif ctx.r3 = r3; ctx.r4 = r4; ctx.r5 = r5; ctx.r6 = r6; } } } __imp__sub_828C8F60(ctx, base); } void RenderCsdCastNodeMidAsmHook(PPCRegister& r10, PPCRegister& r27) { g_castNodeModifier = FindCsdModifier(r10.u32 + r27.u32); } void RenderCsdCastMidAsmHook(PPCRegister& r4) { g_castModifier = FindCsdModifier(r4.u32); } void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) { CsdModifier modifier{}; auto vpWidth = float(Video::s_viewportWidth); auto vpHeight = float(Video::s_viewportHeight); if (g_castModifier.has_value()) { modifier = g_castModifier.value(); } else if (g_castNodeModifier.has_value()) { modifier = g_castNodeModifier.value(); } else if (g_sceneModifier.has_value()) { modifier = g_sceneModifier.value(); } // Hide original button window whilst the options menu is visible. if ((modifier.Flags & CSD_BUTTON_WINDOW) != 0 && MainMenuTaskPatches::s_hideButtonWindow) return; // Remove all flags if the aspect ratio is above 16:9. if ((modifier.Flags & CSD_MODIFIER_ULTRAWIDE_ONLY) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) modifier.Flags &= (~modifier.Flags) | CSD_MODIFIER_ULTRAWIDE_ONLY; // Remove all flags if the aspect ratio is below 16:9. if ((modifier.Flags & CSD_MODIFIER_NARROW_ONLY) != 0 && g_aspectRatio >= WIDE_ASPECT_RATIO) modifier.Flags &= (~modifier.Flags) | CSD_MODIFIER_NARROW_ONLY; if ((modifier.Flags & CSD_SKIP) != 0) { if ((modifier.Flags & CSD_CHEVRON) != 0) { // Don't draw non-extended arrows at ultrawide. if (g_aspectRatio > WIDE_ASPECT_RATIO) return; } else { // Skip drawing this cast. return; } } if (g_cornerExtract) { if ((modifier.Flags & (CSD_STORE_LEFT_CORNER | CSD_STORE_RIGHT_CORNER)) != 0) { uint32_t vertexIndex = ((modifier.Flags & CSD_STORE_LEFT_CORNER) != 0) ? 0 : 3; g_corners[modifier.CornerIndex] = *reinterpret_cast*>(base + ctx.r4.u32 + vertexIndex * stride); } return; } // Draw black bars if this cast is drawn. if ((modifier.Flags & CSD_BLACK_BAR) != 0) BlackBar::Show(); // Prohibit black bars from being drawn if this cast is drawn. if ((modifier.Flags & CSD_PROHIBIT_BLACK_BAR) != 0) BlackBar::Hide(); if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::IsVisible()) { // Prevent chevron arrows from being unaligned by centred aspect ratio. if ((modifier.Flags & CSD_CHEVRON) == 0) { if (g_aspectRatio > WIDE_ASPECT_RATIO) { // Remove horizontal alignments at wide aspect ratios. modifier.Flags &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); } else if (g_aspectRatio < WIDE_ASPECT_RATIO) { // Remove vertical alignments at narrow aspect ratios. modifier.Flags &= ~(CSD_ALIGN_TOP | CSD_ALIGN_BOTTOM); } } } // Reserve stack space for vertices. auto size = ctx.r5.u32 * stride; ctx.r1.u32 -= size; // Copy vertices to stack. auto stack = base + ctx.r1.u32; memcpy(stack, base + ctx.r4.u32, size); struct CSDVertex { be X; be Y; be Colour; be U; be V; }; auto getVertex = [&](size_t index) { return reinterpret_cast(stack + (index * stride)); }; auto offsetX = 0.0f; auto offsetY = 0.0f; auto pivotX = 0.0f; auto pivotY = 0.0f; auto scaleX = 1.0f; auto scaleY = 1.0f; auto needsStretch = g_aspectRatio >= WIDE_ASPECT_RATIO; if (needsStretch && (modifier.Flags & CSD_STRETCH_HORIZONTAL) != 0) { scaleX = vpWidth / 1280.0f; } else { scaleX = g_aspectRatioScale; if (needsStretch && (modifier.Flags & CSD_UNSTRETCH_HORIZONTAL) != 0) { pivotX = getVertex(0)->X; offsetX = pivotX * vpWidth / 1280.0f; } else { if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) { offsetX = g_aspectRatioOffsetX * 2.0f; if ((modifier.Flags & CSD_MULTIPLAYER) != 0 && g_aspectRatio > WIDE_ASPECT_RATIO) offsetX = g_aspectRatioMultiplayerOffsetX * 2.0f; } else if ((modifier.Flags & CSD_ALIGN_LEFT) == 0) offsetX = g_aspectRatioOffsetX; // Don't offset arrows at 16:9 or narrower. if ((modifier.Flags & CSD_CHEVRON) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) offsetX = 0.0f; if ((modifier.Flags & CSD_SCALE) != 0) { scaleX *= g_aspectRatioGameplayScale; if ((modifier.Flags & CSD_ALIGN_RIGHT) != 0) offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; else if ((modifier.Flags & CSD_ALIGN_LEFT) == 0) offsetX += g_horzCentre - g_aspectRatioOffsetX; offsetX += pivotX * g_aspectRatioScale; } } } if ((modifier.Flags & CSD_STRETCH_VERTICAL) != 0) { scaleY = vpHeight / 720.0f; } else { scaleY = g_aspectRatioScale; if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) offsetY = g_aspectRatioOffsetY * 2.0f; else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) offsetY = g_aspectRatioOffsetY; if ((modifier.Flags & CSD_SCALE) != 0) { scaleY *= g_aspectRatioGameplayScale; if ((modifier.Flags & CSD_ALIGN_BOTTOM) != 0) offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; else if ((modifier.Flags & CSD_ALIGN_TOP) == 0) offsetY += g_vertCentre - g_aspectRatioOffsetY; offsetY += pivotY * g_aspectRatioScale; } } // Offset cast to movie aspect ratio boundaries. if ((modifier.Flags & CSD_MOVIE) != 0) { if (g_aspectRatio > g_aspectRatioMovie) { offsetX -= (vpWidth - (vpHeight * g_aspectRatioMovie)) / 2.0f; } else { offsetY -= (vpHeight - (vpWidth / g_aspectRatioMovie)) / 2.0f; } } if (g_aspectRatio > WIDE_ASPECT_RATIO) { CsdModifier offsetScaleModifier{}; auto corner = 0.0f; if (g_castModifier.has_value()) { offsetScaleModifier = g_castModifier.value(); corner = getVertex(((offsetScaleModifier.Flags & CSD_STORE_LEFT_CORNER) != 0) ? 0 : 3)->X; } if (offsetScaleModifier.CornerMax == 0.0f && g_castNodeModifier.has_value()) { offsetScaleModifier = g_castNodeModifier.value(); corner = g_corners[offsetScaleModifier.CornerIndex]; } if (offsetScaleModifier.CornerMax == 0.0f && g_sceneModifier.has_value()) { offsetScaleModifier = g_sceneModifier.value(); corner = g_corners[offsetScaleModifier.CornerIndex]; } #ifdef CORNER_DEBUG if ((offsetScaleModifier.Flags & (CSD_OFFSET_SCALE_LEFT | CSD_OFFSET_SCALE_RIGHT)) != 0 && offsetScaleModifier.CornerMax == FLT_MAX) fmt::println("Corner: {}", corner); #endif if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_LEFT) != 0) offsetX *= corner / offsetScaleModifier.CornerMax; else if ((offsetScaleModifier.Flags & CSD_OFFSET_SCALE_RIGHT) != 0) offsetX = vpWidth - (vpWidth - offsetX) * (1280.0f - corner) / (1280.0f - offsetScaleModifier.CornerMax); } auto firstX = 0.0f; auto firstY = 0.0f; auto lastX = 0.0f; auto lastY = 0.0f; auto width = 0.0f; auto height = 0.0f; for (size_t i = 0; i < ctx.r5.u32; i++) { auto vertex = getVertex(i); auto x = offsetX + (vertex->X - pivotX) * scaleX; auto y = offsetY + (vertex->Y - pivotY) * scaleY; if ((modifier.Flags & CSD_EXTEND_LEFT) != 0 && (i == 0 || i == 1)) { x = std::min(x, 0.0f); } else if ((modifier.Flags & CSD_EXTEND_RIGHT) != 0 && (i == 2 || i == 3)) { // Preserve Audio Room "pod" base right edge. if ((modifier.Flags & CSD_POD_BASE) != 0) g_podBaseRightX = x; x = std::max(x, vpWidth); } switch (i) { case 0: firstX = x; break; case 1: lastY = y; break; case 2: firstY = y; break; case 3: lastX = x; break; } if ((modifier.Flags & CSD_RADARMAP) != 0) { g_radarMapX = x; g_radarMapY = y; } vertex->X = round(x); vertex->Y = round(y); } width = lastX - firstX; height = lastY - firstY; if ((modifier.Flags & CSD_RADARMAP) != 0) { g_radarMapCoverWidth = width; g_radarMapCoverHeight = height; } auto flipHorizontally = [&]() { getVertex(0)->X = getVertex(0)->X + width; getVertex(1)->X = getVertex(1)->X + width; getVertex(2)->X = getVertex(2)->X - width; getVertex(3)->X = getVertex(3)->X - width; }; auto flipVertically = [&]() { getVertex(0)->Y = getVertex(0)->Y + height; getVertex(1)->Y = getVertex(1)->Y - height; getVertex(2)->Y = getVertex(2)->Y + height; getVertex(3)->Y = getVertex(3)->Y - height; }; auto applyUVModifier = [&](CsdUVs uvModifier) { getVertex(0)->U = getVertex(0)->U + uvModifier.U0; getVertex(0)->V = getVertex(0)->V + uvModifier.V0; getVertex(1)->U = getVertex(1)->U + uvModifier.U1; getVertex(1)->V = getVertex(1)->V + uvModifier.V1; getVertex(2)->U = getVertex(2)->U + uvModifier.U2; getVertex(2)->V = getVertex(2)->V + uvModifier.V2; getVertex(3)->U = getVertex(3)->U + uvModifier.U3; getVertex(3)->V = getVertex(3)->V + uvModifier.V3; }; auto applyColourModifier = [&](CsdColours colourModifier) { getVertex(0)->Colour = colourModifier.C0; getVertex(1)->Colour = colourModifier.C1; getVertex(2)->Colour = colourModifier.C2; getVertex(3)->Colour = colourModifier.C3; }; if ((modifier.Flags & CSD_UV_MODIFIER) != 0) applyUVModifier(modifier.UVs); if ((modifier.Flags & CSD_COLOUR_MODIFIER) != 0) applyColourModifier(modifier.Colours); // Don't repeat arrows at 16:9 or narrower. if ((modifier.Flags & CSD_CHEVRON) != 0 && g_aspectRatio <= WIDE_ASPECT_RATIO) modifier.Flags &= ~(CSD_REPEAT_LEFT | CSD_REPEAT_RIGHT); auto isRepeatLeft = (modifier.Flags & CSD_REPEAT_LEFT) != 0; auto isRepeatRight = (modifier.Flags & CSD_REPEAT_RIGHT) != 0; auto isMainMenuPanels = (modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0221 | CSD_MAIN_MENU_PARTS_CAST_0222 | CSD_MAIN_MENU_PARTS_CAST_0226 | CSD_MAIN_MENU_PARTS_CAST_0227)) != 0; auto r3 = ctx.r3; auto r5 = ctx.r5; auto drawOriginal = [&]() { ctx.r3 = r3; ctx.r4 = ctx.r1; ctx.r5 = r5; original(ctx, base); }; if (isRepeatLeft || isRepeatRight) { auto isFlipHorz = (modifier.Flags & CSD_REPEAT_FLIP_HORIZONTAL) != 0; auto isFlipVert = (modifier.Flags & CSD_REPEAT_FLIP_VERTICAL) != 0; auto applyRepeatModifiers = [&]() { if (isFlipHorz) flipHorizontally(); if (isFlipVert) flipVertically(); if ((modifier.Flags & CSD_REPEAT_UV_MODIFIER) != 0) applyUVModifier(modifier.RepeatUVs); if ((modifier.Flags & CSD_REPEAT_COLOUR_MODIFIER) != 0) applyColourModifier(modifier.RepeatColours); }; if (isRepeatLeft) { auto x = getVertex(2)->X; auto arrowIndex = 0; auto arrowCount = 0; auto arrowWidth = (width - (width / 2.5f)); if ((modifier.Flags & CSD_CHEVRON) != 0) { // Shift root arrow forwards to use entirely custom arrows. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X + arrowWidth; // Compute number of foreground arrows. while (x > 0.0f) { x = x - arrowWidth; arrowCount++; } g_fgArrowsEnd = (CHEVRON_INTRO_DURATION / 2.0f) * float(arrowCount + 1); // Reset X to first vertex. x = getVertex(0)->X; } while (x > 0.0f) { drawOriginal(); if ((modifier.Flags & CSD_CHEVRON) != 0) { /////////////////////////////////////////////////////////// // Clone foreground arrows to recreate chevron animation // /////////////////////////////////////////////////////////// // These values are based off the animation in background.xncp. auto endAlpha = (uint8_t)std::lerp(30, 10, x / vpWidth); auto introDuration = CHEVRON_INTRO_DURATION; auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1) + g_bgArrowsEnd; auto outroDuration = CHEVRON_OUTRO_DURATION; auto outroStartTime = (g_fgArrowsEnd + introStartTime) + 0.5f; // This will be set by the last foreground arrow in the // sequence, therefore making it the loop end point. g_chevronLoopTime = outroStartTime + outroDuration; // Store current arrow animation progress. auto& arrow = g_fgArrows[arrowIndex]; arrow.EndAlpha = endAlpha; arrow.IntroDuration = introDuration; arrow.IntroStartTime = introStartTime; arrow.OutroDuration = outroDuration; arrow.OutroStartTime = outroStartTime; // Animate arrow position and alpha. for (size_t i = 0; i < r5.u32; i++) { getVertex(i)->X = (getVertex(i)->X - arrowWidth) - g_bgArrows[arrowIndex].CurrentOffsetX; getVertex(i)->Colour = 0xFFFFFF00 | g_fgArrows[arrowIndex].CurrentAlpha; } arrowIndex++; } else { // Move cloned cast to left edge of existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X - width; } // Update loop condition. x = getVertex(2)->X; applyRepeatModifiers(); if ((modifier.Flags & CSD_REPEAT_EXTEND) != 0) { for (size_t i = 0; i < r5.u32; i++) { if (i == (isFlipHorz ? 2 : 0) || i == (isFlipHorz ? 3 : 1)) getVertex(i)->X = std::min(getVertex(i)->X.get(), 0.0f); } } } } if (isRepeatRight) { auto x = getVertex(0)->X; auto arrowIndex = 0; auto arrowCount = 0; auto arrowWidth = (width - (width / 4.0f)); if ((modifier.Flags & CSD_CHEVRON) != 0) { // Shift root arrow backwards to use entirely custom arrows. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X - arrowWidth; // Compute number of background arrows. while (x < vpWidth) { x = x + arrowWidth; arrowCount++; } g_bgArrowsEnd = (CHEVRON_INTRO_DURATION / 2.0f) * float(arrowCount + 1); // Reset X to first vertex. x = getVertex(0)->X; } while (x < vpWidth) { drawOriginal(); if ((modifier.Flags & CSD_CHEVRON) != 0) { /////////////////////////////////////////////////////////// // Clone background arrows to recreate chevron animation // /////////////////////////////////////////////////////////// // These values are based off the animation in background.xncp. auto endAlpha = (uint8_t)std::lerp(10, 35, x / vpWidth); auto introDuration = CHEVRON_INTRO_DURATION; auto introStartTime = (introDuration / 2.0f) * float(arrowIndex + 1); auto outroDuration = CHEVRON_OUTRO_DURATION; auto outroStartTime = (g_bgArrowsEnd + introStartTime) + 0.5f; // Store current arrow animation progress. auto& arrow = g_bgArrows[arrowIndex]; arrow.EndAlpha = endAlpha; arrow.IntroDuration = introDuration; arrow.IntroStartTime = introStartTime; arrow.OutroDuration = outroDuration; arrow.OutroStartTime = outroStartTime; // Animate arrow position and alpha. for (size_t i = 0; i < r5.u32; i++) { getVertex(i)->X = (getVertex(i)->X + arrowWidth) + g_bgArrows[arrowIndex].CurrentOffsetX; getVertex(i)->Colour = 0xFFFFFF00 | g_bgArrows[arrowIndex].CurrentAlpha; } arrowIndex++; } else { // Move cloned cast to right edge of existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X + width; } // Update loop condition. x = getVertex(0)->X; applyRepeatModifiers(); // Extend to the right of the screen. if ((modifier.Flags & CSD_REPEAT_EXTEND) != 0) { for (size_t i = 0; i < r5.u32; i++) { if (i == (isFlipHorz ? 0 : 2) || i == (isFlipHorz ? 1 : 3)) getVertex(i)->X = std::max(getVertex(i)->X.get(), vpWidth); } } } } } else { if (!isMainMenuPanels) drawOriginal(); // Clone Audio Room "pod" left edge to fill // in the rest of the box at ultrawide. if ((modifier.Flags & CSD_POD_CLONE) != 0 && g_aspectRatio > WIDE_ASPECT_RATIO) { auto v0 = getVertex(0); auto v1 = getVertex(1); auto v2 = getVertex(2); auto v3 = getVertex(3); // Shift cloned element to base edge. v0->X = g_podBaseRightX; v1->X = g_podBaseRightX; // Stretch cloned element to screen edge. v2->X = vpWidth; v3->X = vpWidth; // Shift UVs to remove cloned element edge line. v0->U = v0->U + 0.008f; v0->V = v0->V + 0.008f; v1->U = v1->U + 0.008f; v1->V = v1->V + 0.008f; drawOriginal(); } // Don't adjust CRI logo at 16:9 or if the alignment mode is centre at ultrawide. if (g_aspectRatio >= WIDE_ASPECT_RATIO && Config::UIAlignmentMode == EUIAlignmentMode::Centre) modifier.Flags &= ~CSD_CRI_LOGO; // Clone CRI logo to move the technology // logos to the true bottom right corner. if ((modifier.Flags & CSD_CRI_LOGO) != 0) { auto v0 = getVertex(0); auto v1 = getVertex(1); auto v2 = getVertex(2); auto v3 = getVertex(3); auto originalTop = v0->Y; auto originalHeight = v1->Y - v0->Y; auto width = 422.0f; auto height = 132.0f; auto widthScaled = width * g_aspectRatioScale; auto heightScaled = height * g_aspectRatioScale; ///////////////////////////////////////////////////////////////////////// // Clone and scale white square to block the original technology logos // ///////////////////////////////////////////////////////////////////////// auto top = originalTop + (originalHeight - heightScaled); auto left = g_aspectRatio >= WIDE_ASPECT_RATIO ? 0.0f : vpWidth - widthScaled; // Top Left v0->X = left; v0->Y = top; v0->U = 0.0f; v0->V = 0.01f; // Bottom Left v1->X = left; v1->Y = vpHeight; v1->U = 0.0f; v1->V = 0.01f; // Top Right v2->X = vpWidth; v2->Y = top; v2->U = 0.0f; v2->V = 0.01f; // Bottom Right v3->X = vpWidth; v3->Y = vpHeight; v3->U = 0.0f; v3->V = 0.01f; drawOriginal(); ///////////////////////////////////////////// // Clone and scale to fit technology logos // ///////////////////////////////////////////// v0 = getVertex(0); v1 = getVertex(1); v2 = getVertex(2); v3 = getVertex(3); auto uv = PIXELS_TO_UV_COORDS(2048, 512, 858, 380, width, height); auto& min = std::get<0>(uv); auto& max = std::get<1>(uv); // Top Left v0->X = vpWidth - widthScaled; v0->Y = vpHeight - heightScaled; v0->U = min.x; v0->V = min.y; // Bottom Left v1->X = vpWidth - widthScaled; v1->Y = vpHeight; v1->U = min.x; v1->V = max.y; // Top Right v2->X = vpWidth; v2->Y = vpHeight - heightScaled; v2->U = max.x; v2->V = min.y; // Bottom Right v3->X = vpWidth; v3->Y = vpHeight; v3->U = max.x; v3->V = max.y; drawOriginal(); } } if (g_aspectRatio > WIDE_ASPECT_RATIO) { if (isMainMenuPanels) drawOriginal(); ///////////////////////////////////////////////// // Scale metal borders to wide aspect ratios // ///////////////////////////////////////////////// if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0222) != 0) { // Move cloned cast to left edge of existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X - width; flipHorizontally(); // Extend to the left of the screen. getVertex(2)->X = 0.0f; getVertex(3)->X = 0.0f; // Crop UVs to remove metal notch. getVertex(0)->U = getVertex(0)->U + 0.0015f; getVertex(1)->U = getVertex(1)->U + 0.0015f; getVertex(2)->U = getVertex(2)->U + -0.1f; getVertex(3)->U = getVertex(3)->U + -0.1f; drawOriginal(); } if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0226) != 0) { // Move cloned cast to left edge of existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X - width; flipHorizontally(); // Extend to the left of the screen. getVertex(2)->X = 0.0f; getVertex(3)->X = 0.0f; // Crop UVs to remove metal dip. getVertex(2)->U = getVertex(2)->U + -0.8f; getVertex(3)->U = getVertex(3)->U + -0.8f; drawOriginal(); } if ((modifier.Flags & CSD_MAIN_MENU_PARTS_CAST_0227) != 0) { // Move cloned cast to right edge of existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->X = getVertex(i)->X + width; flipHorizontally(); // Extend to the right of the screen. getVertex(0)->X = vpWidth; getVertex(1)->X = vpWidth; // Crop UVs to remove metal dip. getVertex(0)->U = getVertex(0)->U + -0.8f; getVertex(1)->U = getVertex(1)->U + -0.8f; drawOriginal(); } } else if (g_aspectRatio < WIDE_ASPECT_RATIO) { ///////////////////////////////////////////////// // Scale metal borders to narrow aspect ratios // ///////////////////////////////////////////////// if ((modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0221 | CSD_MAIN_MENU_PARTS_CAST_0222)) != 0) { // Move vertices for UV adjustment. getVertex(0)->Y = getVertex(0)->Y + 7.0f; getVertex(2)->Y = getVertex(2)->Y + 7.0f; // Crop top UVs to move panel top to vertex edge. getVertex(0)->V = getVertex(0)->V + 0.00675f; getVertex(2)->V = getVertex(2)->V + 0.00675f; drawOriginal(); // Move cloned cast above existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->Y = getVertex(i)->Y - height; flipVertically(); // Crop top UVs to reduce pattern repetition. getVertex(0)->V = getVertex(0)->V + 0.005f; getVertex(2)->V = getVertex(2)->V + 0.005f; drawOriginal(); } if ((modifier.Flags & (CSD_MAIN_MENU_PARTS_CAST_0226 | CSD_MAIN_MENU_PARTS_CAST_0227)) != 0) { // Move vertices for UV adjustment. getVertex(1)->Y = getVertex(1)->Y - 20.0f; getVertex(3)->Y = getVertex(3)->Y - 20.0f; // Crop bottom UVs to move panel bottom to vertex edge. getVertex(1)->V = getVertex(1)->V + -0.0168; getVertex(3)->V = getVertex(3)->V + -0.0168; drawOriginal(); // Move cloned cast below existing cast. for (size_t i = 0; i < r5.u32; i++) getVertex(i)->Y = getVertex(i)->Y + height; flipVertically(); // Crop bottom UVs to reduce pattern repetition. getVertex(1)->V = getVertex(1)->V + -0.005f; getVertex(3)->V = getVertex(3)->V + -0.005f; drawOriginal(); } } else { if (isMainMenuPanels) drawOriginal(); } ctx.r1.u32 += size; } // Sonicteam::CPlatformMarathon::Draw PPC_FUNC_IMPL(__imp__sub_826315C8); PPC_FUNC(sub_826315C8) { // r3 = Sonicteam::CPlatformMarathon* // r4 = Vertex[r5] // r5 = Vertex Count Draw(ctx, base, __imp__sub_826315C8, 0x14); } // Sonicteam::CPlatformMarathon::DrawNoTex PPC_FUNC_IMPL(__imp__sub_82631718); PPC_FUNC(sub_82631718) { // r3 = Sonicteam::CPlatformMarathon* // r4 = Vertex[r5] // r5 = Vertex Count Draw(ctx, base, __imp__sub_82631718, 0x0C); } // Set CSD scale. PPC_FUNC_IMPL(__imp__sub_828C78E0); PPC_FUNC(sub_828C78E0) { ctx.f1.f64 = 1280.0; ctx.f2.f64 = 720.0; __imp__sub_828C78E0(ctx, base); } // Sonicteam::EventEntityTask::Update PPC_FUNC_IMPL(__imp__sub_8264AC48); PPC_FUNC(sub_8264AC48) { if (Config::CutsceneAspectRatio == ECutsceneAspectRatio::Original) BlackBar::Show(); __imp__sub_8264AC48(ctx, base); } // Sonicteam::HUDResult::Update PPC_FUNC_IMPL(__imp__sub_824F4D80); PPC_FUNC(sub_824F4D80) { BlackBar::Show(); __imp__sub_824F4D80(ctx, base); } PPC_FUNC_IMPL(__imp__sub_824D32C8); PPC_FUNC(sub_824D32C8) { auto vftable = PPC_LOAD_U32(ctx.r3.u32); // Sonicteam::HUDBattleResult if (vftable == 0x820363E0) BlackBar::Show(); __imp__sub_824D32C8(ctx, base); } // Sonicteam::EndingMode::Update PPC_FUNC_IMPL(__imp__sub_824A4A40); PPC_FUNC(sub_824A4A40) { BlackBar::Show(); __imp__sub_824A4A40(ctx, base); } // Sonicteam::HUDLimitTime::ProcessMessage PPC_FUNC_IMPL(__imp__sub_824D6E50); PPC_FUNC(sub_824D6E50) { if (g_aspectRatio < WIDE_ASPECT_RATIO && Config::UIAlignmentMode == EUIAlignmentMode::Centre) { __imp__sub_824D6E50(ctx, base); return; } auto pHUDLimitTime = (Sonicteam::HUDLimitTime*)(base + ctx.r3.u32); pHUDLimitTime->m_Y = 64.0f - (g_aspectRatioOffsetY / g_aspectRatioScale); GuestToHostFunction(sub_824D6D38, pHUDLimitTime, (double)pHUDLimitTime->m_X, (double)pHUDLimitTime->m_Y); __imp__sub_824D6E50(ctx, base); } // Sonicteam::HUDRaderMap::Update PPC_FUNC_IMPL(__imp__sub_824F1538); PPC_FUNC(sub_824F1538) { auto pHUDRaderMap = (Sonicteam::HUDRaderMap*)(base + ctx.r3.u32); __imp__sub_824F1538(ctx, base); pHUDRaderMap->m_pMaskTexture->m_Width = g_radarMapScale; pHUDRaderMap->m_pMaskTexture->m_Height = g_radarMapScale; pHUDRaderMap->m_X = g_radarMapX - g_radarMapCoverWidth / 2; pHUDRaderMap->m_Y = g_radarMapY - g_radarMapCoverHeight / 2; } // Sonicteam::CObjBalloonIconDrawable::Draw PPC_FUNC_IMPL(__imp__sub_82352220); PPC_FUNC(sub_82352220) { auto pCObjBalloonIconDrawable = (Sonicteam::CObjBalloonIconDrawable*)(base + ctx.r3.u32); auto scale = g_aspectRatioScale; if (g_aspectRatio > WIDE_ASPECT_RATIO) scale = g_aspectRatio / WIDE_ASPECT_RATIO; pCObjBalloonIconDrawable->m_aVertices[0].X = -1.0f / scale; pCObjBalloonIconDrawable->m_aVertices[0].Y = 0.0f; pCObjBalloonIconDrawable->m_aVertices[1].X = -1.0f / scale; pCObjBalloonIconDrawable->m_aVertices[1].Y = g_aspectRatio / scale; pCObjBalloonIconDrawable->m_aVertices[2].X = 1.0f / scale; pCObjBalloonIconDrawable->m_aVertices[2].Y = 0.0f; pCObjBalloonIconDrawable->m_aVertices[3].X = 1.0f / scale; pCObjBalloonIconDrawable->m_aVertices[3].Y = g_aspectRatio / scale; __imp__sub_82352220(ctx, base); } // Sonicteam::MovieObjectWmv::Draw PPC_FUNC_IMPL(__imp__sub_8264CC90); PPC_FUNC(sub_8264CC90) { auto pMovieObjectWmv = (Sonicteam::MovieObjectWmv*)(base + ctx.r3.u32); static XXH64_hash_t s_movieNameHash{}; static bool s_movieUsesCustomDimensions{}; static float s_movieLeft{}; static float s_movieRight{}; static float s_movieTop{}; static float s_movieBottom{}; auto movieNameHash = HashStr(pMovieObjectWmv->m_FilePath.c_str()); if (movieNameHash != s_movieNameHash) { s_movieNameHash = movieNameHash; s_movieUsesCustomDimensions = pMovieObjectWmv->m_UseCustomDimensions; s_movieLeft = pMovieObjectWmv->m_Left; s_movieRight = pMovieObjectWmv->m_Right; s_movieTop = pMovieObjectWmv->m_Top; s_movieBottom = pMovieObjectWmv->m_Bottom; LOGFN_UTILITY("Movie: {} - {}x{}", pMovieObjectWmv->m_FilePath.c_str(), pMovieObjectWmv->m_Width.get(), pMovieObjectWmv->m_Height.get()); } auto movieModifier = FindHash(g_movieModifiers, movieNameHash); g_aspectRatioMovie = (float)pMovieObjectWmv->m_Width / (float)pMovieObjectWmv->m_Height; float width, height, left, right, top, bottom; if (s_movieUsesCustomDimensions) { auto movieRectLeft = s_movieLeft; auto movieRectRight = s_movieRight; auto movieRectTop = s_movieTop; auto movieRectBottom = s_movieBottom; if (g_aspectRatio > g_aspectRatioMovie) { // Scale width at wide aspect ratios. movieRectLeft = s_movieLeft / (g_aspectRatio / g_aspectRatioMovie); movieRectRight = s_movieRight / (g_aspectRatio / g_aspectRatioMovie); } else { // Scale height at narrow aspect ratios. movieRectTop = s_movieTop / (g_aspectRatioMovie / g_aspectRatio); movieRectBottom = s_movieBottom / (g_aspectRatioMovie / g_aspectRatio); } auto movieRectCentreX = (movieRectLeft + movieRectRight) / 2.0f; auto movieRectCentreY = (movieRectTop + movieRectBottom) / 2.0f; width = movieRectRight - movieRectLeft; height = movieRectTop - movieRectBottom; left = movieRectCentreX - (width / 2.0f); right = movieRectCentreX + (width / 2.0f); top = movieRectCentreY + (height / 2.0f); bottom = movieRectCentreY - (height / 2.0f); } else { width = 2.0f; height = 2.0f; if (g_aspectRatio > g_aspectRatioMovie) { if ((movieModifier.Flags & MOVIE_CROP_WIDE) != 0) { // Crop vertically at wide aspect ratios. height = 2.0f * (g_aspectRatio / g_aspectRatioMovie); } else { // Pillarbox at wide aspect ratios. width = 2.0f * (g_aspectRatioMovie / g_aspectRatio); } } else { if ((movieModifier.Flags & MOVIE_CROP_NARROW) != 0) { // Crop horizontally at narrow aspect ratios. width = 2.0f * (g_aspectRatioMovie / g_aspectRatio); } else { // Letterbox at narrow aspect ratios. height = 2.0f * (g_aspectRatio / g_aspectRatioMovie); } } left = -width / 2.0f; right = (width / 2.0f) * 2.0f; top = height / 2.0f; bottom = (-height / 2.0f) * 2.0f; } pMovieObjectWmv->m_UseCustomDimensions = true; pMovieObjectWmv->m_Left = left; pMovieObjectWmv->m_Right = right; pMovieObjectWmv->m_Top = top; pMovieObjectWmv->m_Bottom = bottom; __imp__sub_8264CC90(ctx, base); } void ReplaceTextVariables(Sonicteam::TextEntity* pTextEntity) { if (!pTextEntity || !pTextEntity->m_ImageVertexCount) return; auto variables = MapTextVariables(pTextEntity->m_pVariables); auto variablesIndex = 0; auto pictureIndex = 0; for (int i = 0; i < pTextEntity->m_Text.size(); i++) { auto str = pTextEntity->m_Text.c_str(); auto c = ByteSwap(str[i]); if (c != L'$') continue; // Some strings may have more placeholder // characters than variables in the map. if (variablesIndex >= variables.size()) break; auto& variable = variables[variablesIndex]; if (variable.first == "picture") { auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; if (Config::ControllerIcons == EControllerIcons::Auto) isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; auto& pftModifier = isPlayStation ? g_buttonCropsPS3 : g_buttonCropsXenon; auto hash = HashStr(variable.second); auto baseParams = FindHash(g_buttonCropsXenon, hash); auto newParams = FindHash(pftModifier, hash); auto uv = PIXELS_TO_UV_COORDS(g_fontPictureWidth, g_fontPictureHeight, newParams.X, newParams.Y, newParams.Width, newParams.Height); auto& min = std::get<0>(uv); auto& max = std::get<1>(uv); auto vi = pictureIndex * 6; auto& v0 = pTextEntity->m_pImageVertices[vi + 0]; auto& v1 = pTextEntity->m_pImageVertices[vi + 1]; auto& v2 = pTextEntity->m_pImageVertices[vi + 2]; auto& v3 = pTextEntity->m_pImageVertices[vi + 3]; auto& v4 = pTextEntity->m_pImageVertices[vi + 4]; auto& v5 = pTextEntity->m_pImageVertices[vi + 5]; auto centreX = (v0.X + v2.X) / 2.0f; auto widthAdjust = (float)newParams.Width / (float)baseParams.Width; auto adjust = [&](auto& vertex) { vertex.X = centreX + (vertex.X - centreX) * widthAdjust; }; // Bottom Left (Triangle 1) adjust(v0); v0.U = min.x; v0.V = min.y; // Top Left (Triangle 1) adjust(v1); v1.U = min.x; v1.V = max.y; // Top Right (Triangle 1) adjust(v2); v2.U = max.x; v2.V = max.y; // Bottom Left (Triangle 2) adjust(v3); v3.U = min.x; v3.V = min.y; // Top Right (Triangle 2) adjust(v4); v4.U = max.x; v4.V = max.y; // Bottom Right (Triangle 2) adjust(v5); v5.U = max.x; v5.V = min.y; pictureIndex++; } variablesIndex++; } } void TextEntityAlloc(PPCRegister& r3) { r3.u32 += sizeof(uint64_t); } // Sonicteam::TextEntity::TextEntity PPC_FUNC_IMPL(__imp__sub_8262DC60); PPC_FUNC(sub_8262DC60) { auto pTextModifier = (uint64_t*)(base + ctx.r3.u32 + sizeof(Sonicteam::TextEntity)); // Default text modifier to scale. *pTextModifier = CSD_SCALE; __imp__sub_8262DC60(ctx, base); } // Sonicteam::TextEntity::Draw PPC_FUNC_IMPL(__imp__sub_8262D868); PPC_FUNC(sub_8262D868) { auto pTextEntity = (Sonicteam::TextEntity*)(base + ctx.r3.u32); auto textModifier = *(uint64_t*)(base + ctx.r3.u32 + sizeof(Sonicteam::TextEntity)); if ((textModifier & CSD_SKIP) != 0) return; auto x = pTextEntity->m_X; auto y = pTextEntity->m_Y; auto scaleX = pTextEntity->m_ScaleX; auto scaleY = pTextEntity->m_ScaleY; auto offsetX = 0.0f; auto offsetY = 0.0f; auto scale = g_aspectRatioScale; if (Config::UIAlignmentMode == EUIAlignmentMode::Centre || BlackBar::IsVisible()) { if (g_aspectRatio > WIDE_ASPECT_RATIO) { textModifier &= ~(CSD_ALIGN_LEFT | CSD_ALIGN_RIGHT); } else if (g_aspectRatio < WIDE_ASPECT_RATIO) { textModifier &= ~(CSD_ALIGN_TOP | CSD_ALIGN_BOTTOM); } } if ((textModifier & CSD_ALIGN_RIGHT) != 0) offsetX = g_aspectRatioOffsetX * 2.0f; else if ((textModifier & CSD_ALIGN_LEFT) == 0) offsetX = g_aspectRatioOffsetX; if ((textModifier & CSD_ALIGN_BOTTOM) != 0) offsetY = g_aspectRatioOffsetY * 2.0f; else if ((textModifier & CSD_ALIGN_TOP) == 0) offsetY = g_aspectRatioOffsetY; if ((textModifier & CSD_SCALE) != 0) { scale *= g_aspectRatioGameplayScale; if ((textModifier & CSD_ALIGN_RIGHT) != 0) offsetX += 1280.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; else if ((textModifier & CSD_ALIGN_LEFT) == 0) offsetX += 640.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; if ((textModifier & CSD_ALIGN_BOTTOM) != 0) offsetY += 720.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; else if ((textModifier & CSD_ALIGN_TOP) == 0) offsetY += 360.0f * (1.0f - g_aspectRatioGameplayScale) * g_aspectRatioScale; } pTextEntity->m_X = offsetX + pTextEntity->m_X * scale; pTextEntity->m_Y = offsetY + pTextEntity->m_Y * scale; pTextEntity->m_ScaleX = scale; pTextEntity->m_ScaleY = scale; __imp__sub_8262D868(ctx, base); pTextEntity->m_X = x; pTextEntity->m_Y = y; pTextEntity->m_ScaleX = scaleX; pTextEntity->m_ScaleY = scaleY; ReplaceTextVariables(pTextEntity); } void SetTextEntityModifier(Sonicteam::TextEntity* pTextEntity, uint64_t flags) { if (!pTextEntity) return; auto pTextModifier = (uint64_t*)(reinterpret_cast(pTextEntity) + sizeof(Sonicteam::TextEntity)); *pTextModifier = flags; pTextEntity->m_FieldDD = true; pTextEntity->Update(); } // Sonicteam::HUDMainMenu::Destroy PPC_FUNC_IMPL(__imp__sub_824E2978); PPC_FUNC(sub_824E2978) { g_bgArrows.clear(); g_fgArrows.clear(); __imp__sub_824E2978(ctx, base); } // Sonicteam::HUDMainMenu::Update PPC_FUNC_IMPL(__imp__sub_824E11D0); PPC_FUNC(sub_824E11D0) { auto pHUDMainMenu = (Sonicteam::HUDMainMenu*)(base + ctx.r3.u32 - 8); auto pHudTextRoot = pHUDMainMenu->m_pHudTextRoot; static bool s_preservedTextParams{}; static float s_multiplayerTextOffsetY{}; static float s_tagTextOffsetY{}; static float s_battleTextOffsetY{}; static constexpr double HIDE_TEXT_OFFSET = -100000.0f; auto isTrialSelect = MainMenuTaskPatches::s_state >= 12 && MainMenuTaskPatches::s_state <= 15; auto isTag = MainMenuTaskPatches::s_state == Sonicteam::MainMenuTask::MainMenuState_Tag || MainMenuTaskPatches::s_state == Sonicteam::MainMenuTask::MainMenuState_Tag1PSelect || MainMenuTaskPatches::s_state == Sonicteam::MainMenuTask::MainMenuState_ExitToStage; while (pHudTextRoot) { if (pHudTextRoot->m_SceneName == "main_menu") { if (pHudTextRoot->m_CastName == "multi_player") { if (!s_preservedTextParams) s_multiplayerTextOffsetY = pHudTextRoot->m_OffsetY; pHudTextRoot->m_OffsetY = isTrialSelect ? HIDE_TEXT_OFFSET : s_multiplayerTextOffsetY; } else if (pHudTextRoot->m_CastName == "tag") { if (!s_preservedTextParams) s_tagTextOffsetY = pHudTextRoot->m_OffsetY; pHudTextRoot->m_OffsetY = (isTrialSelect || isTag) ? HIDE_TEXT_OFFSET : s_tagTextOffsetY; } else if (pHudTextRoot->m_CastName == "battle") { if (!s_preservedTextParams) s_battleTextOffsetY = pHudTextRoot->m_OffsetY; pHudTextRoot->m_OffsetY = isTrialSelect ? HIDE_TEXT_OFFSET : s_battleTextOffsetY; } } pHudTextRoot = pHudTextRoot->m_pNext; } s_preservedTextParams = true; // Draw faded letterbox at tall aspect ratios. if (!OptionsMenu::s_isVisible && g_aspectRatio < NARROW_ASPECT_RATIO) { BlackBar::Show(true); BlackBar::SetBorderMargin(Scale(BlackBar::ms_MenuBorderMargin, true)); } __imp__sub_824E11D0(ctx, base); } // Sonicteam::HUDMainDisplay::Update PPC_FUNC_IMPL(__imp__sub_824DCF40); PPC_FUNC(sub_824DCF40) { auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)(base + ctx.r3.u32); SetTextEntityModifier(pHUDMainDisplay->m_Field184.get(), CSD_ALIGN_RIGHT | CSD_SCALE); SetTextEntityModifier(pHUDMainDisplay->m_spTrickPointText.get(), CSD_ALIGN_RIGHT | CSD_SCALE); SetTextEntityModifier(pHUDMainDisplay->m_Field194.get(), CSD_ALIGN_RIGHT | CSD_SCALE); SetTextEntityModifier(pHUDMainDisplay->m_spSavePointTimeText.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); __imp__sub_824DCF40(ctx, base); } // Sonicteam::HintWindowTask::Update PPC_FUNC_IMPL(__imp__sub_824D12F0); PPC_FUNC(sub_824D12F0) { auto pHintWindowTask = (Sonicteam::HintWindowTask*)(base + ctx.r3.u32); SetTextEntityModifier(pHintWindowTask->m_Field8C.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); SetTextEntityModifier(pHintWindowTask->m_Field94.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); __imp__sub_824D12F0(ctx, base); } // Sonicteam::MessageWindowTask::Update PPC_FUNC_IMPL(__imp__sub_82507E68); PPC_FUNC(sub_82507E68) { auto pMessageWindowTask = (Sonicteam::MessageWindowTask*)(base + ctx.r3.u32); SetTextEntityModifier(pMessageWindowTask->m_Field90.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); SetTextEntityModifier(pMessageWindowTask->m_Field98.get(), CSD_ALIGN_BOTTOM | CSD_SCALE); __imp__sub_82507E68(ctx, base); } // Sonicteam::TextFontPicture::LoadResource PPC_FUNC_IMPL(__imp__sub_8263CC40); PPC_FUNC(sub_8263CC40) { auto pTextFontPicture = (Sonicteam::TextFontPicture*)(base + ctx.r3.u32); __imp__sub_8263CC40(ctx, base); g_fontPictureWidth = pTextFontPicture->m_TextureWidth; g_fontPictureHeight = pTextFontPicture->m_TextureHeight; } // -------------- CSD MODIFIERS --------------- // const xxHashMap g_csdModifiers = { // audio { HashStr("sprite/audio/audio/pod/pod"), { CSD_SCALE } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1084"), { CSD_POD_BASE | CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1085"), { CSD_SCALE } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1086"), { CSD_POD_CLONE | CSD_SCALE } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1087"), { CSD_SCALE } }, { HashStr("sprite/audio/audio/pod/pod/Cast_1088"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/audio/audio/genre"), { CSD_SCALE } }, { HashStr("sprite/audio/audio/genre_cursor"), { CSD_SCALE } }, { HashStr("sprite/audio/audio/audio_cursor"), { CSD_SCALE } }, { HashStr("sprite/audio/audio/audio_cursor2"), { CSD_SCALE } }, // background { HashStr("sprite/background/background/mainmenu_back"), { CSD_MODIFIER_ULTRAWIDE_ONLY | CSD_STRETCH_HORIZONTAL | CSD_PROHIBIT_BLACK_BAR } }, { HashStr("sprite/background/background/tag"), { CSD_SCALE } }, { HashStr("sprite/background/background/movie"), { CSD_SCALE } }, { HashStr("sprite/background/background/main_menu"), { CSD_SCENE_DISABLE_MOTION | CSD_MODIFIER_ULTRAWIDE_ONLY } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji1"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji2"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji3"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji4"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji5"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu1/yaji6"), { CSD_CHEVRON | CSD_SCALE | CSD_ALIGN_RIGHT | CSD_REPEAT_LEFT } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji7"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji8"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji9"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji10"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji11"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji12"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji13"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji14"), { CSD_CHEVRON | CSD_SCALE | CSD_SKIP } }, { HashStr("sprite/background/background/main_menu/main_menu2/yaji15"), { CSD_CHEVRON | CSD_SCALE | CSD_ALIGN_LEFT | CSD_REPEAT_RIGHT } }, { HashStr("sprite/background/background/cd"), { CSD_SCALE } }, { HashStr("sprite/background/background/battle"), { CSD_SCALE } }, { HashStr("sprite/background/background/fileselect"), { CSD_SCALE } }, // battledisplay_1p { HashStr("sprite/battledisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/enemy"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/bronze_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/gold_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_1p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // battledisplay_2p { HashStr("sprite/battledisplay_2p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/enemy"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/bronze_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/gold_medal"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/battledisplay_2p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // battle_result { HashStr("sprite/battle_result/battle_result/congratulations"), { CSD_ALIGN_TOP } }, { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate1"), { CSD_ALIGN_TOP | CSD_EXTEND_LEFT } }, { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate2"), { CSD_ALIGN_TOP } }, { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate3"), { CSD_ALIGN_TOP } }, { HashStr("sprite/battle_result/battle_result/plate/plate_ue/plate4"), { CSD_ALIGN_TOP | CSD_EXTEND_RIGHT } }, { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate5"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_LEFT } }, { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate6"), { CSD_ALIGN_BOTTOM } }, { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate7"), { CSD_ALIGN_BOTTOM } }, { HashStr("sprite/battle_result/battle_result/plate/plate_sita/plate8"), { CSD_ALIGN_BOTTOM | CSD_EXTEND_RIGHT } }, // black_out { HashStr("sprite/black_out/black_out"), { CSD_STRETCH } }, // bossname { HashStr("sprite/bossname/egg_cerberus/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1071/egg_cerberus/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1081/egg_cerberus/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/egg_genesis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1091/egg_genesis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1111/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1111/egg_wyvern/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1001/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1011/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1031/iblis/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/mephiles/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1041/mephiles/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1061/mephiles/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/shadow_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1161/shadow_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/silver_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1151/silver_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/solaris/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1121/solaris/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/bossname/sonic_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("event/e1141/sonic_the_hedgehog/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // button_window { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0001"), { CSD_BUTTON_WINDOW | CSD_SCALE } }, { HashStr("sprite/button_window/button_window/Scene_0000/Null_0000/Cast_0002"), { CSD_BUTTON_WINDOW | CSD_SCALE | CSD_EXTEND_RIGHT } }, // cri_logo { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/bg"), { CSD_STRETCH } }, { HashStr("sprite/logo/cri_logo/Scene_0000/Null_0002/criware"), { CSD_CRI_LOGO | CSD_SCALE } }, // gadget_ber { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar"), { CSD_ALIGN_BOTTOM_RIGHT } }, { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_anime"), { CSD_ALIGN_BOTTOM_RIGHT } }, { HashStr("sprite/gadget_ber/gadget_bar/gadgetbar_ue"), { CSD_ALIGN_BOTTOM_RIGHT } }, { HashStr("sprite/gadget_ber/gadget_bar/icon_text"), { CSD_ALIGN_BOTTOM_RIGHT } }, // goldmedal { HashStr("sprite/goldmedal/goldmedal/ranking"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/goldmedal"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/charaselect_cursor2"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/board_cursor_sita"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/board_cursor_ue"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/total_medal"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/sonic"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/shadow"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/silver"), { CSD_SCALE } }, { HashStr("sprite/goldmedal/goldmedal/last"), { CSD_SCALE } }, // tips_sh_x { HashStr("sprite/interlude/tips_x/tips_sh_x/Scene_0000"), { CSD_SCALE } }, // tips_si_x { HashStr("sprite/interlude/tips_x/tips_si_x/Scene_0000"), { CSD_SCALE } }, // tips_so_x { HashStr("sprite/interlude/tips_x/tips_so_x/Scene_0000"), { CSD_SCALE } }, // loading { HashStr("sprite/loading/loading/Scene_0000/Loading"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, { HashStr("sprite/loading/loading/Scene_0000/Loading_02"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, { HashStr("sprite/loading/loading/Scene_0000/arrow_01"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, { HashStr("sprite/loading/loading/Scene_0000/arrow_02"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, { HashStr("sprite/loading/loading/Scene_0000/arrow_03"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, // maindisplay { HashStr("sprite/maindisplay/power"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/custom_bar_anime"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/power_a"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/power_bar_anime"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/itembox_01"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, { HashStr("sprite/maindisplay/score"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/life_ber_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/life"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/time"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/ring"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/ring_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/bar_ue"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/power_bar_effect"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/ring_000_effect"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/boss_gauge"), { CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/boss_gauge_anime"), { CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/item_ber_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/item"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/maindisplay/custom_gem"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/maindisplay/custom_level"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // main_menu { HashStr("sprite/main_menu/main_menu_cursor"), { CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_cursor2"), { CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_cursor3"), { CSD_SCALE } }, { HashStr("sprite/main_menu/eposodeselect"), { CSD_SCALE } }, { HashStr("sprite/main_menu/episodeselect_cursor1"), { CSD_SCALE } }, { HashStr("sprite/main_menu/episodeselect_cursor2"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita3"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita5"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita6"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita7"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita8"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/savedate/savedata/Null_1074/sita9"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/goldmedal"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/zanki_3"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/zanki_2"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/zanki"), { CSD_SCALE } }, { HashStr("sprite/main_menu/savedate/savedata/ring"), { CSD_SCALE } }, { HashStr("sprite/main_menu/character_choice"), { CSD_SCALE } }, { HashStr("sprite/main_menu/character_cursor1"), { CSD_SCALE } }, { HashStr("sprite/main_menu/character_cursor2"), { CSD_SCALE } }, { HashStr("sprite/main_menu/stage_plate/stage_plate"), { CSD_SCALE } }, { HashStr("sprite/main_menu/stage_plate/stage_plate/stage_plate2"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1337"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1338"), { CSD_SCALE } }, { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1339"), { CSD_SCALE } }, { HashStr("sprite/main_menu/stage_plate/stage_plate/Cast_1340"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/mission_plate/mission_plate"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_plate/mission_plate/mission_plate2"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1332"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1334"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1335"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_plate/mission_plate/Cast_1336"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/stage_cursor"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_cursor"), { CSD_SCALE } }, { HashStr("sprite/main_menu/stage_cursor2"), { CSD_SCALE } }, { HashStr("sprite/main_menu/text"), { CSD_SCALE } }, { HashStr("sprite/main_menu/text_cover/Null_0290/cover_l"), { CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.05f, 0, -0.05f, 0 } } }, { HashStr("sprite/main_menu/text_cover/Null_0290/cover_c"), { CSD_SCALE } }, { HashStr("sprite/main_menu/text_cover/Null_0290/vocer_r"), { CSD_SCALE | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.0015f, 0, 0.0015f, 0 }, {}, { -0.05f, 0, -0.05f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/mission_text/rank"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_text/item_icon"), { CSD_SCALE } }, { HashStr("sprite/main_menu/mission_text/m_ring_icon"), { CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0221"), { CSD_MAIN_MENU_PARTS_CAST_0221 | CSD_SCALE | CSD_EXTEND_RIGHT, { 0, 0.005f, 0, 0, 0, 0.005f, 0, 0 }, {}, { 0, 0, 0, -0.0175f, 0, 0, 0, -0.0175f } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0218/Cast_0222"), { CSD_MAIN_MENU_PARTS_CAST_0222 | CSD_SCALE | CSD_UV_MODIFIER, { 0.0015f, 0, 0.0015f, 0, 0, 0, 0, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0964"), { CSD_SCALE | CSD_EXTEND_RIGHT } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0965"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 } } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966"), { CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0960/Cast_0966/Cast_0967"), { CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0226"), { CSD_MAIN_MENU_PARTS_CAST_0226 | CSD_SCALE } }, { HashStr("sprite/main_menu/main_menu_parts/Null_0224/Cast_0227"), { CSD_MAIN_MENU_PARTS_CAST_0227 | CSD_SCALE } }, { HashStr("sprite/main_menu/titlebar_effect"), { CSD_SCALE } }, // map_twn { HashStr("sprite/interlude/map_twn/map_twn/Scene_0000"), { CSD_SCALE } }, // pausemenu { HashStr("sprite/pausemenu/pausemenu/pause_menu"), { CSD_SCALE } }, { HashStr("sprite/pausemenu/pausemenu/pause_menu_cursor"), { CSD_SCALE } }, { HashStr("sprite/pausemenu/pausemenu/mission"), { CSD_SCALE } }, // radarmap_cover { HashStr("sprite/radarmap_cover/radarmap_cover/Scene_0000"), { CSD_RADARMAP | CSD_ALIGN_TOP_RIGHT | CSD_SCALE } }, // result { HashStr("sprite/result/result/title_plate/plate_ue"), { CSD_ALIGN_TOP | CSD_SCALE } }, { HashStr("sprite/result/result/title_plate/plate_sita"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, { HashStr("sprite/result/result/title_plate/result_title/result_title_ob"), { CSD_ALIGN_TOP | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate/score_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/score/score_plate/score_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate/time_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time/time_plate/time_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plare_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plate/ring_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring/ring_plate/ring_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_plate/timebonus_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/time_bonus/time_bonus_plate/time_bonus_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate/ringbonus_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/ring_bonus/ring_bonus_plate/ring_bonus_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_t/score_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/town_score_t/score_plate_t/score_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_t/time_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_t/time_plate_t/time_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plare_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plate_t/ring_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_t/ring_plate_t/ring_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_plate_t/timebonus_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/time_bonus_t/time_bonus_plate_t/time_bonus_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_t/ringbonus_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/ring_bonus_t/ring_bonus_plate_t/ring_bonus_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_plate_kage_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_bonus_plate_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_bonus_plate_t/record_text_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/record_bonus_plate_t/record_plate_title_t"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/record_t/item_icon"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/town_mission/parts_anime_result_t/amiplate_t"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate_kage01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/list_text01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/list_text01/nyoro01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/ring01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/ring01/ring01_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/bonus_text01"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/rank_list_s_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_s/list_plate01/rank_list_s"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate_kage02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/list_text02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/list_text02/nyoro02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/ring02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/ring02/ring02_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/bonus_text02"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/rank_list_a_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_a/list_plate02/rank_list_a"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate_kage03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/list_text03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/list_text03/nyoro03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/ring03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/ring03/ring03_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/bonus_text03"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/rank_list_b_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_b/list_plate03/rank_list_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate_kage04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/list_text04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/list_text04/nyoro04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/ring04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/ring04/ring04_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/list_plate04/bonus_text04"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/rank_list_c_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_c/rank_list_c"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate_kage05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/list_text05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/list_text05/nyoro05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/ring05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/ring05/ring05_b"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/bonus_text05"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/rank_list_d_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/bonus_list/rank_d/list_plate05/rank_list_d"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate/rank_bonus_plate_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_plate/rank_bonus_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_ring"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/rank_bonus/rank_bonus_ring/rank_bonus_ring_b"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/total_ring/total_ring_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/total_ring/total_ring_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/total_ring/total_ring_plate/total_ring_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/TotalRing_Goldfont/total_ring_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/TotalRing_Goldfont/ring_l"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/bonus/parts_anime_bonus/TotalRing_Goldfont/ring_l_b"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate_kage"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate/totalscore_text"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/totalscore_rank/parts_anime/total_score/total_score_plate/total_score_plate_title"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/totalscore_rank/parts_anime/rank/rank_plate_kage"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/totalscore_rank/parts_anime/rank/rank_plate"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/totalscore_rank/parts_anime/rank/rank_plate/rank_plate_title"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/rank_anime"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/result/result/score_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/time_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/ring_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/timebonus_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, { HashStr("sprite/result/result/ringbonus_newrecord"), { CSD_ALIGN_LEFT | CSD_SCALE } }, // result WIP ultrawide adjustments // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_kage_l"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_kage_r"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, 0.015f, 0, 0.015f, 0 }, {}, { -0.6f, 0, -0.6f, 0, 0, 0, 0, 0 } } }, // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_plate_l"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, // { HashStr("sprite/result/result/title_plate/plate_ue/plate_ue_plate_r"), { CSD_ALIGN_TOP | CSD_SCALE | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.015f, 0, -0.015f, 0 }, {}, { 0.6f, 0, 0.6f, 0, 0, 0, 0, 0 } } }, // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_kage_l"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.08f, 0, -0.08f, 0 }, {}, { 0.6f, 0, 0.6f, 0, 0, 0, 0, 0 } } }, // TODO: this crashes the game. // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_kage_r"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, // TODO: this crashes the game. // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_plate_l"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_RIGHT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.015f, 0, -0.015f, 0 }, {}, { 0.6f, 0, 0.6f, 0, 0, 0, 0, 0 } } }, // TODO: this crashes the game. // { HashStr("sprite/result/result/title_plate/plate_sita/plate_sita_plate_r"), { CSD_ALIGN_BOTTOM | CSD_REPEAT_FLIP_HORIZONTAL | CSD_UV_MODIFIER | CSD_REPEAT_LEFT | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER, { 0.015f, 0, 0.015f, 0, 0, 0, 0, 0 }, {}, { 0, 0, 0, 0, -0.6f, 0, -0.6f, 0 } } }, // TODO: this crashes the game. // { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate"), { CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_FLIP_VERTICAL | CSD_UV_MODIFIER | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.025f, 0, -0.025f, 0 }, {}, { -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f } } }, // { HashStr("sprite/result/result/town_mission/parts_anime_result_t/amiplate_t/ami_plate_t"), { CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_FLIP_VERTICAL | CSD_UV_MODIFIER | CSD_REPEAT_UV_MODIFIER, { 0, 0, 0, 0, -0.025f, 0, -0.025f, 0 }, {}, { -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f, -0.01285f, 0.002f } } }, // { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate"), { CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_FLIP_VERTICAL | CSD_UV_MODIFIER | CSD_REPEAT_UV_MODIFIER, { 0.025f, 0, 0.025f, 0, 0, 0, 0, 0 }, {}, { 0.027f, 0.002f, 0.027f, 0.002f, 0.027f, 0.002f, 0.027f, 0.002f } } }, // result amiplate fade (needs special case for 16:9 or narrower) // { HashStr("sprite/result/result/result/parts_anime_result/amiplate/ami_plate"), { CSD_COLOUR_MODIFIER, {}, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFF00 }, } }, // { HashStr("sprite/result/result/town_mission/parts_anime_result_t/amiplate_t/ami_plate_t"), { CSD_COLOUR_MODIFIER, {}, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFFFF00 } } }, // { HashStr("sprite/result/result/bonus/parts_anime_bonus/amiplate/ami_plate"), { CSD_COLOUR_MODIFIER, {}, { 0xFFFFFF00, 0xFFFFFF00, 0xFFFFFFFF, 0xFFFFFFFF } } }, // sonicteam_logo { HashStr("sprite/logo/sonicteam_logo/sonicteam"), { CSD_SCALE } }, // stagetitle { HashStr("sprite/interlude/stagetitle/stage_title_aqa/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_cmn/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_csc/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_dtd/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_end/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_flc/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_kdv/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_rct/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_tpj/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_twn_a/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_twn_b/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_twn_c/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_wap/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/interlude/stagetitle/stage_title_wvo/Scene_0000"), { CSD_SCALE } }, // tag_character { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug1"), { CSD_SCALE | CSD_REPEAT_LEFT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0, 0, 0, 0, -0.5f, 0, -0.5f, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, { HashStr("sprite/tag_character/tag_character/1p_tug/1p_tug/1p_tug2"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug1"), { CSD_SCALE | CSD_REPEAT_RIGHT | CSD_REPEAT_FLIP_HORIZONTAL | CSD_REPEAT_EXTEND | CSD_REPEAT_UV_MODIFIER | CSD_REPEAT_COLOUR_MODIFIER, {}, {}, { 0.5f, 0, 0.5f, 0, 0, 0, 0, 0 }, { 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50, 0xFFFFFF50 } } }, { HashStr("sprite/tag_character/tag_character/2p_tug/2p_tug/2p_tug2"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/1p_cursor"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/2p_cursor"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/1p_name"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/2p_name"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/stage_window"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/stage"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/controller_1p"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/controller_2p"), { CSD_SCALE } }, { HashStr("sprite/tag_character/tag_character/entry"), { CSD_SCALE } }, // tagdisplay_1p { HashStr("sprite/tagdisplay_1p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/score"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/time"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/life_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/life"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/item_ber_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/item"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_1p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // tagdisplay_2p { HashStr("sprite/tagdisplay_2p/power"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/power_a"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/power_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/itembox_01"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/score"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/time"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/ring"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/ring_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/bar_ue"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/life_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/life"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/item_ber_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/item"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/power_bar_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/ring_000_effect"), { CSD_MULTIPLAYER | CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/custom_gem"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/custom_level1"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, { HashStr("sprite/tagdisplay_2p/custom_bar_anime"), { CSD_MULTIPLAYER | CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE } }, // talkwindow { HashStr("sprite/talkwindow/talkwindow/window"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, { HashStr("sprite/talkwindow/talkwindow/nafuda"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, { HashStr("sprite/talkwindow/talkwindow/Scene_0021"), { CSD_ALIGN_BOTTOM | CSD_SCALE } }, // title { HashStr("sprite/title/title/Scene_Title/Logo_add"), { CSD_SCALE } }, { HashStr("sprite/title/title/Scene_Title/Logo"), { CSD_SCALE } }, { HashStr("sprite/title/title/Scene_Title/copyright"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_MODIFIER_NARROW_ONLY } }, // titleloop_sth { HashStr("sprite/logo/titleloop_sth/Scene_0000"), { CSD_ALIGN_BOTTOM_RIGHT | CSD_SCALE | CSD_MOVIE | CSD_MODIFIER_NARROW_ONLY } }, // towndisplay { HashStr("sprite/towndisplay/ring"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, { HashStr("sprite/towndisplay/ring_anime"), { CSD_ALIGN_TOP_LEFT | CSD_SCALE } }, // trickpoint { HashStr("sprite/trickpoint/trickpoint/Scene_0000"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/trickpoint/trickpoint/Scene_0001"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, { HashStr("sprite/trickpoint/trickpoint/score"), { CSD_ALIGN_RIGHT | CSD_SCALE } }, // windowtest { HashStr("sprite/windowtest/Scene_0000"), { CSD_SCALE } }, { HashStr("sprite/windowtest/okuribotan"), { CSD_SCALE } }, { HashStr("sprite/windowtest/cursor"), { CSD_SCALE } }, }; std::optional FindCsdModifier(uint32_t data) { XXH64_hash_t path; { std::lock_guard lock(g_pathMutex); auto findResult = g_paths.find(g_memory.Translate(data)); if (findResult == g_paths.end()) return {}; path = findResult->second; } auto findResult = g_csdModifiers.find(path); if (findResult != g_csdModifiers.end()) return findResult->second; return {}; } // ------------- MOVIE MODIFIERS -------------- // const xxHashMap g_movieModifiers = { { HashStr("sound\\title_loop_GBn.wmv"), { MOVIE_CROP_NARROW } } }; ================================================ FILE: MarathonRecomp/patches/aspect_ratio_patches.h ================================================ #pragma once #include #define MAKE_BITFLAG32(bit) 1U << bit #define MAKE_BITFLAG64(bit) 1ULL << bit inline constexpr float NARROW_ASPECT_RATIO = 4.0f / 3.0f; inline constexpr float WIDE_ASPECT_RATIO = 16.0f / 9.0f; inline constexpr float STEAM_DECK_ASPECT_RATIO = 16.0f / 10.0f; inline float g_aspectRatio; inline float g_aspectRatioMovie; inline float g_aspectRatioOffsetX; inline float g_aspectRatioOffsetY; inline float g_aspectRatioMultiplayerOffsetX; inline float g_aspectRatioScale; inline float g_aspectRatioGameplayScale; inline float g_aspectRatioNarrowScale; inline float g_aspectRatioNarrowMargin; inline float g_horzCentre; inline float g_vertCentre; inline float g_radarMapScale; class AspectRatioPatches { public: static void Init(); static void ComputeOffsets(); }; // -------------- CSD MODIFIERS --------------- // enum CsdFlags : uint64_t { CSD_ALIGN_CENTER = 0, CSD_ALIGN_TOP = MAKE_BITFLAG64(0), CSD_ALIGN_LEFT = MAKE_BITFLAG64(1), CSD_ALIGN_BOTTOM = MAKE_BITFLAG64(2), CSD_ALIGN_RIGHT = MAKE_BITFLAG64(3), CSD_ALIGN_TOP_LEFT = CSD_ALIGN_TOP | CSD_ALIGN_LEFT, CSD_ALIGN_TOP_RIGHT = CSD_ALIGN_TOP | CSD_ALIGN_RIGHT, CSD_ALIGN_BOTTOM_LEFT = CSD_ALIGN_BOTTOM | CSD_ALIGN_LEFT, CSD_ALIGN_BOTTOM_RIGHT = CSD_ALIGN_BOTTOM | CSD_ALIGN_RIGHT, CSD_STRETCH_HORIZONTAL = MAKE_BITFLAG64(4), CSD_STRETCH_VERTICAL = MAKE_BITFLAG64(5), CSD_STRETCH = CSD_STRETCH_HORIZONTAL | CSD_STRETCH_VERTICAL, CSD_SCALE = MAKE_BITFLAG64(6), CSD_EXTEND_LEFT = MAKE_BITFLAG64(7), CSD_EXTEND_RIGHT = MAKE_BITFLAG64(8), CSD_STORE_LEFT_CORNER = MAKE_BITFLAG64(9), CSD_STORE_RIGHT_CORNER = MAKE_BITFLAG64(10), CSD_SKIP = MAKE_BITFLAG64(11), CSD_OFFSET_SCALE_LEFT = MAKE_BITFLAG64(12), CSD_OFFSET_SCALE_RIGHT = MAKE_BITFLAG64(13), CSD_REPEAT_LEFT = MAKE_BITFLAG64(14), CSD_REPEAT_RIGHT = MAKE_BITFLAG64(15), CSD_REPEAT_FLIP_HORIZONTAL = MAKE_BITFLAG64(16), CSD_REPEAT_FLIP_VERTICAL = MAKE_BITFLAG64(17), CSD_REPEAT_EXTEND = MAKE_BITFLAG64(18), CSD_UV_MODIFIER = MAKE_BITFLAG64(19), CSD_COLOUR_MODIFIER = MAKE_BITFLAG64(20), CSD_REPEAT_UV_MODIFIER = MAKE_BITFLAG64(21), CSD_REPEAT_COLOUR_MODIFIER = MAKE_BITFLAG64(22), CSD_BLACK_BAR = MAKE_BITFLAG64(23), CSD_PROHIBIT_BLACK_BAR = MAKE_BITFLAG64(24), CSD_UNSTRETCH_HORIZONTAL = MAKE_BITFLAG64(25), CSD_CORNER_EXTRACT = MAKE_BITFLAG64(26), CSD_RADARMAP = MAKE_BITFLAG64(27), CSD_POD_BASE = MAKE_BITFLAG64(28), CSD_POD_CLONE = MAKE_BITFLAG64(29), CSD_MULTIPLAYER = MAKE_BITFLAG64(30), CSD_CHEVRON = MAKE_BITFLAG64(31), CSD_MODIFIER_ULTRAWIDE_ONLY = MAKE_BITFLAG64(32), CSD_MODIFIER_NARROW_ONLY = MAKE_BITFLAG64(33), CSD_SCENE_DISABLE_MOTION = MAKE_BITFLAG64(34), CSD_MOVIE = MAKE_BITFLAG64(35), CSD_CRI_LOGO = MAKE_BITFLAG64(36), CSD_MAIN_MENU_PARTS_CAST_0221 = MAKE_BITFLAG64(37), CSD_MAIN_MENU_PARTS_CAST_0222 = MAKE_BITFLAG64(38), CSD_MAIN_MENU_PARTS_CAST_0226 = MAKE_BITFLAG64(39), CSD_MAIN_MENU_PARTS_CAST_0227 = MAKE_BITFLAG64(40), CSD_BUTTON_WINDOW = MAKE_BITFLAG64(41) }; struct CsdUVs { float U0{}; float V0{}; float U1{}; float V1{}; float U2{}; float V2{}; float U3{}; float V3{}; }; struct CsdColours { uint32_t C0{}; uint32_t C1{}; uint32_t C2{}; uint32_t C3{}; }; struct CsdModifier { int64_t Flags{}; CsdUVs UVs{}; CsdColours Colours{}; CsdUVs RepeatUVs{}; CsdColours RepeatColours{}; float CornerMax{}; uint32_t CornerIndex{}; }; extern const xxHashMap g_csdModifiers; std::optional FindCsdModifier(uint32_t data); // ------------- MOVIE MODIFIERS -------------- // enum MovieFlags : uint32_t { MOVIE_CROP_NARROW = MAKE_BITFLAG32(0), MOVIE_CROP_WIDE = MAKE_BITFLAG32(1), MOVIE_CROP = MOVIE_CROP_NARROW | MOVIE_CROP_WIDE }; struct MovieModifier { uint32_t Flags{}; }; extern const xxHashMap g_movieModifiers; MovieModifier FindMovieModifier(XXH64_hash_t nameHash); #undef MAKE_BITFLAG64 #undef MAKE_BITFLAG32 ================================================ FILE: MarathonRecomp/patches/audio_patches.cpp ================================================ #include #include #include #include #include #include #include int AudioPatches::m_isAttenuationSupported = -1; bool AudioPatches::CanAttenuate() { #if _WIN32 if (m_isAttenuationSupported >= 0) return m_isAttenuationSupported; auto version = os::version::GetOSVersion(); m_isAttenuationSupported = version.Major >= 10 && version.Build >= 17763; return m_isAttenuationSupported; #elif __linux__ return true; #else return false; #endif } void AudioPatches::Update(float deltaTime) { auto pAudioEngine = Sonicteam::AudioEngineXenon::GetInstance(); if (!pAudioEngine) return; const float musicVolume = Config::MusicVolume * Config::MasterVolume; if (Config::MusicAttenuation && CanAttenuate()) { auto time = 1.0f - expf(2.5f * -deltaTime); if (os::media::IsExternalMediaPlaying()) { pAudioEngine->m_MusicVolume = std::lerp(pAudioEngine->m_MusicVolume, 0.0f, time); } else { pAudioEngine->m_MusicVolume = std::lerp(pAudioEngine->m_MusicVolume, musicVolume, time); } } else { pAudioEngine->m_MusicVolume = musicVolume; } pAudioEngine->m_EffectsVolume = Config::EffectsVolume * Config::MasterVolume; } // Update function for CRI cues. // This hook fixes jingles fading the BGM back in prematurely. PPC_FUNC_IMPL(__imp__sub_8260F168); PPC_FUNC(sub_8260F168) { struct CueParams { be Duration; be FrameTime; be Field08; be Field0C; be MusicVolume; bool Field14; }; auto pParams = (CueParams*)(base + ctx.r3.u32); pParams->FrameTime = App::s_deltaTime; __imp__sub_8260F168(ctx, base); } void CriCueUpdateDeltaTimeFix(PPCRegister& deltaTime) { deltaTime.f64 = App::s_deltaTime; } void PowerUpJingleDurationFix(PPCRegister& duration) { if (!Config::FixPowerUpJingleDuration) return; duration.f64 = 20.0; } void XmvPlayerLang(PPCRegister& r11) { r11.u32 = 1; if (Config::VoiceLanguage == EVoiceLanguage::Japanese) r11.u32++; } void CsbSbkLang(PPCRegister& r8) { r8.u32 = Config::VoiceLanguage == EVoiceLanguage::Japanese ? 0 : 1; } void MovieVoiceLang(PPCRegister& r19) { r19.u32 = Config::VoiceLanguage == EVoiceLanguage::Japanese ? 0x80000000 : 0x40000000; } ================================================ FILE: MarathonRecomp/patches/audio_patches.h ================================================ #pragma once class AudioPatches { protected: static int m_isAttenuationSupported; public: static bool CanAttenuate(); static void Update(float deltaTime); }; ================================================ FILE: MarathonRecomp/patches/camera_patches.cpp ================================================ #include "camera_patches.h" #include #include #include #include static class MainMenuCameraUpdateEvent : public ContextHookEvent { public: void Update(Sonicteam::MainMenuTask* pThis, float deltaTime) override { auto pMainMode = pThis->GetDocMode(); if (!pMainMode) return; pMainMode->m_spSelectCamera->Update(); } } g_mainMenuCameraUpdateEvent{}; void CameraPatches::Init() { MainMenuTaskPatches::s_events.push_back(&g_mainMenuCameraUpdateEvent); } void CameraImp_SetFOV(PPCRegister& f1) { if (g_aspectRatio >= WIDE_ASPECT_RATIO) return; f1.f64 = 2.0 * atan(tan(0.5 * f1.f64) / g_aspectRatio * WIDE_ASPECT_RATIO); } void SonicCamera_InvertAzDriveK(PPCRegister& az_driveK) { // X axis is inverted by default. if (Config::HorizontalCamera == ECameraRotationMode::Reverse) return; az_driveK.f64 = -az_driveK.f64; } void SonicCamera_InvertAltDriveK(PPCRegister& alt_driveK) { // Y axis is not inverted by default. if (Config::VerticalCamera == ECameraRotationMode::Normal) return; alt_driveK.f64 = -alt_driveK.f64; } void DemoGMCamera_InvertHorizontal(PPCRegister& horz) { // X axis is inverted by default. if (Config::HorizontalCamera == ECameraRotationMode::Reverse) return; horz.f64 = -horz.f64; } void DemoGMCamera_InvertVertical(PPCRegister& vert) { // Y axis is inverted by default. if (Config::VerticalCamera == ECameraRotationMode::Reverse) return; vert.f64 = -vert.f64; } ================================================ FILE: MarathonRecomp/patches/camera_patches.h ================================================ #pragma once class CameraPatches { public: static void Init(); }; ================================================ FILE: MarathonRecomp/patches/fps_patches.cpp ================================================ #include #include #include constexpr double REFERENCE_DELTA_TIME = 1.0 / 60.0; // Only use this in threaded context with fixed delta time! double MakeDeltaTime(std::chrono::steady_clock::time_point& prev) { auto now = std::chrono::steady_clock::now(); auto deltaTime = std::min(std::chrono::duration(now - prev).count(), 1.0 / 15.0); prev = now; now = std::chrono::steady_clock::now(); return deltaTime; } bool HasFrameElapsed(double reference, double& timeElapsed, double deltaTime) { timeElapsed += deltaTime; if (timeElapsed < reference) return false; timeElapsed = 0.0f; return true; } // Sonicteam::SoX::Physics::Havok::WorldHavok::Update PPC_FUNC_IMPL(__imp__sub_82587AA8); PPC_FUNC(sub_82587AA8) { auto pPhysicsWorld = (Sonicteam::SoX::Physics::Havok::WorldHavok*)(base + ctx.r3.u32); // Use dynamic update rate if the FPS exceeds the fixed rate. pPhysicsWorld->m_IsDynamicUpdateRate = Config::FPS > 120; __imp__sub_82587AA8(ctx, base); } void PostureControl_RotationSpeedFix(PPCRegister& c_rotation_speed, PPCRegister& stack) { auto deltaTime = *(be*)g_memory.Translate(stack.u32 + 0x200); c_rotation_speed.f64 = (c_rotation_speed.f64 * (60.0 * deltaTime)); } void SonicCamera_RotationSpeedFix(PPCRegister& f0, PPCRegister& deltaTime, PPCRegister& f13) { f0.f64 = float((f0.f64 * (deltaTime.f64 * (1.0 / (deltaTime.f64 * 60.0)))) + f13.f64); } bool ObjTarzan_VolatileBranch(PPCRegister& r30) { if (Config::FPS <= 60) return false; return r30.f64 >= 0; } void ObjTarzan_PatchStaticDeltaTime(PPCRegister& value, PPCRegister& delta) { if (Config::FPS <= 60) return; value.f64 = delta.f64; } void ObjTarzan_PatchDeltaTimeArgument(PPCRegister& value, PPCRegister& value2, PPCRegister& stack) { if (Config::FPS <= 60) return; auto deltaTime = *(be*)g_memory.Translate(stack.u32 + 0x90 + 0x420 - 0x88); value.f64 = deltaTime; value2.f64 = deltaTime; } // Sonicteam::ObjTarzan::UpdatePoint (speculatory) PPC_FUNC_IMPL(__imp__sub_8232D770); PPC_FUNC(sub_8232D770) { if (Config::FPS <= 60) { __imp__sub_8232D770(ctx, base); return; } struct TarzanPoint { MARATHON_INSERT_PADDING(0x4C); be m_Time; MARATHON_INSERT_PADDING(0xB0); }; auto pTarzanPoint = (TarzanPoint*)(base + ctx.r3.u32); auto deltaTime = ctx.f1.f64; if (deltaTime > 1.0) deltaTime = 1.0; do { GuestToHostFunction(sub_8232D288, pTarzanPoint, ctx.f1.u64, deltaTime, deltaTime, deltaTime); pTarzanPoint->m_Time = pTarzanPoint->m_Time - deltaTime; } while (pTarzanPoint->m_Time >= deltaTime); } void ObjEspSwing_DecayRateFix(PPCRegister& f0, PPCRegister& f13, PPCRegister& deltaTime) { f0.f64 = float(f13.f64 * pow(pow(f0.f64, 60.0), deltaTime.f64)); } struct MsgSuckPlayerEx : public Sonicteam::Message::Player::MsgSuckPlayer { be DeltaTime; }; void ObjectInputWarp_ExtendMsgSuckPlayer(PPCRegister& phantom, PPCRegister& message, PPCRegister& deltaTime) { auto pPhantom = (Sonicteam::SoX::Physics::Phantom*)g_memory.Translate(phantom.u32); auto pMessage = (Sonicteam::Message::Player::MsgSuckPlayer*)g_memory.Translate(message.u32); auto pMsgSuckPlayerEx = (MsgSuckPlayerEx*)g_userHeap.Alloc(sizeof(MsgSuckPlayerEx)); pMsgSuckPlayerEx->ID = pMessage->ID; pMsgSuckPlayerEx->Point = pMessage->Point; pMsgSuckPlayerEx->DeltaTime = deltaTime.f64; pPhantom->ProcessMessage(pMsgSuckPlayerEx); g_userHeap.Free(pMsgSuckPlayerEx); } void PlayerObject_ProcessMsgSuckPlayer_FixForce(PPCRegister& message, PPCRegister& force) { auto pMessage = (MsgSuckPlayerEx*)g_memory.Translate(message.u32); force.f64 = pow(force.f64, pMessage->DeltaTime * 60.0); } void PlayerObject_ProcessMsgSuckPlayer_FixDeltaTime(PPCRegister& message, PPCRegister& deltaTime) { auto pMessage = (MsgSuckPlayerEx*)g_memory.Translate(message.u32); deltaTime.f64 = pMessage->DeltaTime; } void Spanverse_GE1PE_DeltaTimeFix(PPCRegister& deltaTime) { deltaTime.f64 = App::s_deltaTime; } // Allocate more space to store the previous loading // time for this instance of Sonicteam::HUDLoading. void HUDLoadingAlloc(PPCRegister& r3) { r3.u32 += sizeof(std::chrono::steady_clock::time_point); } // Sonicteam::HUDLoading::HUDLoading PPC_FUNC_IMPL(__imp__sub_824D7BC8); PPC_FUNC(sub_824D7BC8) { auto pPrevLoadingTime = (std::chrono::steady_clock::time_point*)(base + ctx.r3.u32 + sizeof(Sonicteam::HUDLoading)); *pPrevLoadingTime = {}; __imp__sub_824D7BC8(ctx, base); } // Accumulate own delta time and provide it to the CSD update function. void HUDLoading_DeltaTimeFix(PPCRegister& pThis, PPCRegister& deltaTime) { auto pPrevLoadingTime = (std::chrono::steady_clock::time_point*)g_memory.Translate(pThis.u32 + sizeof(Sonicteam::HUDLoading)); deltaTime.f64 = MakeDeltaTime(*pPrevLoadingTime); } // This function is an override of Sonicteam::SoX::Engine::Task::Update, // it does not pass delta time to HUDCALLBACK::Update, so we must preserve // the register here in order to do it ourselves. void HUDWindow_PreserveDeltaTime(PPCRegister& f31, PPCRegister& f1) { f31.f64 = f1.f64; } // HUDCALLBACK::Update doesn't have a delta time argument, // so we pass one in here to do some delta time accumulation // safely in the hooks below. void HUDWindow_Callback(PPCRegister& f1, PPCRegister& f31) { f1.f64 = f31.f64; } // Sonicteam::PriceListWindowTask::HUDCALLBACK::Update PPC_FUNC_IMPL(__imp__sub_8250AAB0); PPC_FUNC(sub_8250AAB0) { static auto s_time = 0.0; // Fix for white ⇄ red text breathing animation. if (!HasFrameElapsed(REFERENCE_DELTA_TIME, s_time, ctx.f1.f64)) return; __imp__sub_8250AAB0(ctx, base); } // Sonicteam::SelectWindowTask::HUDCALLBACK::Update PPC_FUNC_IMPL(__imp__sub_8250D698); PPC_FUNC(sub_8250D698) { static auto s_time = 0.0; // Fix for white ⇄ red text breathing animation. if (!HasFrameElapsed(REFERENCE_DELTA_TIME, s_time, ctx.f1.f64)) return; __imp__sub_8250D698(ctx, base); } bool ObjectVehicleBike_EnemyShot_DisableVehicleCollisionLayer(PPCRegister& r3) { if (Config::FPS <= 60) return false; auto pObjectVehicleBike = (Sonicteam::ObjectVehicleBike*)g_memory.Translate(r3.u32); auto pPrefixDependency = pObjectVehicleBike->m_pDependency->GetFirstDependency(); // Call original function. sub_822C1FA8(*GetPPCContext(), g_memory.base); auto pPostfixDependency = pObjectVehicleBike->m_pDependency->GetFirstDependency(); auto isUpdatePhysicsWorld = false; // Process rigid bodies created after the function call. while (pPrefixDependency != pPostfixDependency) { pPrefixDependency = pPrefixDependency->m_pPrevSibling.get(); if (strcmp(pPrefixDependency->GetName(), "EnemyShotNormal") == 0) { auto pEnemyShotNormal = (Sonicteam::Enemy::EnemyShotNormal*)pPrefixDependency; if (auto pPhantom = pEnemyShotNormal->m_spPhantom.get()) { // Fix bullets colliding with the bike at high frame rates. auto& collidable = pPhantom->m_pRigidBody->m_collidable; collidable.m_broadPhaseHandle.m_collisionFilterInfo = 0xFFFF5E00; isUpdatePhysicsWorld = true; } } } if (isUpdatePhysicsWorld) { if (auto pGameMode = App::s_pApp->m_pDoc->GetDocMode()) { auto pWorldHavok = pGameMode->GetGame()->GetPhysicsWorld(); if (auto pWorld = pWorldHavok->m_pWorld) pWorld->updateCollisionFilterOnWorld(1, 1); } } return true; } ================================================ FILE: MarathonRecomp/patches/frontend_listener.cpp ================================================ #include #include #include #include #include static class FrontendListener : public SDLEventListener { bool m_isF8KeyDown = false; public: bool OnSDLEvent(SDL_Event* event) override { if (!Config::HUDToggleKey || OptionsMenu::s_isVisible) return false; switch (event->type) { case SDL_KEYDOWN: { if (event->key.keysym.sym != SDLK_F8 || m_isF8KeyDown) break; // TODO m_isF8KeyDown = true; break; } case SDL_KEYUP: m_isF8KeyDown = event->key.keysym.sym != SDLK_F8; break; } return false; } } g_frontendListener; ================================================ FILE: MarathonRecomp/patches/hook_event.h ================================================ #pragma once class IHookEvent { public: virtual ~IHookEvent() = default; virtual void Prefix() = 0; virtual void Update(float deltaTime) = 0; virtual void Postfix() = 0; }; class HookEvent : public IHookEvent { public: void Prefix() override {} void Update(float deltaTime) override {} void Postfix() override {} }; template class IContextHookEvent { public: virtual ~IContextHookEvent() = default; virtual void Prefix(T* pThis) = 0; virtual void Update(T* pThis, float deltaTime) = 0; virtual void Postfix(T* pThis) = 0; }; template class ContextHookEvent : public IContextHookEvent { public: void Prefix(T* pThis) override {} void Update(T* pThis, float deltaTime) override {} void Postfix(T* pThis) override {} }; ================================================ FILE: MarathonRecomp/patches/input_patches.cpp ================================================ #include #include static constexpr int INPUT_LISTENER_B_DOWN = 0x2000000; // Sonicteam::Player::Input::ListenerNormal::Update PPC_FUNC_IMPL(__imp__sub_82222428); PPC_FUNC(sub_82222428) { auto pListenerNormal = (Sonicteam::Player::Input::ListenerNormal*)(base + ctx.r3.u32); auto pInputManager = (Sonicteam::SoX::Input::Manager*)(base + ctx.r4.u32); __imp__sub_82222428(ctx, base); if (*pListenerNormal->m_pIsListening) { auto& rPadState = pInputManager->m_PadState; // Add B down support to the normal input listener. if (rPadState.IsDown(Sonicteam::SoX::Input::KeyState_B)) pListenerNormal->m_State = pListenerNormal->m_State.get() | INPUT_LISTENER_B_DOWN; } } void RemapAntigravityEnter(PPCRegister& r11, PPCRegister& r28) { auto state = 0x800; if (Config::SlidingAttack == ESlidingAttack::B) state = INPUT_LISTENER_B_DOWN; r11.u64 = (r28.u32 & state) != 0; } void RemapAntigravityExit(PPCRegister& r11, PPCRegister& r30) { if (Config::SlidingAttack == ESlidingAttack::X) return; r11.u64 = (r30.u32 & INPUT_LISTENER_B_DOWN) == 0; } void RemapLightDash(PPCRegister& r3, PPCRegister& r11) { r11.u64 = ((r3.u32 >> (Config::LightDash == ELightDash::X ? 8 : 15)) & 1) != 0; } ================================================ FILE: MarathonRecomp/patches/loading_patches.cpp ================================================ #include "loading_patches.h" #include // Sonicteam::HUDLoading::Update PPC_FUNC_IMPL(__imp__sub_824D7340); PPC_FUNC(sub_824D7340) { auto pHUDLoading = (Sonicteam::HUDLoading*)(base + ctx.r3.u32); if ((pHUDLoading->m_Flags.get() & Sonicteam::HUDLoading::HUDLoadingFlags_Finished) == 0) { for (auto& event : LoadingPatches::Events) event->Update(ctx.f1.f64); } __imp__sub_824D7340(ctx, base); } ================================================ FILE: MarathonRecomp/patches/loading_patches.h ================================================ #pragma once #include "hook_event.h" class LoadingPatches { public: static inline std::vector Events{}; }; ================================================ FILE: MarathonRecomp/patches/misc_patches.cpp ================================================ #include #include #include #include #include #include void UnlockAchievement(PPCRegister& id) { AchievementManager::Unlock(id.u32); } void SetLifeBarAnimation(PPCRegister& r3, PPCRegister& r4, PPCRegister& r5, PPCRegister& r31) { static bool s_initContextualHUD{}; static char* s_lifeBarSceneName{}; if (!s_initContextualHUD) { constexpr const char* LIFE_BAR_ANIME = "life_bar_anime"; s_lifeBarSceneName = (char*)g_userHeap.Alloc(strlen(LIFE_BAR_ANIME) + 1); strcpy(s_lifeBarSceneName, LIFE_BAR_ANIME); if (Config::RestoreContextualHUDColours) { *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Shadow] = 1.0f; *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Omega] = 1.0f; *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Rouge] = 1.0f; *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Silver] = 2.0f; *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Amy] = 2.0f; *Sonicteam::Globals::ms_MainDisplayColours[Sonicteam::Character_Blaze] = 2.0f; } s_initContextualHUD = true; } auto base = g_memory.base; auto pCsdObject = (Sonicteam::CsdObject*)(base + PPC_LOAD_U32(r3.u32)); auto pSceneName = (const char*)(base + r4.u32); // Redirect "life_ber_anime" to "life_bar_anime", as they // actually spelt "bar" correctly in the tag XNCP scene names... if ((pCsdObject->m_pCsdResource->m_FilePath == "sprite/tagdisplay_1p" || pCsdObject->m_pCsdResource->m_FilePath == "sprite/tagdisplay_2p") && strcmp(pSceneName, "life_ber_anime") == 0) { r4.u32 = g_memory.MapVirtual(s_lifeBarSceneName); } if (!Config::RestoreContextualHUDColours) return; auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)g_memory.Translate(r31.u32); switch (pHUDMainDisplay->m_Character) { case Sonicteam::Character_Sonic: case Sonicteam::Character_Tails: case Sonicteam::Character_Knuckles: r5.u32 = 0x82036778; // "sonic_in" break; case Sonicteam::Character_Shadow: case Sonicteam::Character_Omega: case Sonicteam::Character_Rouge: r5.u32 = 0x8203676C; // "shadow_in" break; case Sonicteam::Character_Silver: case Sonicteam::Character_Amy: case Sonicteam::Character_Blaze: r5.u32 = 0x82036760; // "silver_in" break; } } void SetRingBarIndex(PPCRegister& r5, PPCRegister& r31) { if (!Config::RestoreContextualHUDColours) return; auto pHUDMainDisplay = (Sonicteam::HUDMainDisplay*)g_memory.Translate(r31.u32); r5.u32 = pHUDMainDisplay->m_Character; } // Redirects 2P HUD to 1P HUD to remove overscan compensation. void Load2PDisplayMidAsmHook() {} void PostureDisableEdgeGrabLeftover(PPCRegister& posture) { if (!Config::DisableEdgeGrabLeftover) return; auto base = g_memory.base; *(volatile uint8_t*)(base + (posture.u32 + 0x3C0)) = 1; } void PedestrianAnimationLOD(PPCRegister& val) { val.u32 = 0; } // Sonicteam::CommonObjectHint::Update PPC_FUNC_IMPL(__imp__sub_822CE930); PPC_FUNC(sub_822CE930) { auto* pCommonObjectHint = (Sonicteam::CommonObjectHint*)(base + ctx.r3.u32); auto* pGame = App::s_pApp->m_pDoc->GetDocMode()->GetGame(); auto& rMessageName = pCommonObjectHint->m_MessageName; if (!Config::Hints && pCommonObjectHint->m_Type == Sonicteam::CommonObjectHint::CommonObjectHintType_HintRing) { pCommonObjectHint->Destroy(); return; } if (!Config::ControlTutorial || Config::SlidingAttack != ESlidingAttack::X) { // Get global flag for Sonic's Antigravity being unlocked to remove "hint_all03_h26_so". guest_stack_var msgGetSonicAntigravityFlag(6001); pGame->m_pMissionCore->ProcessMessage(msgGetSonicAntigravityFlag.get()); if (msgGetSonicAntigravityFlag->FlagValue != 0 && strcmp(rMessageName, "hint_twn01_e02_tl") == 0) { pCommonObjectHint->Destroy(); return; } } if (!Config::ControlTutorial || Config::LightDash != ELightDash::X) { // Get global flag for Sonic's Light Dash being unlocked to remove "hint_twn01_e01_tl". guest_stack_var msgGetSonicLightDashFlag(6000); pGame->m_pMissionCore->ProcessMessage(msgGetSonicLightDashFlag.get()); // Get global flag for Shadow's Light Dash being unlocked to remove "hint_twn01_e44_rg". guest_stack_var msgGetShadowLightDashFlag(6012); pGame->m_pMissionCore->ProcessMessage(msgGetShadowLightDashFlag.get()); auto isSonicLightDashHint = msgGetSonicLightDashFlag->FlagValue != 0 && strcmp(rMessageName, "hint_twn01_e00_tl") == 0; auto isShadowLightDashHint = msgGetShadowLightDashFlag->FlagValue != 0 && strcmp(rMessageName, "hint_twn01_e43_rg") == 0; if (isSonicLightDashHint || isShadowLightDashHint || strcmp(rMessageName, "hint_all03_h00_so") == 0 || strcmp(rMessageName, "hint_all03_h31_so") == 0) { pCommonObjectHint->Destroy(); return; } } auto isPlayStation = Config::ControllerIcons == EControllerIcons::PlayStation; if (Config::ControllerIcons == EControllerIcons::Auto) isPlayStation = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; if (!Config::ControlTutorial || isPlayStation) { guest_stack_var stack{}; auto pspTextCard = GuestToHostFunction*>(sub_825ECB48, stack.get(), App::s_pApp->GetGame()->m_pHintTextBook.get(), (const char*)rMessageName); if (auto pTextCard = pspTextCard->get()) { if (pTextCard->m_pVariables) { if (!Config::ControlTutorial) { if (strstr(pTextCard->m_pVariables, "picture(button_")) { pCommonObjectHint->Destroy(); return; } } if (isPlayStation) { // L1/R1 and L2/R2 are flipped on PS3, leading // to the voice lines being wrong for the hints. // // We'll provide an alternate control scheme to // address this later on, but for now these should // be hidden. if (strstr(pTextCard->m_pVariables, "button_lb") || strstr(pTextCard->m_pVariables, "button_rb")) { pCommonObjectHint->Destroy(); return; } } } } } __imp__sub_822CE930(ctx, base); } PPC_FUNC_IMPL(__imp__sub_8244D288); PPC_FUNC(sub_8244D288) { auto isShadowEggCerberus = PPC_LOAD_U32(ctx.r3.u32 + 0x1C); // Prevent Eggman's voice line playing // for Shadow's variant of Egg Cerberus. if (!Config::Hints && isShadowEggCerberus) return; __imp__sub_8244D288(ctx, base); } bool Super3_DisableChangeRequestHint() { return !Config::ControlTutorial; } PPC_FUNC_IMPL(__imp__sub_824A6EA8); PPC_FUNC(sub_824A6EA8) { if ((App::s_isSkipLogos || Config::SkipIntroLogos) && ctx.r4.u32 == 1) ctx.r4.u32 = 4; __imp__sub_824A6EA8(ctx, base); } PPC_FUNC(sub_82188460) { ctx.r3.u64 = Config::Subtitles; } void NOP() {} ================================================ FILE: MarathonRecomp/patches/patches.h ================================================ #include "aspect_ratio_patches.h" #include "camera_patches.h" inline void InitPatches() { AspectRatioPatches::Init(); CameraPatches::Init(); } ================================================ FILE: MarathonRecomp/patches/pause_patches.cpp ================================================ #include #include #include void AddPauseMenuItem ( Sonicteam::TextBook* in_pTextBook, stdx::vector* in_pvActionNames, stdx::vector* in_pvTextCards, stdx::vector* in_pvEnabledItems, const char* in_pActionName, const char* in_pTextName, bool in_isEnabled ) { guest_stack_var actionName(in_pActionName); guest_stack_var textName(in_pTextName); guest_stack_var> spTextCard; guest_stack_var isEnabled((int)in_isEnabled); GuestToHostFunction(sub_8217D608, in_pvActionNames, actionName.get()); GuestToHostFunction(sub_825ECB48, spTextCard.get(), in_pTextBook, textName->c_str()); GuestToHostFunction(sub_8239E8F0, in_pvTextCards, spTextCard.get()); GuestToHostFunction(sub_823879C8, in_pvEnabledItems, isEnabled.get()); } void GameImp_PauseMenu_AddQuitPrefix(PPCRegister& r1, PPCRegister& r30) { auto pGameImp = (Sonicteam::GameImp*)g_memory.Translate(r30.u32); auto pvActionNames = (stdx::vector*)g_memory.Translate(r1.u32 + 0x70); auto pvTextCards = (stdx::vector*)g_memory.Translate(r1.u32 + 0x60); auto pvEnabledItems = (stdx::vector*)g_memory.Translate(r1.u32 + 0x80); AddPauseMenuItem(pGameImp->m_pSystemTextBook, pvActionNames, pvTextCards, pvEnabledItems, "options", "msg_options", true); } // Sonicteam::PauseAdapter::MapActionNameToID (speculatory) PPC_FUNC_IMPL(__imp__sub_8216DA08); PPC_FUNC(sub_8216DA08) { auto pPauseAdapter = (Sonicteam::PauseAdapter*)(base + ctx.r3.u32); auto pMsgGetText = (Sonicteam::Message::PauseAdapter::MsgGetText*)(base + ctx.r4.u32); __imp__sub_8216DA08(ctx, base); // Set selected ID to unused slot. if (pMsgGetText->SelectedName == "options") pPauseAdapter->m_SelectedID = 6; } // Sonicteam::PauseAdapter::DoAction (speculatory) PPC_FUNC_IMPL(__imp__sub_82170E48); PPC_FUNC(sub_82170E48) { auto pPauseAdapter = (Sonicteam::PauseAdapter*)(base + ctx.r3.u32); if (pPauseAdapter->m_SelectedID == 6) { OptionsMenu::s_pBgmCue = pPauseAdapter->GetGame()->GetBgmCue(); OptionsMenu::Open(true); return; } __imp__sub_82170E48(ctx, base); } // Sonicteam::PauseTask::Update PPC_FUNC_IMPL(__imp__sub_82509870); PPC_FUNC(sub_82509870) { auto pPauseTask = (Sonicteam::PauseTask*)(base + ctx.r3.u32); static bool s_isReturningFromOptionsMenu{}; switch (pPauseTask->m_State) { case Sonicteam::PauseTask::PauseTaskState_Opening: case Sonicteam::PauseTask::PauseTaskState_Idle: { if (!s_isReturningFromOptionsMenu) break; // Set cursor to Options (should always be above the last item). pPauseTask->m_SelectedIndex = pPauseTask->m_ItemCount - 2; s_isReturningFromOptionsMenu = false; break; } case Sonicteam::PauseTask::PauseTaskState_Closed: { if (OptionsMenu::s_isVisible) { if (OptionsMenu::s_state == OptionsMenuState::Closing) { pPauseTask->m_State = Sonicteam::PauseTask::PauseTaskState_Opened; s_isReturningFromOptionsMenu = true; } else { return; } } break; } } __imp__sub_82509870(ctx, base); } ================================================ FILE: MarathonRecomp/patches/player_patches.cpp ================================================ #include #include #include #include #include // Sonicteam::Player::Object::Update PPC_FUNC_IMPL(__imp__sub_82195500); PPC_FUNC(sub_82195500) { auto pPlayer = (Sonicteam::Player::Object*)(base + ctx.r3.u32); auto pInputManager = pPlayer->GetInputManager(); if (pPlayer->m_IsPlayer && pInputManager) { if (Config::EnableDebugMode) { switch (pPlayer->m_SetupModuleIndexPostfix) { case 1: { // Toggle debug posture on Select press. if (pPlayer->m_SetupModuleIndexPrefix == 1 && pInputManager->m_PadState.IsPressed(Sonicteam::SoX::Input::KeyState_Select)) { pPlayer->m_SetupModuleIndexPostfix = 2; LOGN("Debug Mode: Enabled"); } break; } case 2: { // Toggle camera volume collision on B press. if (pInputManager->m_PadState.IsPressed(Sonicteam::SoX::Input::KeyState_B)) { auto pGame = App::s_pApp->GetGame(); auto pZock = pPlayer->GetPlugin("zock"); auto collisionFilterInfo = pZock->m_spPhantomA->m_pRigidBody->m_collidable.m_broadPhaseHandle.m_collisionFilterInfo == 6 ? 0x383 : 6; LOGFN("Camera Volumes: {}", collisionFilterInfo != 6 ? "Enabled" : "Disabled"); pZock->m_spPhantomA->m_pRigidBody->m_collidable.m_broadPhaseHandle.m_collisionFilterInfo = collisionFilterInfo; pGame->GetPhysicsWorld()->m_pWorld->updateCollisionFilterOnWorld(1, 1); } break; } } } // Toggle demo camera on right stick press. if (Config::RestoreDemoCameraMode && pInputManager->m_PadState.IsPressed(Sonicteam::SoX::Input::KeyState_RightStick)) { auto pCameraman = static_cast(pPlayer->m_pCameraman.get()); if (auto pCameraMode = pCameraman->m_spCameraModeManager->m_spCameraMode.get()) { guest_stack_var msgChangeMode; msgChangeMode->PadID = pInputManager->m_PadID; msgChangeMode->TargetActorID = pPlayer->m_ActorID; msgChangeMode->IsDemoCamera = pCameraMode->m_pVftable.ptr != 0x82002004; LOGFN("Demo Camera: {}", msgChangeMode->IsDemoCamera ? "Enabled" : "Disabled"); pCameraman->ProcessMessage(msgChangeMode.get()); } } } __imp__sub_82195500(ctx, base); } // Sonicteam::Player::State::TailsContext::Update PPC_FUNC_IMPL(__imp__sub_8221A7D8); PPC_FUNC(sub_8221A7D8) { if (!Config::TailsGauge) { __imp__sub_8221A7D8(ctx, base); return; } auto pTailsContext = (Sonicteam::Player::State::TailsContext*)(base + ctx.r3.u32); auto pPlayer = pTailsContext->m_spScore->m_pPlayer; if (auto pGauge = pPlayer->GetGauge()) { pGauge->c_gauge_max = 100.0f; pGauge->m_Value = (100.0f / pTailsContext->m_FlightDuration) * pTailsContext->m_FlightTime; } auto pTailsFlight = pPlayer->m_spStateMachine->GetBase()->GetState(); auto pGame = App::s_pApp->GetGame(); auto maturityValue = 1.0f; // Set maturity value if the current state is Sonicteam::Player::State::TailsFlight. if (pTailsFlight->m_pVftable.ptr == 0x82005404) maturityValue = (1.0f / pTailsContext->m_FlightLimit) * pTailsFlight->m_FlightTime; for (int i = 0; i < 4; i++) { if (pGame->m_PlayerData[i].ActorID == pPlayer->m_ActorID.get()) pGame->m_PlayerData[i].MaturityValue = maturityValue; } __imp__sub_8221A7D8(ctx, base); } // Sonicteam::Player::Score::Score PPC_FUNC_IMPL(__imp__sub_821E8C48); PPC_FUNC(sub_821E8C48) { if (!Config::TailsGauge) { __imp__sub_821E8C48(ctx, base); return; } auto pPlayer = (Sonicteam::Player::Object*)(base + ctx.r4.u32); if (pPlayer->m_LuaFile == "player/tails.lua") { auto pSonicGauge = GuestToHostFunction(sub_8223F208, g_userHeap.Alloc(sizeof(Sonicteam::Player::SonicGauge))); guest_stack_var> spSonicGauge; // Make shared pointer. GuestToHostFunction(sub_821BEAB0, spSonicGauge.get(), pSonicGauge); // Add gauge plugin to player. GuestToHostFunction(sub_821BECE0, pPlayer, spSonicGauge.get(), 1); pPlayer->m_spGauge = *spSonicGauge.get(); } __imp__sub_821E8C48(ctx, base); } // Add missing SetupModuleDebug to table. void PlayerDebugMode_RegisterLuaSetup(PPCRegister& str, PPCRegister& index) { if (!Config::EnableDebugMode) return; auto pString = (stdx::string*)g_memory.Translate(str.u32); switch (index.u32) { case 0: *pString = "SetupModuleDebug"; break; case 1: *pString = "SetupModule"; break; case 2: *pString = "SetupModuleDebug"; break; } } bool PlayerDebugMode_RemapDebugExitButton(PPCRegister& r30) { auto pPadState = (Sonicteam::SoX::Input::PadState*)g_memory.Translate(r30.u32); if (pPadState->IsPressed(Sonicteam::SoX::Input::KeyState_Select)) { LOGN("Debug Mode: Disabled"); return true; } return false; } bool AntigravityRetainsMomentum() { return Config::AntigravityRetainsMomentum; } bool ControllableBoundAttack() { return Config::ControllableBoundAttack; } bool ControllableBoundAttack2(PPCCRRegister& cmp) { if (Config::ControllableBoundAttack) return !cmp.eq; return cmp.eq; } bool ControllableSpinkick() { return Config::ControllableSpinkick; } bool ControllableTeleportDash() { return Config::ControllableTeleportDash; } bool DisablePushState() { return Config::DisablePushState; } bool MidairControlForMachSpeed() { return Config::MidairControlForMachSpeed; } bool MidairControlForSnowboards() { return Config::MidairControlForSnowboards; } void RestoreChaosBoostJump(PPCRegister& r10, PPCRegister& r11) { if (!Config::RestoreChaosBoostJump) return; r10.u32 = 1; r11.u32 = 2; } void RestoreChainJumpFlips(PPCRegister& r31, PPCRegister& r30, PPCRegister& r11, PPCRegister& f1, PPCRegister& f2, PPCRegister& f3) { if (!Config::RestoreChainJumpFlips) return; struct Message { be ID; Sonicteam::SoX::Math::Quaternion Rotation; Sonicteam::SoX::Math::Vector Position; be ActorID; }; auto pPlayer = (Sonicteam::Player::Object*)g_memory.Translate(r31.u32); auto pMessage = (Message*)g_memory.Translate(r30.u32); auto pPlayerContext = (Sonicteam::Player::State::ICommonContext*)g_memory.Translate(r11.u32); auto pActorManager = App::s_pApp->m_pDoc->GetDocMode()->GetGame()->m_spActorManager.get(); auto origin = pPlayer->m_spRootFrame->m_PositionF0; auto target = pMessage->Position; if (pMessage->ActorID != -1) { auto pFixture = GuestToHostFunction(sub_821609D0, pActorManager, &pMessage->ActorID); auto msgGetNextPoint = guest_stack_var(); msgGetNextPoint->Rotation = { 0, 0, 0, 1 }; msgGetNextPoint->Position = { 0, 0, 0, 1 }; if (pFixture->ProcessMessage(msgGetNextPoint.get())) target = msgGetNextPoint->Position; } auto magnitudeHorz = f1.f64; auto magnitudeForward = f2.f64; auto distance = origin.DistanceTo(target); auto magnitude = std::sqrt(magnitudeHorz * magnitudeHorz + magnitudeForward * magnitudeForward); auto time = 1.0; if (distance > 0.0 && magnitude > 0.0) time = distance / magnitude; // CommonContext has a slightly different algorithm to process chain flips. if (((Sonicteam::Player::IPlugIn*)pPlayerContext)->m_pVftable.ptr != 0x8200A728) time *= 0.35; f3.f64 = time; } bool RestoreChaosSpearFlips() { return Config::RestoreChaosSpearFlips; } bool UnlimitedAntigravity() { if (Config::SlidingAttack == ESlidingAttack::B) return true; return Config::UnlimitedAntigravity; } PPC_FUNC_IMPL(__imp__sub_82217FC0); PPC_FUNC(sub_82217FC0) { if (!Config::RestoreSonicActionGauge) { __imp__sub_82217FC0(ctx, base); return; } auto pSonicContext = (Sonicteam::Player::State::SonicContext*)(base + ctx.r3.u32); auto pSonicGauge = pSonicContext->m_Gauge.get(); using enum Sonicteam::Player::State::SonicContext::Gem; auto gemIndex = (Sonicteam::Player::State::SonicContext::Gem)ctx.r4.u32; switch (gemIndex) { case Gem_Yellow: { // Prevent Yellow Gem spam. if (pSonicContext->m_ThunderGuard) break; } case Gem_Blue: case Gem_Green: case Gem_Sky: case Gem_White: case Gem_Super: { auto spriteIndex = Sonicteam::Player::State::SonicContext::ms_GemSpriteConversionTable[gemIndex - 1] - 1; if (pSonicGauge->m_Value >= (&pSonicGauge->c_gauge_green)[spriteIndex].get()) { ctx.r3.u64 = 1; return; } break; } case Gem_Red: case Gem_Purple: { if (pSonicContext->m_Field24A == 0) { ctx.r3.u64 = 1; return; } break; } } ctx.r3.u64 = 0; } PPC_FUNC_IMPL(__imp__sub_82218068); PPC_FUNC(sub_82218068) { if (!Config::RestoreSonicActionGauge) { __imp__sub_82217FC0(ctx, base); return; } auto pSonicContext = (Sonicteam::Player::State::SonicContext*)(base + ctx.r3.u32); auto pSonicGauge = pSonicContext->m_Gauge.get(); auto deltaTime = ctx.f1.f64; using enum Sonicteam::Player::State::SonicContext::Gem; auto gemIndex = (Sonicteam::Player::State::SonicContext::Gem)ctx.r4.u32; auto spriteIndex = Sonicteam::Player::State::SonicContext::ms_GemSpriteConversionTable[gemIndex - 1] - 1; switch (gemIndex) { case Gem_Blue: case Gem_Green: case Gem_Yellow: case Gem_Sky: case Gem_White: case Gem_Super: { pSonicGauge->m_Value = pSonicGauge->m_Value.get() - (&pSonicGauge->c_gauge_green)[spriteIndex].get(); pSonicGauge->m_GroundedTime = 0.0; break; } case Gem_Red: case Gem_Purple: { pSonicGauge->m_Value = pSonicGauge->m_Value.get() - (&pSonicGauge->c_gauge_green)[spriteIndex].get() * deltaTime; pSonicGauge->m_GroundedTime = 0.0; if (pSonicGauge->m_Value <= 0) { pSonicGauge->m_Value = 0.0; pSonicContext->m_Shrink = 0; pSonicContext->m_SlowTime = 0; pSonicContext->m_Field24A = 1; } break; } } } // Sonicteam::Player::SonicGauge::IVariable::Init // This hook redirects the incorrectly named Lua variables to the ones actually used in the scripts. PPC_FUNC_IMPL(__imp__sub_8223F360); PPC_FUNC(sub_8223F360) { auto pIVariable = ctx.r3.u32; auto pLuaSystem = ctx.r4.u32; __imp__sub_8223F360(ctx, base); if (!Config::RestoreSonicActionGauge) return; auto pSonicGauge = (Sonicteam::Player::SonicGauge*)g_memory.Translate(pIVariable - 0x20); auto pVariableName = g_userHeap.Alloc(); *pVariableName = "c_gauge_green"; if (pSonicGauge->c_gauge_green == 0.0f) pSonicGauge->c_gauge_green = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); *pVariableName = "c_gauge_red"; if (pSonicGauge->c_gauge_red == 0.0f) pSonicGauge->c_gauge_red = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); *pVariableName = "c_gauge_blue"; if (pSonicGauge->c_gauge_blue == 0.0f) pSonicGauge->c_gauge_blue = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); *pVariableName = "c_gauge_white"; if (pSonicGauge->c_gauge_white == 0.0f) pSonicGauge->c_gauge_white = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); *pVariableName = "c_gauge_sky"; if (pSonicGauge->c_gauge_sky == 0.0f) pSonicGauge->c_gauge_sky = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); *pVariableName = "c_gauge_yellow"; if (pSonicGauge->c_gauge_yellow == 0.0f) pSonicGauge->c_gauge_yellow = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); *pVariableName = "c_gauge_purple"; if (pSonicGauge->c_gauge_purple == 0.0f) pSonicGauge->c_gauge_purple = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); *pVariableName = "c_gauge_super"; if (pSonicGauge->c_gauge_super == 0.0f) pSonicGauge->c_gauge_super = GuestToHostFunction(sub_821EA350, pLuaSystem, pVariableName); pVariableName->~string(); g_userHeap.Free(pVariableName); } void SonicGauge_FixGemSprite(PPCRegister& r) { auto pGame = (Sonicteam::GameImp*)g_memory.Translate(r.u32); for (int i = 0; i < 4; i++) pGame->m_PlayerData[i].GemIndex = 0; } void SonicGauge_FixFlags(PPCRegister& r3, PPCRegister& r31) { if (!Config::RestoreSonicActionGauge || !r3.u32) return; auto pSonicGauge = (Sonicteam::Player::SonicGauge*)g_memory.Translate(r3.u32); // Ensure this is SonicGauge. if (((Sonicteam::Player::IPlugIn*)pSonicGauge)->m_pVftable.ptr != 0x8200D4D8) return; auto pSonicContext = (Sonicteam::Player::State::SonicContext*)g_memory.Translate(r31.u32); auto pSonicWeapons = pSonicContext->m_spScore->m_pPlayer->GetPlugin("sonic_weapons"); static constexpr uint8_t s_groundedAnims[4] = { 0xCB, 0xCC, 0x46, 0xCE }; for (auto i = 0; i < 4; i++) { if (pSonicContext->m_AnimationID == s_groundedAnims[i]) pSonicGauge->m_GroundedFlags = 1; } if (pSonicContext->m_Tornado != 0 || pSonicWeapons->m_GunDrive.m_pElement) { pSonicGauge->m_GroundedFlags = 1; } else { using enum Sonicteam::Player::State::SonicContext::Gem; if ((pSonicContext->m_Buttons.get() & 0x10000) != 0) pSonicContext->m_Field24A = 0; if ((pSonicContext->m_Buttons.get() & 0x20000) != 0 && (pSonicContext->m_CurrentGem == Gem_Red || pSonicContext->m_CurrentGem == Gem_Purple)) { pSonicGauge->m_GroundedFlags = 1; if (pSonicContext->m_Field24A) { pSonicGauge->m_GroundedFlags = 0; pSonicContext->m_Shrink = 0; pSonicContext->m_SlowTime = 0; } } else if ((pSonicContext->m_PostureFlags.get() & Sonicteam::Player::PostureControl::PostureFlag_Grounded) != 0 || pSonicContext->m_Field24A) { pSonicGauge->m_GroundedFlags = 0; } } } bool InfiniteLives() { return Config::InfiniteLives; } ================================================ FILE: MarathonRecomp/patches/text_patches.cpp ================================================ #include "text_patches.h" #include #include #include #include #include #include // Load text card. PPC_FUNC_IMPL(__imp__sub_825ECB48); PPC_FUNC(sub_825ECB48) { auto* pMessage = (const char*)(base + ctx.r5.u32); void* pNewMessage = nullptr; for (auto& redirect : TextPatches::s_redirectedMessages) { if (HashStr(pMessage) != redirect.first) continue; pNewMessage = g_userHeap.Alloc(strlen(redirect.second) + 1); strcpy((char*)pNewMessage, redirect.second); ctx.r5.u32 = g_memory.MapVirtual(pNewMessage); } __imp__sub_825ECB48(ctx, base); if (pNewMessage) g_userHeap.Free(pNewMessage); static uint16_t* s_replacementStringPool{}; static ELanguage s_replacementStringPoolLanguage{}; if (s_replacementStringPoolLanguage != Config::Language) { auto length = 0; // Compute string pool length. for (auto& replacement : TextPatches::s_replacedMessages) length += (Localise(replacement.second.pKey).length() * 2) + 2; if (s_replacementStringPool) g_userHeap.Free(s_replacementStringPool); s_replacementStringPool = (uint16_t*)g_userHeap.Alloc(length); s_replacementStringPoolLanguage = Config::Language; auto stringPoolPos = s_replacementStringPool; for (auto& replacement : TextPatches::s_replacedMessages) { auto& message = Localise(replacement.second.pKey); auto wideMessage = TransformUTF8ToWString(message); auto wideMessageLen = wideMessage.length() + 1; replacement.second.pGuestText = stringPoolPos; for (size_t i = 0; i < wideMessageLen; i++) { *stringPoolPos = ByteSwap(wideMessage.c_str()[i]); stringPoolPos++; } } } auto pspTextCard = (boost::shared_ptr*)(base + ctx.r3.u32); for (auto& replacement : TextPatches::s_replacedMessages) { if (HashStr(pMessage) != replacement.first) continue; pspTextCard->get()->m_spResource = boost::make_shared(0, 0x820334C4); pspTextCard->get()->m_pText = replacement.second.pGuestText; pspTextCard->get()->m_pVariables = (const char*)0x8200139C; } } // Load text book. PPC_FUNC_IMPL(__imp__sub_821735B8); PPC_FUNC(sub_821735B8) { auto* pTextBookPath = (const char*)(base + ctx.r5.u32); void* pNewTextBookPath = nullptr; if (Config::IsControllerIconsPS3()) { static constexpr const char* MSG_HINT_PS3 = "text/msg_hint_ps3.mst"; if (strcmp(pTextBookPath, "text/msg_hint_xenon.mst") == 0) { pNewTextBookPath = g_userHeap.Alloc(strlen(MSG_HINT_PS3) + 1); strcpy((char*)pNewTextBookPath, MSG_HINT_PS3); ctx.r5.u32 = g_memory.MapVirtual(pNewTextBookPath); } } __imp__sub_821735B8(ctx, base); if (!pNewTextBookPath) return; g_userHeap.Free(pNewTextBookPath); } // Sonicteam::GameImp::OpenHintWindow (speculatory) PPC_FUNC_IMPL(__imp__sub_82173838); PPC_FUNC(sub_82173838) { if (!Config::Hints) { auto pMessage = (const char*)(base + ctx.r4.u32); // Block specific hints from volumes. for (auto& pattern : TextPatches::s_hintPatterns) { if (strcmpWildcard(pMessage, pattern)) { LOGFN_UTILITY("Blocked hint: {}", pMessage); return; } } } __imp__sub_82173838(ctx, base); } ================================================ FILE: MarathonRecomp/patches/text_patches.h ================================================ #pragma once #include struct ReplacementMessage { const char* pKey{}; xpointer pGuestText{}; }; class TextPatches { public: static inline xxHashMap s_redirectedMessages { { HashStr("msg_deviceselect"), "msg_retry" }, // Replace "Select storage device." text with "Retry" for alert windows. { HashStr("msg_gamequitconfirm4"), "msg_backtotitle1" } // Replace "Exit the game." text with "Go back to the Title Screen." }; static inline xxHashMap s_replacedMessages { { HashStr("msg_goldmedalresults"), { "MainMenu_GoldMedalResults_Name" } }, { HashStr("msg_goldmedalresults_c"), { "MainMenu_GoldMedalResults_Description" } } }; static inline std::vector s_hintPatterns = { "hint_all01_a0*", "hint_all01_e06_*", "hint_all03_a00_*", "hint_all03_h3*", "hint_all04_*", "hint_all05_a0*", "hint_all05_a1*", "hint_all05_a2*", "hint_all05_a30_*", "hint_all05_a31_*", "hint_all05_a32_*", "hint_all06_*", "hint_kdv01_a00_*", "hint_kdv01_a02_sv", // Silver: Man, the staircase is damaged. Now I'll have to use my power. "hint_kdv01_a04_rg", // Rouge: You can use this vehicle to get across the lake. "hint_kdv01_a05_sv", // Silver: I can manage this cage with my power. "hint_kdv01_a06_*", "hint_kdv01_a07_sv", // Silver: That container might help me move forward. "hint_kdv01_a08_sv", // Silver: I might be able to levitate to that spring. "hint_kdv01_h*", "hint_wvo01_a*", "hint_tpj01_a01_*", "hint_tpj01_a03_pr", // Elise: Over there! "hint_tpj01_a04_*", "hint_tpj01_a05_*", "hint_tpj01_a06_*", "hint_tpj01_a07_sv", // Silver: This hanging ruin might be useful. "hint_tpj01_a08_sv", // Silver: Looks like I can use this to go up. "hint_tpj01_a09_sv", // Silver: If I can hit that ball with this... "hint_tpj01_a10_sv", // Silver: Is this for breaking walls? "hint_tpj01_a11_*", "hint_tpj01_a12_*", "hint_tpj01_a13_*", "hint_tpj01_e03_pr", // Elise: Sonic! To your left! "hint_tpj01_e05_pr", // Elise: Sonic! To your right! "hint_tpj01_e07_rg", // Rouge: It looks like you need to take them down. "hint_tpj01_e09_pr", // Elise: Sonic! Above you! "hint_tpj01_e10_sn", // Sonic: There's a route here. "hint_tpj01_e13_pr", // Elise: Sonic! Look down! "hint_tpj01_e18_sn", // Sonic: Oh? Is there a back road? "hint_tpj01_e19_sn", // Sonic: Thank you, Elise! "hint_tpj01_h*", "hint_dtd01_a00_*", "hint_dtd01_a02_sd", // Shadow: It looks like these pillars will lead me to him. "hint_dtd01_a03_*", "hint_dtd01_a04_sv", // Silver: I might be able to make a path leading up if I use my power over there. "hint_dtd01_a05_sd", // Shadow: These big statues look pretty fragile. "hint_dtd01_h*", "hint_wap01_a*", "hint_wap01_h*", "hint_csc01_a03_sn", // Sonic: This lamp post... Maybe I can swing off of it? "hint_csc01_a06_sv", // Silver: Blaze! Watch out for things flying out of the tornado. "hint_csc01_a07_sv", // Silver: This steel bar has a green mark on it. If I use my Psychokinesis here... "hint_csc01_a08_sv", // Silver: That road doesn't look too solid. "hint_csc01_a09_sv", // Silver: I can wipe them out if I hit that pipe right. "hint_csc01_a10_bz", // Blaze: Use your Psychokinesis! You should have no problem with the enemy's bullets! "hint_csc01_a11_bz", // Blaze: It looks like you can take advantage of this concrete ramp. "hint_flc01_a06_sv", // Silver: I'll have to get closer and use my power directly! "hint_flc01_h*", "hint_rct01_a03_sd", // Shadow: I'm going to have to destroy the train so it doesn't get away! "hint_rct01_a04_sv", // Silver: There's no other way ahead. I guess I've got to break down the door. "hint_rct01_a05_sv", // Silver: If I put weights on this scale, I'll be able to move forward. "hint_rct01_a06_*", "hint_rct01_a07_*", "hint_rct01_a08_sn", // Sonic: I'll be able to move faster if I go on top of the train instead of being on the rails. "hint_rct01_a09_sv", // Silver: I'll blow away these containers with my power! "hint_rct01_a10_*", "hint_rct01_a12_sd", // Shadow: I'm going to have to use Homing Missiles so he doesn't get away. "hint_rct01_h*", "hint_aqa01_a00_*", "hint_aqa01_a01_tl", // Tails: If you use the power of this ball, you can get over there. "hint_aqa01_a02_*", "hint_aqa01_a03_tl", // Tails: If you attack the switch, the magnet will activate. "hint_aqa01_a04_tl", // Tails: There are some magnets that repel if you push them twice... "hint_aqa01_a05_tl", // Tails: Look! There's the magnet switch. Push it, and the path should open! "hint_aqa01_a07_sn", // Sonic: If I slide, I can squeeze through. "hint_aqa00_e*", "hint_end01_e03_*", "hint_end01_h*", "hint_bos01_a*", "hint_bos02_a*", "hint_bos03_a*", "hint_bos04_a*", "hint_bos05_a*", "hint_bos06_a*", "hint_bos07_a*", "hint_bos08_a*", "hint_bos09_a*", "hint_bos10_a*", "hint_bos11_e*", "hint_bos11_a*", "hint_twn01_a*", "hint_twn01_e0?_tl", "hint_twn01_e05_kn", // Knuckles: Heh, there's a secret room here! Let's hurry to the White Acropolis! "hint_twn01_e0?_pr", "hint_twn01_e18_kn", // Knuckles: Ring three bells at once... Sonic, you should be able to do it! "hint_twn01_e30_bz", // Blaze: You need to short out the electricity in order to stop this laser. We'll need to hit the switches at the same time. "hint_twn01_e43_rg", // Rouge: The bridge is broken! Shadow, can't you do something? "hint_twn01_h*", "hint_tpj01_e20_pr" // Elise: If you jump when the bud is glowing, you may be able to jump higher. }; }; ================================================ FILE: MarathonRecomp/patches/video_patches.cpp ================================================ #include #include #include #include #include const char* g_pBlockName{}; void SetMSAALevel(PPCRegister& val) { val.u32 = 0; } void BeginBlockGetName(PPCRegister& r3) { g_pBlockName = (const char*)g_memory.Translate(r3.u32); #if _DEBUG if (g_pBlockName) LOGFN_UTILITY("Block Begin: {}", g_pBlockName); #endif } // EndBlock PPC_FUNC_IMPL(__imp__sub_826078D8); PPC_FUNC(sub_826078D8) { #if _DEBUG if (g_pBlockName) LOGFN_UTILITY("Block End: {}", g_pBlockName); #endif g_pBlockName = nullptr; __imp__sub_826078D8(ctx, base); } float ReflectionScaleFactor(EReflectionResolution ref) { switch (ref) { case EReflectionResolution::Eighth: return 0.5f; case EReflectionResolution::Quarter: return 1.0f; case EReflectionResolution::Half: return 2.0f; case EReflectionResolution::Full: return 4.0f; default: return 1.0f; } } // CreateTexture PPC_FUNC_IMPL(__imp__sub_82619D00); PPC_FUNC(sub_82619D00) { auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); if (*pName == "radermap") { ctx.r5.u32 = g_radarMapScale; ctx.r6.u32 = g_radarMapScale; } if (*pName == "reflection0") { ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * ReflectionScaleFactor(Config::ReflectionResolution)); ctx.r6.u32 = static_cast(static_cast(ctx.r6.u32) * ReflectionScaleFactor(Config::ReflectionResolution)); } // RenderMefiress if (*pName == "user0") { ctx.r5.u32 = static_cast(Config::ShadowResolution.Value); ctx.r6.u32 = static_cast(Config::ShadowResolution.Value); } #if _DEBUG auto width = ctx.r5.u32; auto height = ctx.r6.u32; #endif __imp__sub_82619D00(ctx, base); #if _DEBUG LOGFN_UTILITY("Created texture: {} ({}x{})", pName->c_str(), width, height); #endif } // CreateDepthStencilSurface PPC_FUNC_IMPL(__imp__sub_82619B88); PPC_FUNC(sub_82619B88) { auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); if (g_pBlockName) { if (strcmp(g_pBlockName, "radermap0") == 0) { ctx.r5.u32 = g_radarMapScale; ctx.r6.u32 = g_radarMapScale; } // RenderMefiress if (strcmp(g_pBlockName, "user0") == 0 && *pName == "depthstencil_256") { ctx.r5.u32 = static_cast(Config::ShadowResolution.Value); ctx.r6.u32 = static_cast(Config::ShadowResolution.Value); } } if (*pName == "depthstencil_1_4") { ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * ReflectionScaleFactor(Config::ReflectionResolution)); ctx.r6.u32 = static_cast(static_cast(ctx.r6.u32) * ReflectionScaleFactor(Config::ReflectionResolution)); // Bad hack to stop EDRAM cache from messing up if (Config::ReflectionResolution == EReflectionResolution::Full) ctx.r5.u32++; } #if _DEBUG auto width = ctx.r5.u32; auto height = ctx.r6.u32; #endif __imp__sub_82619B88(ctx, base); #if _DEBUG if (g_pBlockName) { LOGFN_UTILITY("Created texture for {}: {} ({}x{})", g_pBlockName, pName->c_str(), width, height); } else { LOGFN_UTILITY("Created texture: {} ({}x{})", pName->c_str(), width, height); } #endif } float ShadowScaleFactor(EShadowResolution ref) { switch (ref) { case EShadowResolution::x512: return 0.5f; case EShadowResolution::x1024: return 1.0f; case EShadowResolution::x2048: return 2.0f; case EShadowResolution::x4096: return 4.0f; case EShadowResolution::x8192: return 8.0f; default: return 1.0f; } } // CreateArrayTexture PPC_FUNC_IMPL(__imp__sub_82619FF0); PPC_FUNC(sub_82619FF0) { auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); if (*pName == "csm") { ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * ShadowScaleFactor(Config::ShadowResolution)); ctx.r6.u32 = static_cast(static_cast(ctx.r6.u32) * ShadowScaleFactor(Config::ShadowResolution)); } __imp__sub_82619FF0(ctx, base); } std::string g_renderWorldFBO; void GetRenderWorldFBO(PPCRegister& name) { auto pName = xpointer(reinterpret_cast(name.u32)); g_renderWorldFBO = std::string(pName.get()); } void FurtherObjectShadows(PPCRegister& scope) { if (g_renderWorldFBO != "shadowmap") return; scope.u32 = 1; } bool DisableRadialBlur() { return Config::RadialBlur == ERadialBlur::Off; } bool DisableKingdomValleyMist() { return Config::DisableKingdomValleyMist; } ================================================ FILE: MarathonRecomp/preload_executable.cpp ================================================ #include "preload_executable.h" #include // Code from Zelda 64: Recompiled // https://github.com/Zelda64Recomp/Zelda64Recomp/blob/91db87632c2bfb6995ef1554ec71b11977c621f8/src/main/main.cpp#L440-L514 PreloadContext::~PreloadContext() { #ifdef _WIN32 if (preloaded) { VirtualUnlock(view, size); CloseHandle(mappingHandle); CloseHandle(handle); } #endif } void PreloadContext::PreloadExecutable() { #ifdef _WIN32 wchar_t moduleName[MAX_PATH]; GetModuleFileNameW(NULL, moduleName, MAX_PATH); handle = CreateFileW(moduleName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (handle == INVALID_HANDLE_VALUE) { LOG_ERROR("Failed to load executable into memory!"); *this = {}; return; } LARGE_INTEGER moduleSize; if (!GetFileSizeEx(handle, &moduleSize)) { LOG_ERROR("Failed to get size of executable!"); CloseHandle(handle); *this = {}; return; } size = moduleSize.QuadPart; mappingHandle = CreateFileMappingW(handle, nullptr, PAGE_READONLY, 0, 0, nullptr); if (mappingHandle == nullptr) { LOG_ERROR("Failed to create file mapping of executable!"); CloseHandle(handle); *this = {}; return; } view = MapViewOfFile(mappingHandle, FILE_MAP_READ, 0, 0, 0); if (view == nullptr) { LOG_ERROR("Failed to map view of of executable!"); CloseHandle(mappingHandle); CloseHandle(handle); *this = {}; return; } DWORD pid = GetCurrentProcessId(); HANDLE processHandle = OpenProcess(PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION, FALSE, pid); if (processHandle == nullptr) { LOG_ERROR("Failed to open own process!"); CloseHandle(mappingHandle); CloseHandle(handle); *this = {}; return; } SIZE_T minimumSetSize, maximumSetSize; if (!GetProcessWorkingSetSize(processHandle, &minimumSetSize, &maximumSetSize)) { LOG_ERROR("Failed to get working set size!"); CloseHandle(mappingHandle); CloseHandle(handle); *this = {}; return; } if (!SetProcessWorkingSetSize(processHandle, minimumSetSize + size, maximumSetSize + size)) { LOG_ERROR("Failed to set working set size!"); CloseHandle(mappingHandle); CloseHandle(handle); *this = {}; return; } if (VirtualLock(view, size) == 0) { LOGF_ERROR("Failed to lock view of executable! (Error: 0x{:X})\n", GetLastError()); CloseHandle(mappingHandle); CloseHandle(handle); *this = {}; return; } preloaded = true; #endif } ================================================ FILE: MarathonRecomp/preload_executable.h ================================================ #pragma once struct PreloadContext { #ifdef _WIN32 HANDLE handle{}; HANDLE mappingHandle{}; SIZE_T size{}; PVOID view{}; bool preloaded{}; #endif ~PreloadContext(); void PreloadExecutable(); }; ================================================ FILE: MarathonRecomp/res/.gitignore ================================================ ![Ww][Ii][Nn]32/ *.c *.h !credits.h ================================================ FILE: MarathonRecomp/res/credits.h ================================================ #pragma once inline std::array g_credits = { "ga2mer", "IsaacMarovitz", "squidbus", "Hyper", "Rei-san", "Desko", "LJSTAR", "brianuuuSonic", "Kitzuku", "Ray Vassos", "DaGuAr", "NextinHKRY" }; ================================================ FILE: MarathonRecomp/res/macos/MacOSXBundleInfo.plist.in ================================================ CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} LSMinimumSystemVersion 13.0 LSApplicationCategoryType public.app-category.games GCSupportsGameMode NSHighResolutionCapable ================================================ FILE: MarathonRecomp/res/version.cpp.template ================================================ #include "version.h" // This file is auto-generated, do not modify! const char* g_buildType = "@BUILD_TYPE@"; const char* g_branchName = "@BRANCH_NAME@"; const char* g_commitHash = "@COMMIT_HASH@"; const char* g_commitHashShort = "@COMMIT_HASH_SHORT@"; const char* g_versionMilestone = "@VERSION_MILESTONE@"; size_t g_versionMajor = @VERSION_MAJOR@; size_t g_versionMinor = @VERSION_MINOR@; size_t g_versionRevision = @VERSION_REVISION@; const char* g_versionString = "@VERSION_STRING@"; ================================================ FILE: MarathonRecomp/res/version.h.template ================================================ #pragma once // This file is auto-generated, do not modify! extern const char* g_buildType; extern const char* g_branchName; extern const char* g_commitHash; extern const char* g_commitHashShort; extern const char* g_versionMilestone; extern size_t g_versionMajor; extern size_t g_versionMinor; extern size_t g_versionRevision; extern const char* g_versionString; ================================================ FILE: MarathonRecomp/res/version.txt ================================================ VERSION_MILESTONE="" VERSION_MAJOR=1 VERSION_MINOR=0 VERSION_REVISION=0 ================================================ FILE: MarathonRecomp/res/win32/res.rc.template ================================================ #include #define MARATHON_RECOMP_VERSION_BIN @WIN32_VERSION_BINARY@ #define MARATHON_RECOMP_VERSION_STR "@WIN32_VERSION_STRING@" #ifdef _DEBUG #define MARATHON_RECOMP_FILE_FLAGS VS_FF_DEBUG #else #define MARATHON_RECOMP_FILE_FLAGS 0 #endif IDI_ICON1 ICON "@WIN32_ICON_PATH@" VS_VERSION_INFO VERSIONINFO FILEVERSION MARATHON_RECOMP_VERSION_BIN PRODUCTVERSION MARATHON_RECOMP_VERSION_BIN FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS MARATHON_RECOMP_FILE_FLAGS FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080904B0" // English (UK), Unicode BEGIN VALUE "ProductName", "Marathon Recompiled" VALUE "FileDescription", "Marathon Recompiled" VALUE "CompanyName", "sonicnext-dev" VALUE "FileVersion", MARATHON_RECOMP_VERSION_STR VALUE "ProductVersion", MARATHON_RECOMP_VERSION_STR VALUE "InternalName", "MarathonRecomp" VALUE "OriginalFilename", "marathon.exe" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0809, 0x04B0 END END ================================================ FILE: MarathonRecomp/sdl_events.h ================================================ #pragma once #include #include #define SDL_USER_PLAYER_CHAR (SDL_USEREVENT + 1) inline void SDL_ResizeEvent(SDL_Window* pWindow, int width, int height) { SDL_Event event{}; event.type = SDL_WINDOWEVENT; event.window.event = SDL_WINDOWEVENT_RESIZED; event.window.windowID = SDL_GetWindowID(pWindow); event.window.data1 = width; event.window.data2 = height; SDL_PushEvent(&event); } inline void SDL_MoveEvent(SDL_Window* pWindow, int x, int y) { SDL_Event event{}; event.type = SDL_WINDOWEVENT; event.window.event = SDL_WINDOWEVENT_MOVED; event.window.windowID = SDL_GetWindowID(pWindow); event.window.data1 = x; event.window.data2 = y; SDL_PushEvent(&event); } inline void SDL_User_PlayerChar(EPlayerCharacter character) { SDL_Event event{}; event.type = SDL_USER_PLAYER_CHAR; event.user.code = static_cast(character); SDL_PushEvent(&event); } ================================================ FILE: MarathonRecomp/sdl_listener.cpp ================================================ #include "sdl_listener.h" std::vector& GetEventListeners() { static std::vector g_eventListeners; return g_eventListeners; } ================================================ FILE: MarathonRecomp/sdl_listener.h ================================================ #pragma once class ISDLEventListener { public: virtual ~ISDLEventListener() = default; virtual bool OnSDLEvent(SDL_Event* event) = 0; }; extern std::vector& GetEventListeners(); class SDLEventListener : public ISDLEventListener { public: SDLEventListener() { GetEventListeners().emplace_back(this); } ~SDLEventListener() override { auto& eventListeners = GetEventListeners(); auto it = std::find(eventListeners.begin(), eventListeners.end(), this); assert(it != eventListeners.end()); eventListeners.erase(it); } bool OnSDLEvent(SDL_Event* event) override { return false; } }; ================================================ FILE: MarathonRecomp/stdafx.cpp ================================================ #define STB_IMAGE_IMPLEMENTATION #include #include "stdafx.h" ================================================ FILE: MarathonRecomp/stdafx.h ================================================ #pragma once #define NOMINMAX #if defined(_WIN32) #include #include #include using Microsoft::WRL::ComPtr; #elif defined(__linux__) #include #include #endif #ifdef MARATHON_RECOMP_D3D12 #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 "framework.h" #include "mutex.h" #ifndef _WIN32 #include #endif ================================================ FILE: MarathonRecomp/ui/achievement_menu.cpp ================================================ #include "achievement_menu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr double CONTAINER_MOVE_OFFSET = 0; static constexpr double CONTAINER_MOVE_DURATION = 5; static constexpr double CONTAINER_FADE_OFFSET = CONTAINER_MOVE_OFFSET; static constexpr double CONTAINER_FADE_DURATION = 10; static constexpr double ROW_FADE_OFFSET = CONTAINER_MOVE_DURATION; static constexpr double ROW_FADE_TIME = CONTAINER_MOVE_DURATION; static constexpr int MAX_VISIBLE_ROWS = 4; static double g_time{}; static double g_rowSelectionTime{}; static double g_scrollArrowsTime{}; static double g_lastIncrementTime{}; static double g_lastTappedTime{}; static bool g_up{}; static bool g_upWasHeld{}; static bool g_down{}; static bool g_downWasHeld{}; static bool g_hasSwitched{}; static int g_rowCount{}; static int g_selectedIndex{}; static std::vector> g_achievements{}; static void MoveCursor(int& cursorIndex, int min = 0, int max = INT_MAX) { auto time = ImGui::GetTime(); auto scrollUp = g_up; auto scrollDown = g_down; if (scrollUp || scrollDown) g_lastTappedTime = time; static constexpr auto FAST_SCROLL_THRESHOLD = 0.3; static constexpr auto FAST_SCROLL_SPEED = 1.0 / 6.5; auto fastScroll = (time - g_lastTappedTime) > FAST_SCROLL_THRESHOLD; if (fastScroll) { if ((time - g_lastIncrementTime) < FAST_SCROLL_SPEED) { fastScroll = false; } else { g_lastIncrementTime = time; scrollUp = g_upWasHeld; scrollDown = g_downWasHeld; } } if (scrollUp) { --cursorIndex; if (cursorIndex < min) cursorIndex = max - 1; } else if (scrollDown) { ++cursorIndex; if (cursorIndex >= max) cursorIndex = min; } if (scrollUp || scrollDown) { Game_PlaySound("move"); g_rowSelectionTime = time; g_scrollArrowsTime = g_rowSelectionTime; } } static void DrawContainer(ImVec2 min, ImVec2 max) { auto drawList = ImGui::GetBackgroundDrawList(); auto containerTopCornerUVs = PIXELS_TO_UV_COORDS(1024, 1024, 1, 400, 50, 50); auto containerTopCentreUVs = PIXELS_TO_UV_COORDS(1024, 1024, 50, 400, 50, 50); auto containerSideUVs = PIXELS_TO_UV_COORDS(1024, 1024, 1, 450, 50, 50); auto containerCentreUVs = PIXELS_TO_UV_COORDS(1024, 1024, 50, 450, 50, 50); auto containerAlphaMotionTime = AchievementMenu::IsClosing() ? 0 : ComputeLinearMotion(g_time, CONTAINER_FADE_OFFSET, CONTAINER_FADE_DURATION); auto containerAlphaMotion = AchievementMenu::s_state == AchievementMenuState::GoldMedals ? Lerp(63, 0, containerAlphaMotionTime) : 63; auto containerColour = IM_COL32(255, 255, 255, containerAlphaMotion); auto containerEdgeSize = Scale(50, true); ImVec2 containerTopLeftCornerMin = min; ImVec2 containerTopLeftCornerMax = { containerTopLeftCornerMin.x + containerEdgeSize, containerTopLeftCornerMin.y + containerEdgeSize }; ImVec2 containerTopCentreCornerMin = { containerTopLeftCornerMax.x, containerTopLeftCornerMin.y }; ImVec2 containerTopCentreCornerMax = { max.x - containerEdgeSize, containerTopCentreCornerMin.y + containerEdgeSize }; ImVec2 containerTopRightCornerMin = { containerTopCentreCornerMax.x, containerTopCentreCornerMin.y }; ImVec2 containerTopRightCornerMax = { containerTopRightCornerMin.x + containerEdgeSize, containerTopRightCornerMin.y + containerEdgeSize }; ImVec2 containerLeftMin = { containerTopLeftCornerMin.x, containerTopLeftCornerMax.y }; ImVec2 containerLeftMax = { containerTopLeftCornerMax.x, max.y }; ImVec2 containerRightMin = { containerTopRightCornerMin.x, containerTopRightCornerMax.y }; ImVec2 containerRightMax = { containerTopRightCornerMax.x, max.y }; ImVec2 containerCentreMin = containerTopLeftCornerMax; ImVec2 containerCentreMax = { containerRightMin.x, containerRightMax.y }; auto containerBottomFadeStart = Scale(40, true); auto containerBottomFadeEnd = Scale(10, true); ImVec2 containerBottomFadeMin = { containerLeftMin.x, containerLeftMax.y - containerBottomFadeStart }; ImVec2 containerBottomFadeMax = { containerRightMax.x, containerRightMax.y + containerBottomFadeEnd }; SetVerticalGradient(containerBottomFadeMin, containerBottomFadeMax, IM_COL32_WHITE, IM_COL32_WHITE_TRANS); drawList->AddImage(g_upTexMainMenu1.get(), containerTopLeftCornerMin, containerTopLeftCornerMax, GET_UV_COORDS(containerTopCornerUVs), containerColour); drawList->AddImage(g_upTexMainMenu1.get(), containerTopCentreCornerMin, containerTopCentreCornerMax, GET_UV_COORDS(containerTopCentreUVs), containerColour); AddImageFlipped(g_upTexMainMenu1.get(), containerTopRightCornerMin, containerTopRightCornerMax, GET_UV_COORDS(containerTopCornerUVs), containerColour, true); drawList->AddImage(g_upTexMainMenu1.get(), containerLeftMin, containerLeftMax, GET_UV_COORDS(containerSideUVs), containerColour); AddImageFlipped(g_upTexMainMenu1.get(), containerRightMin, containerRightMax, GET_UV_COORDS(containerSideUVs), containerColour, true); drawList->AddImage(g_upTexMainMenu1.get(), containerCentreMin, containerCentreMax, GET_UV_COORDS(containerCentreUVs), containerColour); ResetGradient(); drawList->PushClipRect(containerTopLeftCornerMin, containerRightMax); } static void DrawAchievement(int rowIndex, Achievement& achievement, bool isUnlocked) { auto drawList = ImGui::GetBackgroundDrawList(); auto clipRectMin = drawList->GetClipRectMin(); auto clipRectMax = drawList->GetClipRectMax(); auto itemHeight = Scale(94, true); auto itemMarginX = Scale(18, true); auto imageMarginX = Scale(34, true); auto imageMarginY = Scale(20, true); auto imageSize = Scale(64, true); auto offsetScroll = 0.0f; auto startIndex = 0; // Only scroll if page has more than four items. if (g_rowCount > MAX_VISIBLE_ROWS) { if (g_selectedIndex >= g_rowCount - (MAX_VISIBLE_ROWS - 1)) { // Stop scrolling near bottom to use cursor instead. startIndex = g_rowCount - MAX_VISIBLE_ROWS; offsetScroll = -startIndex * itemHeight; } else if (g_selectedIndex >= 1) { // Start scrolling from the middle item. startIndex = g_selectedIndex - 1; offsetScroll = -startIndex * itemHeight; } } auto offsetY = itemHeight * rowIndex + offsetScroll; auto isCurrent = g_selectedIndex == rowIndex; auto isVisible = ((rowIndex - startIndex + g_rowCount) % g_rowCount) < MAX_VISIBLE_ROWS; if (!isVisible) return; ImVec2 min = { clipRectMin.x + itemMarginX, clipRectMin.y + offsetY }; ImVec2 max = { clipRectMax.x + itemMarginX, min.y + itemHeight }; auto rowUVs = PIXELS_TO_UV_COORDS(1024, 1024, 605, 449, 10, 30); auto rowMarginX = Scale(2, true); auto rowMarginY = Scale(24, true); auto rowColourBreatheMotionTime = BREATHE_MOTION(1.0f, 0.0f, g_rowSelectionTime, 0.9f); auto rowColourTransitionMotionTime = ComputeLinearMotion(g_time, ROW_FADE_OFFSET, ROW_FADE_TIME); constexpr auto rowColourAlphaMax = 165; constexpr auto rowColourAlphaMin = 115; auto rowColourAlpha = isCurrent ? Lerp(rowColourAlphaMax, rowColourAlphaMin, rowColourBreatheMotionTime) * rowColourTransitionMotionTime : rowColourAlphaMin * rowColourTransitionMotionTime; auto rowColour = IM_COL32(255, 255, 255, rowColourAlpha); ImVec2 rowMin = { clipRectMin.x + rowMarginX, min.y + itemHeight - rowMarginY }; ImVec2 rowMax = { clipRectMax.x - rowMarginX, rowMin.y + Scale(30, true) }; SetAdditive(true); SetVerticalGradient(rowMin, rowMax, IM_COL32_WHITE_TRANS, rowColour); drawList->AddImage(g_upTexMainMenu1.get(), rowMin, rowMax, GET_UV_COORDS(rowUVs), rowColour); ResetGradient(); ResetAdditive(); auto image = g_xdbfTextureCache[achievement.ID]; ImVec2 imageMin = { min.x + imageMarginX, min.y + imageMarginY }; ImVec2 imageMax = { min.x + imageMarginX + imageSize, min.y + imageMarginY + imageSize }; if (isCurrent) DrawArrowCursor({ rowMin.x + Scale(2, true), imageMin.y + ((imageMax.y - imageMin.y) / 3)}, g_time, false); if (!isUnlocked) SetShaderModifier(IMGUI_SHADER_MODIFIER_GRAYSCALE); // Draw achievement image. drawList->AddImage(image, imageMin, imageMax, { 0, 0 }, { 1, 1 }, IM_COL32_WHITE); if (!isUnlocked) SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); auto fontSize = Scale(Config::Language == ELanguage::Japanese ? 28 : 27, true); auto textX = imageMax.x + itemMarginX * 2; auto textColour = isUnlocked ? IM_COL32_WHITE : IM_COL32(137, 137, 137, 255); std::string name; std::string lockedDesc; std::string unlockedDesc; if (Config::UseOfficialAchievementText) { name = achievement.Name; lockedDesc = achievement.LockedDesc; unlockedDesc = achievement.UnlockedDesc; } else { auto& newLocale = GetAchievementLocale(achievement.ID); name = newLocale.Name; lockedDesc = newLocale.LockedDesc; unlockedDesc = newLocale.UnlockedDesc; } auto descText = isUnlocked ? unlockedDesc.c_str() : lockedDesc.c_str(); auto descTextOffsetY = Scale(32, true); auto descSize = g_pFntRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, descText); ImVec2 namePos = { textX, imageMin.y + Scale(3, true) }; ImVec2 descPos = { textX, namePos.y + descTextOffsetY }; // Draw achievement name. SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); drawList->AddText(g_pFntRodin, fontSize, namePos, textColour, name.c_str()); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); auto timestampOffsetX = Scale(60, true); ImVec2 marqueeMin = { textX, min.y }; ImVec2 marqueeMax = { max.x - timestampOffsetX, max.y }; constexpr auto marqueeDelay = 0.9; auto shouldMarquee = isCurrent && marqueeMin.x + descSize.x >= marqueeMax.x; // Reduce clip rect size when marquee starts to make the // scrolling text align more with text above or below it. if (shouldMarquee && ImGui::GetTime() - (g_rowSelectionTime + marqueeDelay) > 0.0) { marqueeMin.x += Scale(2, true); marqueeMax.x -= Scale(1, true); } SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); DrawTextWithMarquee(g_pFntRodin, fontSize, descPos, marqueeMin, marqueeMax, textColour, descText, g_rowSelectionTime, marqueeDelay, shouldMarquee ? Scale(200, true) : 0); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); if (!isUnlocked) return; auto timestamp = AchievementManager::GetTimestamp(achievement.ID); if (!timestamp) return; char buffer[32]; #ifdef _WIN32 tm time{}; tm* pTime = &time; localtime_s(pTime, ×tamp); #else tm* pTime = localtime(×tamp); #endif snprintf(buffer, sizeof(buffer), "%d/%d/%d %02d:%02d", pTime->tm_year + 1900, pTime->tm_mon + 1, pTime->tm_mday, pTime->tm_hour, pTime->tm_min); auto timestampSize = g_pFntRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, buffer); // Draw timestamp text. SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); drawList->AddText(g_pFntRodin, fontSize, { max.x - timestampSize.x - timestampOffsetX, namePos.y }, IM_COL32_WHITE, buffer); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); } void AchievementMenu::Draw() { if (AchievementMenu::IsClosing() && s_commonMenu.Close() >= 1.0) { s_isVisible = false; return; } if (!s_isVisible) return; auto* drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; if (s_commonMenu.ShowDescription) { auto horzMargin = Scale(128, true); auto gradientTop = IM_COL32(0, 103, 255, 255); auto gradientBottom = IM_COL32(0, 41, 100, 255); ImVec2 footerClipMin = { g_horzCentre + horzMargin, res.y - g_vertCentre - Scale(152, true) }; ImVec2 footerClipMax = { res.x - g_horzCentre - horzMargin, res.y - g_vertCentre - Scale(107, true) }; drawList->PushClipRect(footerClipMin, footerClipMax); drawList->AddRectFilledMultiColor({ 0.0f, g_vertCentre }, { res.x, res.y - g_vertCentre }, gradientTop, gradientTop, gradientBottom, gradientBottom); drawList->PopClipRect(); } s_commonMenu.Draw(); if (s_pMainMenuTask && s_pMainMenuTask->m_State == Sonicteam::MainMenuTask::MainMenuState_GoldMedalResults) { if (auto& spInputManager = App::s_pApp->m_pDoc->m_vspInputManager[0]) { auto& rPadState = spInputManager->m_PadState; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_B)) { AchievementMenu::Close(); ButtonWindow::Close(); } switch (AchievementMenu::s_state) { case AchievementMenuState::GoldMedals: { if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_Y)) { AchievementMenu::SetState(AchievementMenuState::Achievements); ButtonWindow::Open("Button_GoldMedalsBack"); SetGoldMedalResultsVisible(false); Game_PlaySound("window_open"); g_hasSwitched = true; } break; } case AchievementMenuState::Achievements: { // Discard all buttons besides B. if ((s_pMainMenuTask->m_PressedButtons.get() & 0x20) == 0) s_pMainMenuTask->m_PressedButtons = 0; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_Y)) { AchievementMenu::SetState(AchievementMenuState::GoldMedals); ButtonWindow::Open("Button_AchievementsBack"); SetGoldMedalResultsVisible(true); Game_PlaySound("window_close"); } break; } } } } auto containerMotionTime = ComputeLinearMotion(g_time, CONTAINER_MOVE_OFFSET, CONTAINER_MOVE_DURATION, s_state == AchievementMenuState::GoldMedals); auto containerTop = Scale(Lerp(217, 148, containerMotionTime), true); auto containerBottom = Scale(554, true); auto containerWidth = Scale(1158, true); ImVec2 min = { (res.x / 2) - containerWidth / 2, g_vertCentre + containerTop }; ImVec2 max = { min.x + containerWidth, g_vertCentre + containerBottom }; switch (s_state) { case AchievementMenuState::GoldMedals: { s_commonMenu.SetTitle(Localise("Achievements_GoldMedals_Uppercase")); s_commonMenu.ShowDescription = false; if (g_hasSwitched) { // For outro animation. DrawContainer(min, max); drawList->PopClipRect(); } break; } case AchievementMenuState::Achievements: { s_commonMenu.SetTitle(Localise("Achievements_Title_Uppercase")); s_commonMenu.ShowDescription = true; DrawContainer(min, max); if (containerMotionTime >= 1.0) { auto rowIndex = 0; for (auto& tpl : g_achievements) { auto& achievement = std::get<0>(tpl); if (AchievementManager::IsUnlocked(achievement.ID)) DrawAchievement(rowIndex++, achievement, true); } for (auto& tpl : g_achievements) { auto& achievement = std::get<0>(tpl); if (!AchievementManager::IsUnlocked(achievement.ID)) DrawAchievement(rowIndex++, achievement, false); } drawList->PopClipRect(); auto scrollArrowsOffsetX = Scale(17, true); auto scrollArrowsOffsetTop = Scale(40, true); auto scrollArrowsOffsetBottom = Scale(60, true); ImVec2 scrollArrowsMin = { max.x + scrollArrowsOffsetX, min.y + scrollArrowsOffsetTop }; ImVec2 scrollArrowsMax = { scrollArrowsMin.x + scrollArrowsOffsetX, max.y - scrollArrowsOffsetBottom }; DrawScrollArrows(scrollArrowsMin, scrollArrowsMax, Scale(25, true), g_scrollArrowsTime, g_selectedIndex > MAX_VISIBLE_ROWS / 2, g_selectedIndex < g_rowCount - MAX_VISIBLE_ROWS / 2); auto upIsHeld = false; auto downIsHeld = false; for (auto& spInputManager : App::s_pApp->m_pDoc->m_vspInputManager) { auto& rPadState = spInputManager->m_PadState; if (rPadState.IsDown(Sonicteam::SoX::Input::KeyState_DPadUp) || -rPadState.LeftStickVertical > 0.5f) upIsHeld = true; if (!g_upWasHeld && upIsHeld) g_up = true; if (rPadState.IsDown(Sonicteam::SoX::Input::KeyState_DPadDown) || -rPadState.LeftStickVertical < -0.5f) downIsHeld = true; if (!g_downWasHeld && downIsHeld) g_down = true; } MoveCursor(g_selectedIndex, 0, g_rowCount); g_up = false; g_upWasHeld = upIsHeld; g_down = false; g_downWasHeld = downIsHeld; } drawList->PopClipRect(); break; } } } void AchievementMenu::Open(Sonicteam::MainMenuTask* pMainMenuTask) { s_pMainMenuTask = pMainMenuTask; if (s_isVisible) return; g_achievements.clear(); for (auto& achievement : g_xdbfWrapper.GetAchievements((EXDBFLanguage)Config::Language.Value)) { achievement.LockedDesc = xdbf::FixInvalidSequences(achievement.LockedDesc); g_achievements.push_back(std::make_tuple(achievement, AchievementManager::GetTimestamp(achievement.ID))); } std::sort(g_achievements.begin(), g_achievements.end(), [](const auto& a, const auto& b) { return std::get<1>(a) > std::get<1>(b); }); g_rowCount = g_achievements.size(); // Format achievement progress. char descriptionText[128]; snprintf(descriptionText, sizeof(descriptionText), Localise("Achievements_Progress").c_str(), AchievementManager::GetTotalRecords(), g_rowCount); s_commonMenu = CommonMenu(Localise("Achievements_GoldMedals_Uppercase"), descriptionText, false); s_commonMenu.ShowDescription = false; s_commonMenu.ShowVersionString = false; s_commonMenu.ReduceDraw = true; s_commonMenu.Open(); s_isVisible = true; s_state = AchievementMenuState::GoldMedals; g_time = ImGui::GetTime(); g_hasSwitched = false; g_selectedIndex = 0; ButtonWindow::Open("Button_AchievementsBack"); MainMenuTaskPatches::s_hideButtonWindow = true; } void AchievementMenu::Close() { if (AchievementMenu::IsClosing()) return; switch (s_state) { case AchievementMenuState::GoldMedals: s_state = AchievementMenuState::ClosingGoldMedals; break; case AchievementMenuState::Achievements: s_state = AchievementMenuState::ClosingAchievements; break; } g_time = ImGui::GetTime(); ButtonWindow::Close(); MainMenuTaskPatches::s_hideButtonWindow = false; } void AchievementMenu::SetState(AchievementMenuState state) { s_state = state; g_time = ImGui::GetTime(); g_rowSelectionTime = g_time; } bool AchievementMenu::IsClosing() { return s_state == AchievementMenuState::ClosingGoldMedals || s_state == AchievementMenuState::ClosingAchievements; } void AchievementMenu::SetGoldMedalResultsVisible(bool isVisible) { if (!s_pMainMenuTask) return; std::vector states; if (isVisible) { states = { 0, 3, 5, 11 }; } else { states = { 1, 4, 6, 12 }; } for (auto& state : states) { guest_stack_var msgChangeState(state, s_pMainMenuTask->m_GoldMedalEpisodeIndex); s_pMainMenuTask->m_pHUDGoldMedal->ProcessMessage(msgChangeState.get()); } for (int i = 0; i < 5; i++) { auto& spTextEntity = s_pMainMenuTask->m_pHUDGoldMedal->m_aspTextEntities[i]; if (auto pTextEntity = spTextEntity.get()) { for (size_t i = 0; i < pTextEntity->m_CharacterVertexCount; i++) pTextEntity->m_pCharacterVertices[i].Colour = isVisible ? 0xFFFFFFFF : 0x00FFFFFF; } } } ================================================ FILE: MarathonRecomp/ui/achievement_menu.h ================================================ #pragma once #include #include #define MARATHON_RECOMP_ACHIEVEMENT_MENU enum class AchievementMenuState { GoldMedals, Achievements, ClosingGoldMedals, ClosingAchievements }; class AchievementMenu { public: static inline CommonMenu s_commonMenu{}; static inline AchievementMenuState s_state{}; static inline Sonicteam::MainMenuTask* s_pMainMenuTask{}; static inline bool s_isVisible = false; static void Draw(); static void Open(Sonicteam::MainMenuTask* pMainMenuTask); static void Close(); static void SetState(AchievementMenuState state); static bool IsClosing(); static void SetGoldMedalResultsVisible(bool isVisible); }; ================================================ FILE: MarathonRecomp/ui/achievement_overlay.cpp ================================================ #include "achievement_overlay.h" #include #include #include #include #include #include #include #include #include constexpr double OVERLAY_DURATION = 5.0; static bool g_isClosing{}; static double g_appearTime{}; static Achievement g_achievement{}; // Dequeue achievements only in the main thread. This is also extra thread safety. static std::thread::id g_mainThreadId = std::this_thread::get_id(); static bool CanDequeueAchievement() { return std::this_thread::get_id() == g_mainThreadId && !AchievementOverlay::s_queue.empty(); } void AchievementOverlay::Draw() { if (!AchievementOverlay::s_isVisible && CanDequeueAchievement()) { s_isVisible = true; g_isClosing = false; g_appearTime = ImGui::GetTime(); g_achievement = g_xdbfWrapper.GetAchievement((EXDBFLanguage)Config::Language.Value, s_queue.front()); s_queue.pop(); Game_PlaySound("deside"); } if (!s_isVisible) return; if (ImGui::GetTime() - g_appearTime >= OVERLAY_DURATION) AchievementOverlay::Close(); auto drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; auto& strAchievementUnlocked = Localise("Achievements_Unlock"); auto& strAchievementName = g_achievement.Name; if (!Config::UseOfficialAchievementText) strAchievementName = GetAchievementLocale(g_achievement.ID).Name; // Calculate text sizes. auto fontSize = Scale(Config::Language == ELanguage::Japanese ? 28 : 27, true); auto headerSize = g_pFntRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, strAchievementUnlocked.c_str()); auto nameSize = g_pFntRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, strAchievementName.c_str()); auto nameOffsetY = Scale(6); auto maxWidth = std::max(headerSize.x, nameSize.x) + Scale(5); auto maxHeight = headerSize.y + nameSize.y + nameOffsetY; // Calculate margins. auto imageMarginX = Scale(25); auto imageMarginY = Scale(20); auto imageSize = Scale(64); auto textMarginX = imageMarginX * 2 + imageSize - Scale(5); auto containerWidth = imageMarginX + textMarginX + maxWidth; auto containerHeight = Scale(105); ImVec2 min = { (res.x / 2) - (containerWidth / 2), Scale(55) }; ImVec2 max = { min.x + containerWidth, min.y + containerHeight }; // Calculate centred vertical text offset. auto textMarginY = (min.y + (containerHeight / 2)) - (maxHeight / 2); auto windowMotion = DrawWindow(min, max, true, g_appearTime, g_isClosing); if (windowMotion >= 1.0) { // Draw achievement icon. drawList->AddImage ( g_xdbfTextureCache[g_achievement.ID], { /* X */ min.x + imageMarginX, /* Y */ min.y + imageMarginY }, { /* X */ min.x + imageMarginX + imageSize, /* Y */ min.y + imageMarginY + imageSize }, { 0, 0 }, { 1, 1 }, IM_COL32(255, 255, 255, 255) ); SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); auto colourMotion = BREATHE_MOTION(1.0f, 0.0f, g_appearTime, 0.9f); auto headerColour = ColourLerp(IM_COL32_WHITE, IM_COL32(255, 153, 0, 255), colourMotion); // Draw header text. drawList->AddText ( g_pFntRodin, fontSize, { /* X */ min.x + textMarginX + (maxWidth - headerSize.x) / 2, /* Y */ textMarginY }, headerColour, strAchievementUnlocked.c_str() ); // Draw achievement name. drawList->AddText ( g_pFntRodin, fontSize, { /* X */ min.x + textMarginX + (maxWidth - nameSize.x) / 2, /* Y */ textMarginY + nameSize.y + nameOffsetY }, IM_COL32(255, 255, 255, 255), strAchievementName.c_str() ); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); } else if (windowMotion <= 0.0 && g_isClosing) { s_isVisible = false; } drawList->PopClipRect(); } void AchievementOverlay::Open(int id) { s_queue.push(id); } void AchievementOverlay::Close() { if (!g_isClosing) { g_appearTime = ImGui::GetTime(); g_isClosing = true; } if (CanDequeueAchievement()) s_isVisible = false; } ================================================ FILE: MarathonRecomp/ui/achievement_overlay.h ================================================ #pragma once #include class AchievementOverlay { public: static inline bool s_isVisible = false; static inline std::queue s_queue{}; static void Draw(); static void Open(int id); static void Close(); }; ================================================ FILE: MarathonRecomp/ui/black_bar.cpp ================================================ #include "black_bar.h" #include #include #include static bool g_isVisible{}; static bool g_isEdgeFade{}; void BlackBar::Draw() { s_pillarboxWidth = std::max(0.0f, (Video::s_viewportWidth - (Video::s_viewportHeight * WIDE_ASPECT_RATIO)) / 2.0f); s_letterboxHeight = std::max(0.0f, (Video::s_viewportHeight - (Video::s_viewportWidth / WIDE_ASPECT_RATIO)) / 2.0f); if (!g_isVisible) return; auto* drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; auto fadeSize = Scale(50, true); if (g_aspectRatio > WIDE_ASPECT_RATIO) { ImVec2 leftPillarboxMin = { 0, 0 }; ImVec2 leftPillarboxMax = { s_pillarboxWidth - s_margin, res.y }; ImVec2 rightPillarboxMin = { (res.x - s_pillarboxWidth) + s_margin, 0 }; ImVec2 rightPillarboxMax = { res.x, res.y }; if (g_isEdgeFade) SetHorizontalGradient({ leftPillarboxMax.x - fadeSize, leftPillarboxMin.y }, leftPillarboxMax, IM_COL32_BLACK, IM_COL32_BLACK_TRANS); drawList->AddRectFilled(leftPillarboxMin, leftPillarboxMax, IM_COL32_BLACK); if (g_isEdgeFade) SetHorizontalGradient(rightPillarboxMin, { rightPillarboxMin.x + fadeSize, rightPillarboxMax.y }, IM_COL32_BLACK_TRANS, IM_COL32_BLACK); drawList->AddRectFilled(rightPillarboxMin, rightPillarboxMax, IM_COL32_BLACK); } else if (WIDE_ASPECT_RATIO > g_aspectRatio) { ImVec2 topLetterboxMin = { 0, 0 }; ImVec2 topLetterboxMax = { res.x, s_letterboxHeight - s_margin }; ImVec2 bottomLetterboxMin = { 0, res.y - s_letterboxHeight + s_margin }; ImVec2 bottomLetterboxMax = { res.x, res.y }; if (g_isEdgeFade) SetVerticalGradient({ topLetterboxMin.x, topLetterboxMax.y - fadeSize }, topLetterboxMax, IM_COL32_BLACK, IM_COL32_BLACK_TRANS); drawList->AddRectFilled(topLetterboxMin, topLetterboxMax, IM_COL32_BLACK); if (g_isEdgeFade) SetVerticalGradient(bottomLetterboxMin, { bottomLetterboxMax.x, bottomLetterboxMin.y + fadeSize }, IM_COL32_BLACK_TRANS, IM_COL32_BLACK); drawList->AddRectFilled(bottomLetterboxMin, bottomLetterboxMax, IM_COL32_BLACK); } if (g_isEdgeFade) ResetGradient(); if (App::s_isLoading) return; g_isVisible = false; g_isEdgeFade = false; s_margin = 0.0f; } void BlackBar::Show(bool isEdgeFade) { g_isVisible = true; g_isEdgeFade = isEdgeFade; } void BlackBar::Hide() { g_isVisible = false; } void BlackBar::SetBorderMargin(float margin) { s_margin = margin; } bool BlackBar::IsVisible() { return g_isVisible; } ================================================ FILE: MarathonRecomp/ui/black_bar.h ================================================ #pragma once class BlackBar { public: static constexpr double ms_MenuBorderMargin = 70.0; static inline float s_pillarboxWidth{}; static inline float s_letterboxHeight{}; static inline float s_margin{}; static void Draw(); static void Show(bool isEdgeFade = false); static void Hide(); static void SetBorderMargin(float margin); static bool IsVisible(); }; ================================================ FILE: MarathonRecomp/ui/button_window.cpp ================================================ #include "button_window.h" #include #include #include static std::string g_buttonKey{}; static double g_time{}; static bool g_isAnimated{}; void ButtonWindow::Draw() { if (!s_isVisible || g_buttonKey.empty()) return; auto* drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; auto interpData = GetHidInterpTextData(); auto fontSize = Scale(Config::Language == ELanguage::Japanese ? 28 : 27, true); auto buttonLocale = &Localise(g_buttonKey); auto windowEdgeUVs = PIXELS_TO_UV_COORDS(64, 64, 1, 0, 40, 64); auto windowStretchUVs = PIXELS_TO_UV_COORDS(64, 64, 40, 0, 23, 64); auto windowOffsetX = g_horzCentre + Scale(128, true); auto windowOffsetY = g_vertCentre + Scale(114.5, true); auto windowEdgeWidth = Scale(40, true); auto windowWidth = MeasureInterpolatedText(g_pFntRodin, fontSize, buttonLocale->c_str(), &interpData).x; auto windowHeight = Scale(64, true); auto windowColour = IM_COL32(255, 255, 255, 134); auto windowMotionTime = g_isAnimated ? ComputeLinearMotion(g_time, 0, 100) : 1.0; auto windowMotion = std::clamp(float(res.x - g_horzCentre - Scale(1158.3, true) * windowMotionTime), res.x - windowOffsetX - windowWidth, res.x - g_horzCentre); ImVec2 windowStretchMin = { windowMotion, res.y - windowOffsetY }; ImVec2 windowStretchMax = { res.x, windowStretchMin.y + windowHeight }; ImVec2 windowEdgeMin = { windowStretchMin.x - windowEdgeWidth, windowStretchMin.y }; ImVec2 windowEdgeMax = { windowEdgeMin.x + windowEdgeWidth, windowEdgeMin.y + windowHeight }; drawList->AddImage(g_upTexButtonWindow.get(), windowStretchMin, windowStretchMax, GET_UV_COORDS(windowStretchUVs), windowColour); drawList->AddImage(g_upTexButtonWindow.get(), windowEdgeMin, windowEdgeMax, GET_UV_COORDS(windowEdgeUVs), windowColour); ImVec2 textPos = { windowEdgeMax.x - Scale(7.5, true), windowEdgeMin.y + Scale(12.75, true) }; if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); DrawInterpolatedText(g_pFntRodin, fontSize, textPos, IM_COL32_WHITE, buttonLocale->c_str(), &interpData); if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); } void ButtonWindow::Open(std::string key, bool isAnimated) { s_isVisible = true; if (g_buttonKey != key) { g_buttonKey = key; g_time = ImGui::GetTime(); g_isAnimated = isAnimated; } } void ButtonWindow::Close() { s_isVisible = false; g_buttonKey.clear(); } ================================================ FILE: MarathonRecomp/ui/button_window.h ================================================ #pragma once class ButtonWindow { public: static inline bool s_isVisible = false; static void Draw(); static void Open(std::string key, bool isAnimated = true); static void Close(); }; ================================================ FILE: MarathonRecomp/ui/common_menu.cpp ================================================ #include "common_menu.h" #include #include #include #include static constexpr double ANIMATION_DURATION = 10.0; void CommonMenu::Draw() { auto* drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); ImVec2 min = { g_horzCentre, g_vertCentre }; ImVec2 max = { res.x - min.x, res.y - min.y }; auto borderMotionTime = PlayTransitions ? ComputeLinearMotion(m_time, 0, 10, m_isClosing) : 1.0; auto topPlateCornerUVs = PIXELS_TO_UV_COORDS(1024, 1024, 0, 154, 250, 144); auto topPlateLeftStretchUVs = PIXELS_TO_UV_COORDS(1024, 1024, 2, 154, 150, 144); auto topPlateRightStretchUVs = PIXELS_TO_UV_COORDS(1024, 1024, 250, 154, 750, 144); auto topPlateHeight = Scale(145, true); auto topPlateMotion = Lerp(min.y - topPlateHeight, min.y - Scale(0.2, true), borderMotionTime); ImVec2 topPlateCornerMin = { min.x - Scale(46, true), topPlateMotion }; ImVec2 topPlateCornerMax = { topPlateCornerMin.x + Scale(250, true), topPlateMotion + topPlateHeight }; ImVec2 topPlateStretchMin = { topPlateCornerMax.x, topPlateCornerMin.y }; ImVec2 topPlateStretchMax = { res.x, topPlateCornerMax.y }; auto redStripCornerUVs = PIXELS_TO_UV_COORDS(1024, 1024, 0, 301, 300, 50); auto redStripLeftStretchUVs = PIXELS_TO_UV_COORDS(1024, 1024, 0, 301, 150, 50); auto redStripRightStretchUVs = PIXELS_TO_UV_COORDS(1024, 1024, 300, 301, 300, 50); auto redStripColour = IM_COL32(168, 15, 15, 255); auto redStripOffsetY = Scale(81.5, true); auto redStripHeight = Scale(50, true); auto redStripMotion = topPlateMotion + redStripOffsetY; // TODO: account for metal plate height in motion. ImVec2 redStripCornerMin = { min.x - Scale(43.5, true), redStripMotion }; ImVec2 redStripCornerMax = { redStripCornerMin.x + Scale(300, true), redStripMotion + redStripHeight }; auto gradientTop = IM_COL32(0, 103, 255, 255); auto gradientBottom = IM_COL32(0, 92, 229, 255); auto gradientHeight = Scale(120, true); // Draw gradient to fill gap between red strip and top metal plates. if (!ReduceDraw) drawList->AddRectFilledMultiColor({ 0.0f, topPlateMotion }, { res.x, topPlateMotion + gradientHeight }, gradientTop, gradientTop, gradientBottom, gradientBottom); if (!ReduceDraw) { // Draw corner left red strip. drawList->AddImage(g_upTexMainMenu1.get(), redStripCornerMin, redStripCornerMax, GET_UV_COORDS(redStripCornerUVs), redStripColour); // Draw left stretched red strip. drawList->AddImage(g_upTexMainMenu1.get(), { 0.0f, redStripCornerMin.y }, { redStripCornerMin.x, redStripCornerMax.y }, GET_UV_COORDS(redStripLeftStretchUVs), redStripColour); // Draw right stretched red strip. drawList->AddImage(g_upTexMainMenu1.get(), { redStripCornerMax.x, redStripCornerMin.y }, { res.x, redStripCornerMax.y }, GET_UV_COORDS(redStripRightStretchUVs), redStripColour); auto redStripHighlightWidth = Scale(270, true); auto redStripHighlightHeight = Scale(48, true); auto redStripHighlightY = redStripCornerMin.y - Scale(3, true); auto drawRedStripHighlight = [&](ImVec2 redStripHighlightMin, uint32_t tlColour, uint32_t brColour) { auto redStripHighlightUVs = PIXELS_TO_UV_COORDS(1024, 1024, 201, 501, 264, 50); ImVec2 redStripHighlightMax = { redStripHighlightMin.x + redStripHighlightWidth, redStripHighlightMin.y + redStripHighlightHeight }; SetAdditive(true); SetHorizontalGradient(redStripHighlightMin, redStripHighlightMax, tlColour, brColour); drawList->AddImage(g_upTexMainMenu1.get(), redStripHighlightMin, redStripHighlightMax, GET_UV_COORDS(redStripHighlightUVs)); ResetGradient(); ResetAdditive(); }; // Draw fixed highlights for red strip. drawRedStripHighlight({ redStripCornerMax.x + Scale(68, true), redStripHighlightY }, IM_COL32(255, 172, 0, 0), IM_COL32(255, 172, 0, 67)); drawRedStripHighlight({ redStripCornerMax.x + Scale(-43.5, true), redStripHighlightY }, IM_COL32(255, 89, 0, 0), IM_COL32(255, 89, 0, 20)); auto redStripHighlightMotionOffsetX = Scale(63, true); auto redStripHighlightMotionX1Time = ComputeLinearMotion(m_titleTime, PlayTransitions ? 10 : 0, 10, m_isClosing); auto redStripHighlightMotionX2Time = ComputeLinearMotion(m_titleTime, PlayTransitions ? 12 : 2, 10, m_isClosing); auto redStripHighlightMotionAlphaIn1Time = ComputeLinearMotion(m_titleTime, PlayTransitions ? 10 : 0, 8, m_isClosing); auto redStripHighlightMotionAlphaIn2Time = ComputeLinearMotion(m_titleTime, PlayTransitions ? 12 : 2, 8, m_isClosing); auto redStripHighlightMotionAlphaOut1Time = ComputeLinearMotion(m_titleTime, PlayTransitions ? 18 : 8, 2, m_isClosing); auto redStripHighlightMotionAlphaOut2Time = ComputeLinearMotion(m_titleTime, PlayTransitions ? 20 : 2, 2, m_isClosing); auto redStripHighlightMotionX1 = Lerp(res.x + redStripHighlightWidth, min.x + redStripHighlightMotionOffsetX, redStripHighlightMotionX1Time); auto redStripHighlightMotionX2 = Lerp(res.x + redStripHighlightWidth, min.x + redStripHighlightMotionOffsetX, redStripHighlightMotionX2Time); auto redStripHighlightMotionAlpha1 = Lerp(0.0, 100.0, redStripHighlightMotionAlphaIn1Time); auto redStripHighlightMotionAlpha2 = Lerp(0.0, 100.0, redStripHighlightMotionAlphaIn2Time); if (redStripHighlightMotionAlphaIn1Time >= 1.0) redStripHighlightMotionAlpha1 = Lerp(100.0, 0.0, redStripHighlightMotionAlphaOut1Time); if (redStripHighlightMotionAlphaIn2Time >= 1.0) redStripHighlightMotionAlpha2 = Lerp(100.0, 0.0, redStripHighlightMotionAlphaOut2Time); // Draw animated highlights for title animation. drawRedStripHighlight({ redStripHighlightMotionX1, redStripHighlightY }, IM_COL32(255, 172, 0, 0), IM_COL32(255, 172, 0, redStripHighlightMotionAlpha1)); drawRedStripHighlight({ redStripHighlightMotionX2, redStripHighlightY }, IM_COL32(255, 172, 0, 0), IM_COL32(255, 172, 0, redStripHighlightMotionAlpha2)); } auto titleText = Title.empty() ? "DUMMY" : Title.data(); auto titleFontSize = Scale(33, true); auto titleSize = g_pFntNewRodin->CalcTextSizeA(titleFontSize, FLT_MAX, 0.0f, titleText); auto titleOffsetX = redStripCornerMax.x - Scale(105, true); auto titleOffsetY = redStripMotion + Scale(3.5, true); auto titleMotionTime = ComputeLinearMotion(m_titleTime, PlayTransitions && !m_isClosing ? 10 : 0, 10, m_isClosing); auto titleOffsetXMotion = Lerp(max.x + titleSize.x, titleOffsetX, titleMotionTime); if (!ReduceDraw && !m_previousTitle.empty()) { auto prevTitleAlphaMotionTime = ComputeLinearMotion(m_titleTime, PlayTransitions ? 10 : 0, 3, m_isClosing); // Draw previous title fading out. drawList->AddText(g_pFntNewRodin, titleFontSize, { titleOffsetX, titleOffsetY }, IM_COL32(255, 255, 255, Lerp(255, 0, prevTitleAlphaMotionTime)), m_previousTitle.data()); } // Draw title. drawList->AddText(g_pFntNewRodin, titleFontSize, { titleOffsetXMotion, titleOffsetY }, IM_COL32(255, 255, 255, 255 * titleMotionTime), titleText); if (!ReduceDraw) { // Draw top left corner metal plate. drawList->AddImage(g_upTexMainMenu1.get(), topPlateCornerMin, topPlateCornerMax, GET_UV_COORDS(topPlateCornerUVs)); // Draw top right stretched metal plate. SetHorizontalGradient(topPlateStretchMin, topPlateStretchMax, IM_COL32_WHITE, IM_COL32(200, 200, 200, 255)); drawList->AddImage(g_upTexMainMenu1.get(), topPlateStretchMin, topPlateStretchMax, GET_UV_COORDS(topPlateRightStretchUVs)); ResetGradient(); // Draw top left stretched metal plate for ultrawide. if (g_aspectRatio > WIDE_ASPECT_RATIO) AddImageFlipped(g_upTexMainMenu1.get(), { 0.0f, topPlateCornerMin.y }, { topPlateCornerMin.x + Scale(2, true), topPlateCornerMax.y }, GET_UV_COORDS(topPlateLeftStretchUVs), IM_COL32_WHITE, true); // Draw flipped metal plates for narrow aspect ratios. if (g_aspectRatio < WIDE_ASPECT_RATIO) { ImVec2 topPlateCornerExtendMin = { topPlateCornerMin.x, topPlateCornerMin.y - topPlateHeight }; ImVec2 topPlateCornerExtendMax = { topPlateCornerMax.x, topPlateCornerMin.y + Scale(1, true) }; ImVec2 topPlateStretchExtendMin = { topPlateStretchMin.x, topPlateStretchMin.y - topPlateHeight }; ImVec2 topPlateStretchExtendMax = { topPlateStretchMax.x, topPlateStretchMin.y + Scale(1, true) }; AddImageFlipped(g_upTexMainMenu1.get(), topPlateCornerExtendMin, topPlateCornerExtendMax, GET_UV_COORDS(topPlateCornerUVs), IM_COL32_WHITE, false, true); SetHorizontalGradient(topPlateStretchExtendMin, topPlateStretchExtendMax, IM_COL32_WHITE, IM_COL32(200, 200, 200, 255)); AddImageFlipped(g_upTexMainMenu1.get(), topPlateStretchExtendMin, topPlateStretchExtendMax, GET_UV_COORDS(topPlateRightStretchUVs), IM_COL32_WHITE, false, true); ResetGradient(); } } if (ShowDescription) { auto textCoverCornerUVs = PIXELS_TO_UV_COORDS(1024, 1024, 801, 400, 150, 150); auto textCoverCornerExtendUVs = PIXELS_TO_UV_COORDS(1024, 1024, 801, 400, 125, 150); auto textCoverCentreUVs = PIXELS_TO_UV_COORDS(1024, 1024, 950, 400, 50, 150); auto textCoverCornerUVCompensation = Scale(2, true); auto textCoverOffsetY = Scale(16.4, true); auto textCoverWidth = Scale(149.5, true); auto textCoverHeight = Scale(150, true); auto textCoverMotion = Lerp(max.y + textCoverHeight, max.y - textCoverOffsetY - textCoverHeight, borderMotionTime); auto textCoverColour = IM_COL32(0, 23, 57, 255); ImVec2 textCoverCornerLeftMin = { min.x, textCoverMotion }; ImVec2 textCoverCornerLeftMax = { textCoverCornerLeftMin.x + textCoverWidth, textCoverCornerLeftMin.y + textCoverHeight }; ImVec2 textCoverCentreMin = { textCoverCornerLeftMax.x, textCoverCornerLeftMin.y }; ImVec2 textCoverCentreMax = { max.x - textCoverWidth, textCoverCornerLeftMax.y }; ImVec2 textCoverCornerRightMin = { max.x - textCoverWidth, textCoverCornerLeftMin.y }; ImVec2 textCoverCornerRightMax = { max.x, textCoverCornerLeftMax.y }; if (ReduceDraw) { auto horzMargin = Scale(128, true); ImVec2 textCoverClipMin = { textCoverCornerLeftMin.x + horzMargin, textCoverCornerLeftMin.y + Scale(14, true) }; ImVec2 textCoverClipMax = { textCoverCornerRightMax.x - horzMargin, textCoverCornerRightMax.y - Scale(90, true) }; drawList->PushClipRect(textCoverClipMin, textCoverClipMax); } if (!Description.empty()) { auto descFadeScale = Scale(20, true); auto descFontSize = Scale(Config::Language == ELanguage::Japanese ? 28 : 27, true); auto descSize = g_pFntRodin->CalcTextSizeA(descFontSize, FLT_MAX, 0.0f, Description.data()); ImVec2 descBoundsMin = { textCoverCentreMin.x - Scale(18, true), textCoverCentreMin.y + Scale(20, true) }; ImVec2 descBoundsMax = { textCoverCentreMax.x + Scale(18, true), textCoverCentreMax.y - Scale(90, true) }; auto descBoundsWidth = descBoundsMax.x - descBoundsMin.x; m_descPos = { descBoundsMin.x + ((descBoundsMax.x - descBoundsMin.x) / 2) - (descSize.x / 2), descBoundsMin.y + ((descBoundsMax.y - descBoundsMin.y) / 2) - (descSize.y / 2) }; if (descSize.x > descBoundsWidth) { auto descScrollMax = descSize.x - (descBoundsWidth - descFadeScale * 2); auto descScrollSpeed = Scale(150, true); auto descScrollDelay = 1.2f; if (descScrollMax > 0.0f) { auto horz = -m_inputListener.RightStickX; if (fabs(horz) > 0.25f) { m_isDescManualScrolling = true; m_descScrollOffset += horz * descScrollSpeed * App::s_deltaTime; } else if (m_isDescManualScrolling && fabs(horz) <= 0.25f) { m_isDescScrolling = false; m_isDescManualScrolling = false; m_descScrollTimer = 0.0f; m_descScrollDirection = horz > 0.0f ? 1.0f : -1.0f; } if (!m_isDescManualScrolling) { if (!m_isDescScrolling) { m_descScrollTimer += App::s_deltaTime; if (m_descScrollTimer >= descScrollDelay) m_isDescScrolling = true; } if (m_isDescScrolling) { m_descScrollOffset += descScrollSpeed * m_descScrollDirection * App::s_deltaTime; if (m_descScrollOffset >= descScrollMax) { m_isDescScrolling = false; m_descScrollOffset = descScrollMax; m_descScrollTimer = 0.0f; m_descScrollDirection = -1.0f; } else if (m_descScrollOffset <= 0.0f) { m_isDescScrolling = false; m_descScrollOffset = 0; m_descScrollTimer = 0.0f; m_descScrollDirection = 1.0f; } } } m_descScrollOffset = std::clamp(m_descScrollOffset, 0.0f, descScrollMax); } else { m_isDescScrolling = false; m_descScrollOffset = 0.0f; m_descScrollTimer = 0.0f; m_descScrollDirection = 1.0f; } m_descPos.x = (descBoundsMin.x + descFadeScale) - m_descScrollOffset; } auto descAlphaMotionTime = ComputeLinearMotion(m_descTime, PlayTransitions ? 10 : 0, 15, m_isClosing); // Draw text cover backdrop. drawList->AddRectFilled({ 0.0f, textCoverMotion }, { res.x, textCoverMotion + textCoverHeight }, IM_COL32(0, 0, 0, 65)); // Draw previous description fading out. if (!m_isClosing && !m_previousDesc.empty()) drawList->AddText(g_pFntRodin, descFontSize, m_previousDescPos, IM_COL32(255, 255, 255, Lerp(255, 0, descAlphaMotionTime)), m_previousDesc.data()); // Draw description. drawList->AddText(g_pFntRodin, descFontSize, m_descPos, IM_COL32(255, 255, 255, Lerp(0, 255, descAlphaMotionTime)), Description.data()); // Draw left text cover. drawList->AddImage(g_upTexMainMenu1.get(), { 0.0f, textCoverCornerLeftMin.y }, { textCoverCornerLeftMin.x + textCoverCornerUVCompensation, textCoverCornerLeftMax.y }, GET_UV_COORDS(textCoverCornerExtendUVs), textCoverColour); drawList->AddImage(g_upTexMainMenu1.get(), textCoverCornerLeftMin, textCoverCornerLeftMax, GET_UV_COORDS(textCoverCornerUVs), textCoverColour); // Draw centre text cover. drawList->AddImage(g_upTexMainMenu1.get(), textCoverCentreMin, textCoverCentreMax, GET_UV_COORDS(textCoverCentreUVs), textCoverColour); // Draw right text cover. AddImageFlipped(g_upTexMainMenu1.get(), { textCoverCornerRightMax.x - textCoverCornerUVCompensation, textCoverCornerRightMin.y }, { res.x, textCoverCornerRightMax.y }, GET_UV_COORDS(textCoverCornerExtendUVs), textCoverColour); AddImageFlipped(g_upTexMainMenu1.get(), textCoverCornerRightMin, textCoverCornerRightMax, GET_UV_COORDS(textCoverCornerUVs), textCoverColour, true); } else { // Draw blank text cover. drawList->AddRectFilled({ 0.0f, textCoverCornerLeftMin.y }, { res.x, textCoverCornerLeftMax.y }, textCoverColour); } } if (ReduceDraw) { drawList->PopClipRect(); } else { auto bottomPlateUVs = PIXELS_TO_UV_COORDS(1024, 1024, 1, -17, 700, 145); auto bottomPlateStretchUVs = PIXELS_TO_UV_COORDS(1024, 1024, 1, -17, 128, 145); auto bottomPlateOffsetX = Scale(-60, true); auto bottomPlateOffsetY = Scale(12, true); auto bottomPlateWidth = Scale(700, true); auto bottomPlateHeight = Scale(145, true); auto bottomPlateMotion = Lerp(max.y + bottomPlateHeight, (max.y + bottomPlateOffsetY) - bottomPlateHeight, borderMotionTime); ImVec2 bottomPlateLeftMin = { min.x + bottomPlateOffsetX, bottomPlateMotion }; ImVec2 bottomPlateLeftMax = { bottomPlateLeftMin.x + bottomPlateWidth, bottomPlateMotion + bottomPlateHeight }; ImVec2 bottomPlateRightMin = { max.x - bottomPlateWidth - bottomPlateOffsetX, bottomPlateLeftMin.y }; ImVec2 bottomPlateRightMax = { bottomPlateRightMin.x + bottomPlateWidth, bottomPlateLeftMax.y }; // Draw bottom left metal plate. drawList->AddImage(g_upTexMainMenu1.get(), bottomPlateLeftMin, bottomPlateLeftMax, GET_UV_COORDS(bottomPlateUVs)); // Draw bottom right metal plate. AddImageFlipped(g_upTexMainMenu1.get(), bottomPlateRightMin, bottomPlateRightMax, GET_UV_COORDS(bottomPlateUVs), IM_COL32_WHITE, true); // Draw stretched metal plates for ultrawide. if (g_aspectRatio > WIDE_ASPECT_RATIO) { // Draw bottom left stretched metal plate. AddImageFlipped(g_upTexMainMenu1.get(), { 0.0f, bottomPlateLeftMin.y }, { bottomPlateLeftMin.x, bottomPlateLeftMax.y }, GET_UV_COORDS(bottomPlateStretchUVs), IM_COL32_WHITE, true); // Draw bottom right stretched metal plate. AddImageFlipped(g_upTexMainMenu1.get(), { bottomPlateRightMax.x, bottomPlateRightMin.y }, { res.x, bottomPlateRightMax.y }, GET_UV_COORDS(bottomPlateStretchUVs), IM_COL32_WHITE, true); } // Draw flipped metal plates for narrow aspect ratios. if (g_aspectRatio < WIDE_ASPECT_RATIO) { ImVec2 bottomPlateLeftExtendMin = { bottomPlateLeftMin.x, bottomPlateLeftMax.y }; ImVec2 bottomPlateLeftExtendMax = { bottomPlateLeftMax.x, bottomPlateRightMax.y + bottomPlateHeight }; ImVec2 bottomPlateRightExtendMin = { bottomPlateRightMin.x, bottomPlateRightMax.y }; ImVec2 bottomPlateRightExtendMax = { bottomPlateRightMax.x, bottomPlateRightMax.y + bottomPlateHeight }; AddImageFlipped(g_upTexMainMenu1.get(), bottomPlateLeftExtendMin, bottomPlateLeftExtendMax, GET_UV_COORDS(bottomPlateUVs), IM_COL32_WHITE, false, true); AddImageFlipped(g_upTexMainMenu1.get(), bottomPlateRightExtendMin, bottomPlateRightExtendMax, GET_UV_COORDS(bottomPlateUVs), IM_COL32_WHITE, true, true); } } if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); if (ShowVersionString) { auto verAlphaMotionTime = PlayTransitions ? ComputeLinearMotion(m_time, 0, m_isClosing ? 3 : 10, m_isClosing) : 1.0; DrawVersionString(IM_COL32(0, 0, 0, 70 * verAlphaMotionTime)); } // Draw faded letterbox at narrow aspect ratios. if (g_aspectRatio < NARROW_ASPECT_RATIO) { BlackBar::Show(true); BlackBar::SetBorderMargin(Scale(BlackBar::ms_MenuBorderMargin, true)); } } void CommonMenu::Open() { m_isClosing = false; m_time = ImGui::GetTime(); m_titleTime = m_time; m_descTime = m_time; } double CommonMenu::Close(bool isAnimated) { if (!m_isClosing) { m_isClosing = true; m_time = ImGui::GetTime(); m_titleTime = m_time; m_descTime = m_time; } return isAnimated ? ComputeLinearMotion(m_time, 0, ANIMATION_DURATION) : 1.0; } bool CommonMenu::IsOpen() const { return !m_isClosing && ComputeLinearMotion(m_time, 0, ANIMATION_DURATION) >= 1.0; } void CommonMenu::SetTitle(std::string title, bool isAnimated) { if (Title == title) return; m_previousTitle = Title; Title = title; if (!isAnimated) return; m_titleTime = ImGui::GetTime(); } void CommonMenu::SetDescription(std::string desc, bool isAnimated) { if (Description == desc) return; m_previousDesc = Description; m_previousDescPos = m_descPos; Description = desc; resetDescScroll(); if (!isAnimated) return; m_descTime = ImGui::GetTime(); } ================================================ FILE: MarathonRecomp/ui/common_menu.h ================================================ #pragma once #include class CommonMenu { std::string m_previousTitle{}; std::string m_previousDesc{}; ImVec2 m_descPos{}; ImVec2 m_previousDescPos{}; bool m_isClosing{}; double m_time{}; double m_titleTime{}; double m_descTime{}; bool m_isDescScrolling{}; bool m_isDescManualScrolling{}; float m_descScrollOffset{}; float m_descScrollTimer{}; float m_descScrollDirection{ 1.0f }; class CommonMenuInputListener : public SDLEventListener { public: float RightStickX{}; bool OnSDLEvent(SDL_Event* event) override { if (event->type == SDL_CONTROLLERAXISMOTION && event->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTX) RightStickX = event->caxis.value / 32767.0f; return false; } } m_inputListener{}; void resetDescScroll() { m_isDescScrolling = false; m_descScrollOffset = 0.0f; m_descScrollTimer = 0.0f; m_descScrollDirection = 1.0f; } public: std::string Title{}; std::string Description{}; bool PlayTransitions{}; bool ShowDescription{ true }; bool ShowVersionString{ true }; bool ReduceDraw{}; CommonMenu() {} CommonMenu(std::string title, std::string desc, bool playTransitions = false) : Title(title), Description(desc), PlayTransitions(playTransitions) {} void Draw(); void Open(); double Close(bool isAnimated = true); bool IsOpen() const; void SetTitle(std::string title, bool isAnimated = true); void SetDescription(std::string desc, bool isAnimated = true); }; ================================================ FILE: MarathonRecomp/ui/fader.cpp ================================================ #include "fader.h" #include "imgui_utils.h" #include static bool g_isFading; static bool g_isFadeIn; static float g_startTime; static float g_duration; static ImU32 g_colour = IM_COL32_BLACK; static std::function g_endCallback; static float g_endCallbackDelay; void Fader::Draw() { if (!s_isVisible) return; auto time = (ImGui::GetTime() - g_startTime) / g_duration; auto alpha = 1.0f; if (time >= g_duration) { if (time >= g_duration + g_endCallbackDelay) { if (g_endCallback) { g_endCallback(); g_endCallback = nullptr; } g_isFading = false; } } else { alpha = g_isFadeIn ? Lerp(1, 0, time) : Lerp(0, 1, time); } if (g_isFadeIn && !g_isFading) return; auto colour = IM_COL32(g_colour & 0xFF, (g_colour >> 8) & 0xFF, (g_colour >> 16) & 0xFF, 255 * alpha); ImGui::GetBackgroundDrawList()->AddRectFilled({ 0, 0 }, ImGui::GetIO().DisplaySize, colour); } static void DoFade(bool isFadeIn, float duration, std::function endCallback, float endCallbackDelay) { if (g_isFading) return; g_isFading = true; g_isFadeIn = isFadeIn; g_startTime = ImGui::GetTime(); g_duration = duration; g_endCallback = endCallback; g_endCallbackDelay = endCallbackDelay; Fader::s_isVisible = true; } void Fader::SetFadeColour(ImU32 colour) { g_colour = colour; } void Fader::FadeIn(float duration, std::function endCallback, float endCallbackDelay) { DoFade(true, duration, endCallback, endCallbackDelay); } void Fader::FadeOut(float duration, std::function endCallback, float endCallbackDelay) { DoFade(false, duration, endCallback, endCallbackDelay); } ================================================ FILE: MarathonRecomp/ui/fader.h ================================================ #pragma once class Fader { public: static inline bool s_isVisible = false; static void Draw(); static void SetFadeColour(ImU32 colour); static void FadeIn(float duration = 1, std::function endCallback = nullptr, float endCallbackDelay = 0.75f); static void FadeOut(float duration = 1, std::function endCallback = nullptr, float endCallbackDelay = 0.75f); }; ================================================ FILE: MarathonRecomp/ui/game_window.cpp ================================================ #include "game_window.h" #include #include #include #include #include #include #include #if _WIN32 #include #include #endif #include bool m_isFullscreenKeyReleased = true; bool m_isResizing = false; int Window_OnSDLEvent(void*, SDL_Event* event) { if (ImGui::GetIO().BackendPlatformUserData != nullptr) ImGui_ImplSDL2_ProcessEvent(event); for (auto listener : GetEventListeners()) { if (listener->OnSDLEvent(event)) { return 0; } } switch (event->type) { case SDL_QUIT: { if (App::s_isSaving) break; App::Exit(); break; } case SDL_KEYDOWN: { switch (event->key.keysym.sym) { // Toggle fullscreen on ALT+ENTER. case SDLK_RETURN: { if (!(event->key.keysym.mod & KMOD_ALT) || !m_isFullscreenKeyReleased) break; Config::Fullscreen = GameWindow::SetFullscreen(!GameWindow::IsFullscreen()); if (Config::Fullscreen) { Config::Monitor = GameWindow::GetDisplay(); } else { Config::WindowState = GameWindow::SetMaximised(Config::WindowState == EWindowState::Maximised); } // Block holding ALT+ENTER spamming window changes. m_isFullscreenKeyReleased = false; break; } // Restore original window dimensions on F2. case SDLK_F2: Config::Fullscreen = GameWindow::SetFullscreen(false); GameWindow::ResetDimensions(); break; // Recentre window on F3. case SDLK_F3: { if (GameWindow::IsFullscreen()) break; GameWindow::SetDimensions(GameWindow::s_width, GameWindow::s_height); break; } } break; } case SDL_KEYUP: { switch (event->key.keysym.sym) { // Allow user to input ALT+ENTER again. case SDLK_RETURN: m_isFullscreenKeyReleased = true; break; } } case SDL_WINDOWEVENT: { switch (event->window.event) { case SDL_WINDOWEVENT_FOCUS_LOST: GameWindow::s_isFocused = false; SDL_ShowCursor(SDL_ENABLE); break; case SDL_WINDOWEVENT_FOCUS_GAINED: { GameWindow::s_isFocused = true; if (GameWindow::IsFullscreen()) SDL_ShowCursor(GameWindow::s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE); break; } case SDL_WINDOWEVENT_RESTORED: Config::WindowState = EWindowState::Normal; break; case SDL_WINDOWEVENT_MAXIMIZED: Config::WindowState = EWindowState::Maximised; break; case SDL_WINDOWEVENT_RESIZED: m_isResizing = true; Config::WindowSize = -1; GameWindow::s_width = event->window.data1; GameWindow::s_height = event->window.data2; GameWindow::SetTitle(fmt::format("{} - [{}x{}]", GameWindow::GetTitle(), GameWindow::s_width, GameWindow::s_height).c_str()); break; case SDL_WINDOWEVENT_MOVED: GameWindow::s_x = event->window.data1; GameWindow::s_y = event->window.data2; break; } break; } case SDL_USER_PLAYER_CHAR: GameWindow::s_playerCharacter = static_cast(event->user.code); GameWindow::SetIcon(GameWindow::s_playerCharacter); break; } return 0; } void GameWindow::Init(const char* sdlVideoDriver) { #ifdef __linux__ SDL_SetHint("SDL_APP_ID", "io.github.sonicnext_dev.marathonrecomp"); #endif if (SDL_VideoInit(sdlVideoDriver) != 0 && sdlVideoDriver) { LOGFN_ERROR("Failed to initialise the SDL video driver: \"{}\". Falling back to default.", sdlVideoDriver); SDL_VideoInit(nullptr); } auto videoDriverName = SDL_GetCurrentVideoDriver(); if (videoDriverName) LOGFN("SDL video driver: \"{}\"", videoDriverName); SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); SDL_AddEventWatch(Window_OnSDLEvent, s_pWindow); #ifdef _WIN32 SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); #endif s_x = Config::WindowX; s_y = Config::WindowY; s_width = Config::WindowWidth; s_height = Config::WindowHeight; if (s_x == -1 && s_y == -1) s_x = s_y = SDL_WINDOWPOS_CENTERED; if (!IsPositionValid()) GameWindow::ResetDimensions(); s_pWindow = SDL_CreateWindow("Marathon Recompiled", s_x, s_y, s_width, s_height, GetWindowFlags()); if (IsFullscreen()) SDL_ShowCursor(SDL_DISABLE); SetDisplay(Config::Monitor); SetIcon(); SetTitle(); SDL_SetWindowMinimumSize(s_pWindow, MIN_WIDTH, MIN_HEIGHT); SDL_SysWMinfo info; SDL_VERSION(&info.version); SDL_GetWindowWMInfo(s_pWindow, &info); #if defined(_WIN32) s_renderWindow = info.info.win.window; if (Config::DisableDWMRoundedCorners) { DWM_WINDOW_CORNER_PREFERENCE wcp = DWMWCP_DONOTROUND; DwmSetWindowAttribute(s_renderWindow, DWMWA_WINDOW_CORNER_PREFERENCE, &wcp, sizeof(wcp)); } #elif defined(PLUME_SDL_VULKAN_ENABLED) s_renderWindow = s_pWindow; #elif defined(__linux__) s_renderWindow = { info.info.x11.display, info.info.x11.window }; #elif defined(__APPLE__) s_renderWindow.window = info.info.cocoa.window; s_renderWindow.view = SDL_Metal_GetLayer(SDL_Metal_CreateView(s_pWindow)); #else static_assert(false, "Unknown platform."); #endif SetTitleBarColour(); SDL_ShowWindow(s_pWindow); } void GameWindow::Update() { if (!GameWindow::IsFullscreen() && !GameWindow::IsMaximised() && !s_isChangingDisplay) { Config::WindowX = GameWindow::s_x; Config::WindowY = GameWindow::s_y; Config::WindowWidth = GameWindow::s_width; Config::WindowHeight = GameWindow::s_height; } if (m_isResizing) { SetTitle(); m_isResizing = false; } if (g_needsResize) s_isChangingDisplay = false; } SDL_Surface* GameWindow::GetIconSurface(void* pIconBmp, size_t iconSize) { auto rw = SDL_RWFromMem(pIconBmp, iconSize); auto surface = SDL_LoadBMP_RW(rw, 1); if (!surface) LOGF_ERROR("Failed to load icon: {}", SDL_GetError()); return surface; } void GameWindow::SetIcon(void* pIconBmp, size_t iconSize) { if (auto icon = GetIconSurface(pIconBmp, iconSize)) { SDL_SetWindowIcon(s_pWindow, icon); SDL_FreeSurface(icon); } } void GameWindow::SetIcon(EPlayerCharacter player) { // TODO: Per-character icons switch (player) { case EPlayerCharacter::Sonic: break; case EPlayerCharacter::Shadow: break; case EPlayerCharacter::Silver: break; case EPlayerCharacter::Blaze: break; case EPlayerCharacter::Amy: break; case EPlayerCharacter::Tails: break; case EPlayerCharacter::Rouge: break; case EPlayerCharacter::Knuckles: break; } SetIcon(g_game_icon, sizeof(g_game_icon)); } const char* GameWindow::GetTitle() { if (Config::UseOfficialTitleOnTitleBar) { return "SONIC THE HEDGEHOG"; } return "Marathon Recompiled"; } void GameWindow::SetTitle(const char* title) { SDL_SetWindowTitle(s_pWindow, title ? title : GetTitle()); } void GameWindow::SetTitleBarColour() { #if _WIN32 if (os::user::IsDarkTheme()) { auto version = os::version::GetOSVersion(); if (version.Major < 10 || version.Build <= 17763) return; auto flag = version.Build >= 18985 ? DWMWA_USE_IMMERSIVE_DARK_MODE : 19; // DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 const DWORD useImmersiveDarkMode = 1; DwmSetWindowAttribute(s_renderWindow, flag, &useImmersiveDarkMode, sizeof(useImmersiveDarkMode)); } #endif } bool GameWindow::IsFullscreen() { return SDL_GetWindowFlags(s_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP; } bool GameWindow::SetFullscreen(bool isEnabled) { if (isEnabled) { SDL_SetWindowFullscreen(s_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_ShowCursor(s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE); } else { SDL_SetWindowFullscreen(s_pWindow, 0); SDL_ShowCursor(SDL_ENABLE); SetIcon(GameWindow::s_playerCharacter); SetDimensions(Config::WindowWidth, Config::WindowHeight, Config::WindowX, Config::WindowY); } return isEnabled; } void GameWindow::SetFullscreenCursorVisibility(bool isVisible) { s_isFullscreenCursorVisible = isVisible; if (IsFullscreen()) { SDL_ShowCursor(s_isFullscreenCursorVisible ? SDL_ENABLE : SDL_DISABLE); } else { SDL_ShowCursor(SDL_ENABLE); } } bool GameWindow::IsMaximised() { return SDL_GetWindowFlags(s_pWindow) & SDL_WINDOW_MAXIMIZED; } EWindowState GameWindow::SetMaximised(bool isEnabled) { if (isEnabled) { SDL_MaximizeWindow(s_pWindow); } else { SDL_RestoreWindow(s_pWindow); } return isEnabled ? EWindowState::Maximised : EWindowState::Normal; } SDL_Rect GameWindow::GetDimensions() { SDL_Rect rect{}; SDL_GetWindowPosition(s_pWindow, &rect.x, &rect.y); SDL_GetWindowSize(s_pWindow, &rect.w, &rect.h); return rect; } void GameWindow::GetSizeInPixels(int *w, int *h) { SDL_GetWindowSizeInPixels(s_pWindow, w, h); } void GameWindow::SetDimensions(int w, int h, int x, int y) { s_width = w; s_height = h; s_x = x; s_y = y; SDL_SetWindowSize(s_pWindow, w, h); SDL_ResizeEvent(s_pWindow, w, h); SDL_SetWindowPosition(s_pWindow, x, y); SDL_MoveEvent(s_pWindow, x, y); } void GameWindow::ResetDimensions() { s_x = SDL_WINDOWPOS_CENTERED; s_y = SDL_WINDOWPOS_CENTERED; s_width = DEFAULT_WIDTH; s_height = DEFAULT_HEIGHT; Config::WindowX = s_x; Config::WindowY = s_y; Config::WindowWidth = s_width; Config::WindowHeight = s_height; } uint32_t GameWindow::GetWindowFlags() { uint32_t flags = SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; if (Config::WindowState == EWindowState::Maximised) flags |= SDL_WINDOW_MAXIMIZED; if (Config::Fullscreen) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; #ifdef PLUME_SDL_VULKAN_ENABLED flags |= SDL_WINDOW_VULKAN; #endif return flags; } int GameWindow::GetDisplayCount() { auto result = SDL_GetNumVideoDisplays(); if (result < 0) { LOGF_ERROR("Failed to get display count: {}", SDL_GetError()); return 1; } return result; } int GameWindow::GetDisplay() { return SDL_GetWindowDisplayIndex(s_pWindow); } void GameWindow::SetDisplay(int displayIndex) { if (!IsFullscreen()) return; if (GetDisplay() == displayIndex) return; s_isChangingDisplay = true; SDL_Rect bounds; if (SDL_GetDisplayBounds(displayIndex, &bounds) == 0) { SetFullscreen(false); SetDimensions(bounds.w, bounds.h, bounds.x, bounds.y); SetFullscreen(true); } else { ResetDimensions(); } } std::vector GameWindow::GetDisplayModes(bool ignoreInvalidModes, bool ignoreRefreshRates) { auto result = std::vector(); auto uniqueResolutions = std::set>(); auto displayIndex = GetDisplay(); auto modeCount = SDL_GetNumDisplayModes(displayIndex); if (modeCount <= 0) return result; for (int i = modeCount - 1; i >= 0; i--) { SDL_DisplayMode mode; if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0) { if (ignoreInvalidModes) { if (mode.w < MIN_WIDTH || mode.h < MIN_HEIGHT) continue; SDL_DisplayMode desktopMode; if (SDL_GetDesktopDisplayMode(displayIndex, &desktopMode) == 0) { if (mode.w >= desktopMode.w || mode.h >= desktopMode.h) continue; } } if (ignoreRefreshRates) { auto res = std::make_pair(mode.w, mode.h); if (uniqueResolutions.find(res) == uniqueResolutions.end()) { uniqueResolutions.insert(res); result.push_back(mode); } } else { result.push_back(mode); } } } return result; } int GameWindow::FindNearestDisplayMode() { auto result = -1; auto displayModes = GetDisplayModes(); auto currentDiff = std::numeric_limits::max(); for (int i = 0; i < displayModes.size(); i++) { auto& mode = displayModes[i]; auto widthDiff = abs(mode.w - s_width); auto heightDiff = abs(mode.h - s_height); auto totalDiff = widthDiff + heightDiff; if (totalDiff < currentDiff) { currentDiff = totalDiff; result = i; } } return result; } bool GameWindow::IsPositionValid() { auto displayCount = GetDisplayCount(); for (int i = 0; i < displayCount; i++) { SDL_Rect bounds; if (SDL_GetDisplayBounds(i, &bounds) == 0) { auto x = s_x; auto y = s_y; // Window spans across the entire display in windowed mode, which is invalid. if (!Config::Fullscreen && s_width == bounds.w && s_height == bounds.h) return false; if (x == SDL_WINDOWPOS_CENTERED_DISPLAY(i)) x = bounds.w / 2 - s_width / 2; if (y == SDL_WINDOWPOS_CENTERED_DISPLAY(i)) y = bounds.h / 2 - s_height / 2; if (x >= bounds.x && x < bounds.x + bounds.w && y >= bounds.y && y < bounds.y + bounds.h) { return true; } } } return false; } ================================================ FILE: MarathonRecomp/ui/game_window.h ================================================ #pragma once #include #include #include #define DEFAULT_WIDTH 1280 #define DEFAULT_HEIGHT 720 #define MIN_WIDTH 640 #define MIN_HEIGHT 480 class GameWindow { public: static inline SDL_Window* s_pWindow = nullptr; static inline plume::RenderWindow s_renderWindow; static inline int s_x; static inline int s_y; static inline int s_width = DEFAULT_WIDTH; static inline int s_height = DEFAULT_HEIGHT; static inline EPlayerCharacter s_playerCharacter; static inline bool s_isFocused; static inline bool s_isFullscreenCursorVisible; static inline bool s_isChangingDisplay; static SDL_Surface* GetIconSurface(void* pIconBmp, size_t iconSize); static void SetIcon(void* pIconBmp, size_t iconSize); static void SetIcon(EPlayerCharacter player = EPlayerCharacter::Sonic); static const char* GetTitle(); static void SetTitle(const char* title = nullptr); static void SetTitleBarColour(); static bool IsFullscreen(); static bool SetFullscreen(bool isEnabled); static void SetFullscreenCursorVisibility(bool isVisible); static bool IsMaximised(); static EWindowState SetMaximised(bool isEnabled); static SDL_Rect GetDimensions(); static void GetSizeInPixels(int *w, int *h); static void SetDimensions(int w, int h, int x = SDL_WINDOWPOS_CENTERED, int y = SDL_WINDOWPOS_CENTERED); static void ResetDimensions(); static uint32_t GetWindowFlags(); static int GetDisplayCount(); static int GetDisplay(); static void SetDisplay(int displayIndex); static std::vector GetDisplayModes(bool ignoreInvalidModes = true, bool ignoreRefreshRates = true); static int FindNearestDisplayMode(); static bool IsPositionValid(); static void Init(const char* sdlVideoDriver = nullptr); static void Update(); }; ================================================ FILE: MarathonRecomp/ui/imgui_utils.cpp ================================================ #include "imgui_utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ImFont* g_pFntRodin; ImFont* g_pFntNewRodin; float g_fntRodinSize{}; std::unique_ptr g_upTexButtonWindow; std::unique_ptr g_upTexController; std::unique_ptr g_upTexKbm; std::unique_ptr g_upTexWindow; std::unique_ptr g_upTexSelectArrow; std::unique_ptr g_upTexMainMenu1; std::unique_ptr g_upTexMainMenu7; std::unique_ptr g_upTexMainMenu8; std::unique_ptr g_upTexArrow; void InitImGuiUtils() { g_pFntRodin = ImFontAtlasSnapshot::GetFont("FOT-RodinPro-DB.otf"); g_pFntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf"); g_upTexButtonWindow = LOAD_ZSTD_TEXTURE(g_button_window); g_upTexController = LOAD_ZSTD_TEXTURE(g_controller); g_upTexKbm = LOAD_ZSTD_TEXTURE(g_kbm); g_upTexWindow = LOAD_ZSTD_TEXTURE(g_window); g_upTexSelectArrow = LOAD_ZSTD_TEXTURE(g_select_arrow); g_upTexMainMenu1 = LOAD_ZSTD_TEXTURE(g_main_menu1); g_upTexMainMenu7 = LOAD_ZSTD_TEXTURE(g_main_menu7); g_upTexMainMenu8 = LOAD_ZSTD_TEXTURE(g_main_menu8); g_upTexArrow = LOAD_ZSTD_TEXTURE(g_arrow); } void UpdateImGuiUtils() { g_fntRodinSize = Scale(Config::Language == ELanguage::Japanese ? 28 : 27, true); } void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom) { SetGradient(min, max, top, top, bottom, bottom); } void SetHorizontalGradient(const ImVec2& min, const ImVec2& max, ImU32 left, ImU32 right) { SetGradient(min, max, left, right, right, left); } void SetVerticalGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom) { SetGradient(min, max, top, top, bottom, bottom); } void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 topLeft, ImU32 topRight, ImU32 bottomRight, ImU32 bottomLeft) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); callbackData->setGradient.boundsMin[0] = min.x; callbackData->setGradient.boundsMin[1] = min.y; callbackData->setGradient.boundsMax[0] = max.x; callbackData->setGradient.boundsMax[1] = max.y; callbackData->setGradient.gradientTopLeft = topLeft; callbackData->setGradient.gradientTopRight = topRight; callbackData->setGradient.gradientBottomRight = bottomRight; callbackData->setGradient.gradientBottomLeft = bottomLeft; } void ResetGradient() { auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); memset(&callbackData->setGradient, 0, sizeof(callbackData->setGradient)); } void SetShaderModifier(uint32_t shaderModifier) { if (shaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT && Config::DisableLowResolutionFontOnCustomUI) shaderModifier = IMGUI_SHADER_MODIFIER_NONE; auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier); callbackData->setShaderModifier.shaderModifier = shaderModifier; } void SetOrigin(ImVec2 origin) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetOrigin); callbackData->setOrigin.origin[0] = origin.x; callbackData->setOrigin.origin[1] = origin.y; } void SetScale(ImVec2 scale) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetScale); callbackData->setScale.scale[0] = scale.x; callbackData->setScale.scale[1] = scale.y; } void SetTextSkew(float yCenter, float skewScale) { SetShaderModifier(IMGUI_SHADER_MODIFIER_TEXT_SKEW); SetOrigin({ 0.0f, yCenter }); SetScale({ skewScale, 1.0f }); } void ResetTextSkew() { SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); SetOrigin({ 0.0f, 0.0f }); SetScale({ 1.0f, 1.0f }); } void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleLeft, float fadeScaleRight) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade); callbackData->setMarqueeFade.boundsMin[0] = min.x; callbackData->setMarqueeFade.boundsMin[1] = min.y; callbackData->setMarqueeFade.boundsMax[0] = max.x; callbackData->setMarqueeFade.boundsMax[1] = max.y; SetShaderModifier(IMGUI_SHADER_MODIFIER_HORIZONTAL_MARQUEE_FADE); SetScale({ fadeScaleLeft, fadeScaleRight }); } void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale) { SetHorizontalMarqueeFade(min, max, fadeScale, fadeScale); } void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleTop, float fadeScaleBottom) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade); callbackData->setMarqueeFade.boundsMin[0] = min.x; callbackData->setMarqueeFade.boundsMin[1] = min.y; callbackData->setMarqueeFade.boundsMax[0] = max.x; callbackData->setMarqueeFade.boundsMax[1] = max.y; SetShaderModifier(IMGUI_SHADER_MODIFIER_VERTICAL_MARQUEE_FADE); SetScale({ fadeScaleTop, fadeScaleBottom }); } void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale) { SetVerticalMarqueeFade(min, max, fadeScale, fadeScale); } void ResetMarqueeFade() { ResetGradient(); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); SetScale({ 1.0f, 1.0f }); } void SetOutline(float outline) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetOutline); callbackData->setOutline.outline = outline; } void ResetOutline() { SetOutline(0.0f); } void SetProceduralOrigin(ImVec2 proceduralOrigin) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetProceduralOrigin); callbackData->setProceduralOrigin.proceduralOrigin[0] = proceduralOrigin.x; callbackData->setProceduralOrigin.proceduralOrigin[1] = proceduralOrigin.y; } void ResetProceduralOrigin() { SetProceduralOrigin({ 0.0f, 0.0f }); } void SetAdditive(bool enabled) { auto callbackData = AddImGuiCallback(ImGuiCallback::SetAdditive); callbackData->setAdditive.enabled = enabled; } void ResetAdditive() { SetAdditive(false); } void AddImageFlipped(ImTextureID texture, const ImVec2& min, const ImVec2& max, const ImVec2& uvMin, const ImVec2& uvMax, ImU32 col, bool flipHorz, bool flipVert) { auto drawList = ImGui::GetBackgroundDrawList(); ImVec2 uv0 = uvMin; ImVec2 uv1 = { uvMax.x, uvMin.y }; ImVec2 uv2 = uvMax; ImVec2 uv3 = { uvMin.x, uvMax.y }; if (flipHorz) { std::swap(uv0.x, uv1.x); std::swap(uv2.x, uv3.x); } if (flipVert) { std::swap(uv0.y, uv3.y); std::swap(uv1.y, uv2.y); } ImVec2 p0 = min; ImVec2 p1 = { max.x, min.y }; ImVec2 p2 = max; ImVec2 p3 = { min.x, max.y }; drawList->AddImageQuad(texture, p0, p1, p2, p3, uv0, uv1, uv2, uv3, col); } float Scale(float size, bool useGameplayScale) { auto result = size * g_aspectRatioScale; if (useGameplayScale) result *= g_aspectRatioGameplayScale; return result; } double ComputeLoopMotion(double time, double offset, double total) { return std::clamp(fmod((ImGui::GetTime() - time - (offset / 60.0)) / (total / 60.0), 1.0 + (total / 60.0)), 0.0, 1.0) / 1.0; } double ComputeLinearMotion(double time, double offset, double total, bool reverse) { auto result = std::clamp((ImGui::GetTime() - time - offset / 60.0) / total * 60.0, 0.0, 1.0); return reverse ? 1.0 - result : result; } double ComputeMotion(double time, double offset, double total, bool reverse) { return sqrt(ComputeLinearMotion(time, offset, total, reverse)); } void DrawArrows(ImVec2 min, ImVec2 max, double& time) { auto drawList = ImGui::GetBackgroundDrawList(); constexpr auto FADE_IN_DURATION = 5.0; constexpr auto FADE_OUT_DURATION = 121.0; constexpr auto ARROWS_OFFSET_FRAMES = 3.0; auto arrowUVs = PIXELS_TO_UV_COORDS(512, 512, 0, 0, 400, 434); auto centre = ImVec2(min.x + ((max.x - min.x) / 2), min.y + ((max.y - min.y) / 2)); auto leftArrowWidth = Scale(400, true); auto leftArrowHeight = Scale(434, true); auto leftArrowNextOffset = Scale(230, true); auto rightArrowWidth = Scale(200, true); auto rightArrowHeight = Scale(217, true); auto rightArrowNextOffset = Scale(150, true); const auto leftArrowCount = int((max.x + leftArrowWidth) / leftArrowNextOffset) + 1; const auto rightArrowCount = int((max.x + rightArrowWidth) / rightArrowNextOffset) + 1; const auto fadeOutStartTime = (ARROWS_OFFSET_FRAMES * rightArrowCount) + (FADE_IN_DURATION * rightArrowCount) + 30.0; auto computeArrowLoopMotion = [&](double offset) -> double { auto motionTime = 0.0; auto fadeOutStartMotionTime = ComputeMotion(time, 0, fadeOutStartTime); if (fadeOutStartMotionTime < 1.0) { motionTime = ComputeMotion(time, offset, FADE_IN_DURATION); } else { motionTime = ComputeMotion(time, fadeOutStartTime, FADE_OUT_DURATION, true); if (motionTime <= 0.0) time = ImGui::GetTime(); } return std::clamp(motionTime, 0.0, 1.0); }; auto leftArrowsOffsetFrames = ARROWS_OFFSET_FRAMES * (rightArrowCount - 1); auto animDuration = ((FADE_IN_DURATION + FADE_OUT_DURATION) + (leftArrowsOffsetFrames + (ARROWS_OFFSET_FRAMES * leftArrowCount))) + fadeOutStartTime; auto leftArrowOffsetMotionTime = ComputeMotion(time, 0, animDuration); auto leftArrowGlobalOffset = Scale(Lerp(318, 298, leftArrowOffsetMotionTime), true); auto rightArrowOffsetMotionTime = ComputeMotion(time, 0, animDuration - 20.0); auto rightArrowGlobalOffset = Scale(Lerp(73, 103, rightArrowOffsetMotionTime), true); auto rightBaseY = centre.y - (rightArrowHeight / 2) - Scale(19, true); auto rightEndY = rightBaseY + rightArrowHeight; for (int i = 0; i < rightArrowCount; i++) { auto baseX = max.x - (i * rightArrowNextOffset) - rightArrowWidth + rightArrowGlobalOffset; auto endX = baseX + rightArrowWidth; auto opacity = Lerp(5, 45, std::clamp(baseX / max.x, 0.0f, 1.0f)) * computeArrowLoopMotion(ARROWS_OFFSET_FRAMES * (rightArrowCount - i)); drawList->AddImage(g_upTexArrow.get(), { baseX, rightBaseY }, { endX, rightEndY }, GET_UV_COORDS(arrowUVs), IM_COL32(255, 255, 255, opacity)); } auto leftBaseY = centre.y - (leftArrowHeight / 2) - Scale(20, true); auto leftEndY = leftBaseY + leftArrowHeight; for (int i = 0; i < leftArrowCount; i++) { auto baseX = min.x + (i * leftArrowNextOffset) - leftArrowWidth + leftArrowGlobalOffset; auto endX = baseX + leftArrowWidth; auto opacity = Lerp(45, 5, std::clamp(baseX / max.x, 0.0f, 1.0f)) * computeArrowLoopMotion(leftArrowsOffsetFrames + (ARROWS_OFFSET_FRAMES * (leftArrowCount - i))); drawList->AddImage(g_upTexArrow.get(), { endX, leftBaseY }, { baseX, leftEndY }, GET_UV_COORDS(arrowUVs), IM_COL32(255, 255, 255, opacity)); } } void DrawArrowCursor(ImVec2 pos, double time, bool isIntroAnim, bool isBlinkingAnim, bool isReversed) { auto drawList = ImGui::GetBackgroundDrawList(); auto cursorUVs = PIXELS_TO_UV_COORDS(50, 50, 0, 0, 27, 50); auto cursorScaleX = Scale(14, true); for (int i = 0; i < 3; i++) { auto cursorMotionTime = isIntroAnim ? ComputeLinearMotion(time, i, 5, isReversed) : 1.0; auto cursorScaleYMotion = Lerp(Scale(37.5, true), Scale(24, true), cursorMotionTime); auto cursorOffsetXMotion = Lerp(0, Scale(8.5, true), cursorMotionTime); auto cursorOffsetYMotion = Lerp(pos.y - cursorScaleYMotion / 6, pos.y, cursorMotionTime); auto cursorRightMotion = (cursorOffsetXMotion * 3) - (cursorOffsetXMotion * i); auto cursorAlphaMotionTime = isIntroAnim ? ComputeLinearMotion(time, i, 2, isReversed) : 1.0; auto cursorAlphaMotion = (int)Lerp(0, 255, cursorAlphaMotionTime); if (isBlinkingAnim) { auto cursorAlphaMotionInTime = ComputeLoopMotion(time, 3.0 * i, 12.0); auto cursorAlphaMotionOutTime = ComputeLoopMotion(time, 3.0 * (i + 1), 12.0); // horrible cursorAlphaMotionTime = cursorAlphaMotionInTime >= 1.0 ? cursorAlphaMotionOutTime >= 1.0 ? cursorAlphaMotionInTime : cursorAlphaMotionOutTime : cursorAlphaMotionInTime; cursorAlphaMotion = (int)Lerp(50 * (i + 1), 255, cursorAlphaMotionTime); } ImVec2 cursorMin = { pos.x + cursorRightMotion, cursorOffsetYMotion }; ImVec2 cursorMax = { pos.x + cursorRightMotion + cursorScaleX, cursorMin.y + cursorScaleYMotion }; drawList->AddImage(g_upTexSelectArrow.get(), cursorMin, cursorMax, GET_UV_COORDS(cursorUVs), IM_COL32(255, 255, 255, cursorAlphaMotion)); } } double ImValueDebug(double& value, double increment) { #ifdef _WIN32 if (GetAsyncKeyState(VK_OEM_PLUS) & 1) value += increment; if (GetAsyncKeyState(VK_OEM_MINUS) & 1) value -= increment; #endif LOGF_UTILITY("{}", value); return value; } void DrawContainerBox(ImVec2 min, ImVec2 max, float alpha) { auto drawList = ImGui::GetBackgroundDrawList(); auto commonWidth = Scale(50); auto commonHeight = Scale(50); auto bottomHeight = Scale(5); auto tl = PIXELS_TO_UV_COORDS(1024, 1024, 1, 400, 50, 50); auto tc = PIXELS_TO_UV_COORDS(1024, 1024, 51, 400, 50, 50); auto cl = PIXELS_TO_UV_COORDS(1024, 1024, 1, 450, 50, 50); auto cc = PIXELS_TO_UV_COORDS(1024, 1024, 51, 450, 50, 50); auto bl = PIXELS_TO_UV_COORDS(1024, 1024, 1, 500, 50, 50); auto bc = PIXELS_TO_UV_COORDS(1024, 1024, 51, 500, 50, 50); auto colour = IM_COL32(255, 255, 255, 100 * alpha); drawList->AddImage(g_upTexMainMenu1.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); drawList->AddImage(g_upTexMainMenu1.get(), { min.x + commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tc), colour); drawList->AddImage(g_upTexMainMenu1.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour); drawList->AddImage(g_upTexMainMenu1.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cc), colour); drawList->AddImage(g_upTexMainMenu1.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bl), colour); drawList->AddImage(g_upTexMainMenu1.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x, max.y + bottomHeight }, GET_UV_COORDS(bc), colour); } void DrawTextBasic(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text) { auto drawList = ImGui::GetBackgroundDrawList(); drawList->AddText(font, fontSize, pos, colour, text, nullptr); } void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& position, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed) { auto drawList = ImGui::GetBackgroundDrawList(); auto rectWidth = max.x - min.x; auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text); auto textX = position.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth); drawList->PushClipRect(min, max, true); if (textX <= position.x) DrawTextBasic(font, fontSize, { textX, position.y }, color, text); if (textX + textSize.x < position.x) DrawTextBasic(font, fontSize, { textX + textSize.x + rectWidth, position.y }, color, text); drawList->PopClipRect(); } void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 colour, const char* text, double time, double delay, double speed, float offset, float radius, ImU32 shadowColour) { auto drawList = ImGui::GetBackgroundDrawList(); auto rectWidth = max.x - min.x; auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text); auto textX = pos.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth); drawList->PushClipRect(min, max, true); if (textX <= pos.x) DrawTextWithShadow(font, fontSize, { textX, pos.y }, colour, text, offset, radius, shadowColour); if (textX + textSize.x < pos.x) DrawTextWithShadow(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, colour, text, offset, radius, shadowColour); drawList->PopClipRect(); } void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier) { auto drawList = ImGui::GetBackgroundDrawList(); SetOutline(outlineSize); drawList->AddText(font, fontSize, pos, outlineColor, text); ResetOutline(); if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE) SetShaderModifier(shaderModifier); drawList->AddText(font, fontSize, pos, color, text); if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE) SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); } void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset, float radius, ImU32 shadowColour) { auto drawList = ImGui::GetBackgroundDrawList(); offset = Scale(offset); SetOutline(radius); drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text); ResetOutline(); drawList->AddText(font, fontSize, pos, colour, text, nullptr); } void DrawTextParagraph(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function drawMethod, bool isCentred) { auto lines = Split(text, font, fontSize, maxWidth); auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines); auto offsetY = 0.0f; for (auto& line : lines) { auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, line.c_str()); auto textX = pos.x; auto textY = pos.y + offsetY; if (isCentred) { textX = line.starts_with("- ") ? pos.x - paragraphSize.x / 2 : pos.x - textSize.x / 2; textY = pos.y - paragraphSize.y / 2 + offsetY; } drawMethod(line.c_str(), { textX, textY }); textX += textSize.x; offsetY += textSize.y + Scale(lineMargin); } } float CalcWidestTextSize(const ImFont* font, float fontSize, std::span strs) { auto result = 0.0f; for (auto& str : strs) result = std::max(result, font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()).x); return result; } std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis, bool usePrefixEllipsis) { const std::string ellipsis = "..."; if (input.length() > maxLength) { if (useEllipsis && maxLength > ellipsis.length()) { if (usePrefixEllipsis) { return ellipsis + input.substr(0, maxLength - ellipsis.length()); } else { return input.substr(0, maxLength - ellipsis.length()) + ellipsis; } } else { return input.substr(0, maxLength); } } return input; } std::vector Split(const char* strStart, const ImFont* font, float fontSize, float maxWidth) { if (!strStart) return {}; std::vector result; float textWidth = 0.0f; float lineWidth = 0.0f; const float scale = fontSize / font->FontSize; const char *str = strStart; const char *strEnd = strStart + strlen(strStart); const char *lineStart = strStart; const bool wordWrapEnabled = (maxWidth > 0.0f); const char *wordWrapEOL = nullptr; auto IsKanji = [](const char* str, const char* strEnd) { const char* tempStr = str; unsigned int c = (unsigned int)*tempStr; if (c < 0x80) tempStr += 1; else tempStr += ImTextCharFromUtf8(&c, tempStr, strEnd); // Basic CJK and CJK Extension A return (c >= 0x4E00 && c <= 0x9FBF) || (c >= 0x3400 && c <= 0x4DBF); }; while (*str != 0) { if (wordWrapEnabled) { if (wordWrapEOL == nullptr) { wordWrapEOL = CalcWordWrapPositionA(font, scale, str, strEnd, maxWidth - lineWidth); } if (str >= wordWrapEOL) { if (IsKanji(str, strEnd)) { // If the current character is Kanji, move back to prevent splitting Kanji while (str > lineStart && IsKanji(str - 3, strEnd)) { str -= 3; } } if (textWidth < lineWidth) textWidth = lineWidth; result.emplace_back(lineStart, str); lineWidth = 0.0f; wordWrapEOL = nullptr; while (str < strEnd && ImCharIsBlankA(*str)) str++; if (*str == '\n') str++; if (strncmp(str, "\u200B", 3) == 0) { str += 3; } lineStart = str; continue; } } const char *prevStr = str; unsigned int c = (unsigned int)*str; if (c < 0x80) str += 1; else str += ImTextCharFromUtf8(&c, str, strEnd); if (c < 32) { if (c == '\n') { result.emplace_back(lineStart, str - 1); lineStart = str; textWidth = ImMax(textWidth, lineWidth); lineWidth = 0.0f; continue; } if (c == '\r') { lineStart = str; continue; } } const float charWidth = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale; if (lineWidth + charWidth >= maxWidth) { str = prevStr; break; } lineWidth += charWidth; } if (str != lineStart) { result.emplace_back(lineStart, str); } return result; } ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const std::vector& lines) { auto x = 0.0f; auto y = 0.0f; for (size_t i = 0; i < lines.size(); i++) { auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, lines[i].c_str()); x = std::max(x, textSize.x); y += textSize.y + Scale(lineMargin); } return { x, y }; } ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text) { return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, font, fontSize, maxWidth)); } float Lerp(float a, float b, float t) { return a + (b - a) * t; } float Cubic(float a, float b, float t) { return a + (b - a) * (t * t * t); } float Hermite(float a, float b, float t) { return a + (b - a) * (t * t * (3 - 2 * t)); } ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t) { return { a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t }; } ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t) { auto a = ImGui::ColorConvertU32ToFloat4(c0); auto b = ImGui::ColorConvertU32ToFloat4(c1); ImVec4 result; result.x = a.x + (b.x - a.x) * t; result.y = a.y + (b.y - a.y) * t; result.z = a.z + (b.z - a.z) * t; result.w = a.w + (b.w - a.w) * t; return ImGui::ColorConvertFloat4ToU32(result); } void DrawVersionString(const ImU32 colour) { auto& res = ImGui::GetIO().DisplaySize; auto drawList = ImGui::GetBackgroundDrawList(); auto fontSize = Scale(12, true); auto textSize = g_pFntNewRodin->CalcTextSizeA(fontSize, FLT_MAX, 0, g_versionString); auto textMarginX = Scale(8, true); auto textMarginY = Scale(5, true); auto textY = res.y - textSize.y - textMarginY; if (g_aspectRatio < NARROW_ASPECT_RATIO) textY -= g_vertCentre; // TODO: remove this line after v1 release. drawList->AddText(g_pFntNewRodin, fontSize, { textMarginX, textY }, colour, "WORK IN PROGRESS"); drawList->AddText(g_pFntNewRodin, fontSize, { res.x - textSize.x - textMarginX, textY }, colour, g_versionString); } static void DrawWindowArrow(const ImVec2 pos, float scale, float rotation, uint32_t colour) { auto arrowRadius = Scale(63.0f * scale, true); std::array vertices = { pos, // Top Left { pos.x + arrowRadius, pos.y }, // Top Right { pos.x + arrowRadius, pos.y + arrowRadius }, // Bottom Right { pos.x, pos.y + arrowRadius } // Bottom Left }; // Adjust base rotation, since the texture // points the arrow to the bottom left. auto adjRotation = rotation + 90.0f; auto radians = adjRotation * (IM_PI / 180.0f); auto c = cosf(radians); auto s = sinf(radians); auto& pivot = vertices[3]; // Rotate around bottom left. for (auto& v : vertices) { float dx = v.x - pivot.x; float dy = v.y - pivot.y; v.x = pivot.x + dx * c - dy * s; v.y = pivot.y + dx * s + dy * c; } // Adjust height to pivot. for (auto& v : vertices) v.y -= arrowRadius; auto drawList = ImGui::GetBackgroundDrawList(); auto arrowUVs = PIXELS_TO_UV_COORDS(128, 128, 65, 0, 63, 63); auto& uvMin = std::get<0>(arrowUVs); auto& uvMax = std::get<1>(arrowUVs); drawList->AddImageQuad(g_upTexWindow.get(), vertices[0], vertices[1], vertices[2], vertices[3], uvMin, { uvMax.x, uvMin.y }, { uvMax.x, uvMax.y }, { uvMin.x, uvMax.y }, colour); } double DrawWindow(const ImVec2 min, const ImVec2 max, bool isAnimated, double time, bool isClosing) { auto drawList = ImGui::GetBackgroundDrawList(); auto motionTime = 1.0; auto _min = min; auto _max = max; if (isAnimated) { motionTime = ComputeLinearMotion(time, 0, 8, isClosing); auto centre = ImVec2{ min.x + ((max.x - min.x) / 2), min.y + ((max.y - min.y) / 2) }; _min = Lerp(centre, min, motionTime); _max = Lerp(centre, max, motionTime); } constexpr auto containerTopColour = IM_COL32(20, 56, 130, 200); constexpr auto containerBottomColour = IM_COL32(8, 22, 51, 200); drawList->AddRectFilledMultiColor(_min, _max, containerTopColour, containerTopColour, containerBottomColour, containerBottomColour); auto lineHorzUVs = PIXELS_TO_UV_COORDS(128, 128, 2, 0, 60, 5); auto lineVertUVs = PIXELS_TO_UV_COORDS(128, 128, 0, 66, 5, 60); auto lineScale = Scale(1, true); auto lineOffsetRight = Scale(3, true); // Top drawList->AddImage(g_upTexWindow.get(), _min, { _max.x, _min.y + lineScale }, GET_UV_COORDS(lineHorzUVs)); // Bottom drawList->AddImage(g_upTexWindow.get(), { _min.x, _max.y - lineOffsetRight }, { _max.x, (_max.y - lineOffsetRight) + lineScale }, GET_UV_COORDS(lineHorzUVs)); // Left drawList->AddImage(g_upTexWindow.get(), _min, { _min.x + lineScale, _max.y }, GET_UV_COORDS(lineVertUVs)); // Right drawList->AddImage(g_upTexWindow.get(), { _max.x - lineOffsetRight, _min.y }, { (_max.x - lineOffsetRight) + lineScale, _max.y }, GET_UV_COORDS(lineVertUVs)); SetAdditive(true); constexpr auto arrowPixelRadius = 63.0f; constexpr auto arrowInnerScale = 0.16f; constexpr auto arrowOuterScale = 0.225f; constexpr auto arrowOuterColour = IM_COL32(255, 255, 255, 45); auto arrowOuterOffset = Scale(arrowPixelRadius * arrowOuterScale, true) / 2; // Top Left (Inner) DrawWindowArrow(_min, arrowInnerScale, 0.0f, containerTopColour); // Top Right (Inner) DrawWindowArrow({ _max.x, _min.y }, arrowInnerScale, 90.0f, containerTopColour); // Bottom Right (Inner) DrawWindowArrow(_max, arrowInnerScale, 180.0f, containerTopColour); // Bottom Left (Inner) DrawWindowArrow({ _min.x, _max.y }, arrowInnerScale, 270.0f, containerTopColour); // Top Left (Outer) DrawWindowArrow({ _min.x - arrowOuterOffset, _min.y - arrowOuterOffset }, arrowOuterScale, 0.0f, arrowOuterColour); // Top Right (Outer) DrawWindowArrow({ _max.x + arrowOuterOffset, _min.y - arrowOuterOffset }, arrowOuterScale, 90.0f, arrowOuterColour); // Bottom Right (Outer) DrawWindowArrow({ _max.x + arrowOuterOffset, _max.y + arrowOuterOffset }, arrowOuterScale, 180.0f, arrowOuterColour); // Bottom Left (Outer) DrawWindowArrow({ _min.x - arrowOuterOffset, _max.y + arrowOuterOffset }, arrowOuterScale, 270.0f, arrowOuterColour); ResetAdditive(); drawList->PushClipRect(_min, _max); return motionTime; } void DrawScrollArrows(ImVec2 min, ImVec2 max, float scale, double& time, bool top, bool bottom) { auto drawList = ImGui::GetBackgroundDrawList(); auto scrollArrowUVs = PIXELS_TO_UV_COORDS(1024, 1024, 500, 450, 50, 50); auto scrollArrowOffsetX = Scale(64, true); auto scrollArrowAlphaMotionInTime = ComputeLinearMotion(time, 0, 3); auto scrollArrowAlphaMotionPauseTime = ComputeLinearMotion(time, 3, 11); auto scrollArrowAlphaMotionOutTime = ComputeLinearMotion(time, 11, 4, true); auto scrollArrowAlphaMotionLoopTime = ComputeLinearMotion(time, 15, 50); auto scrollArrowAlphaMotion = 255; if (scrollArrowAlphaMotionPauseTime >= 1.0) { // Fade out arrows. scrollArrowAlphaMotion = 255 * scrollArrowAlphaMotionOutTime; // Reset loop. if (scrollArrowAlphaMotionLoopTime >= 1.0) time = ImGui::GetTime(); } else { // Fade in arrows. scrollArrowAlphaMotion = 255 * scrollArrowAlphaMotionInTime; } auto scrollArrowColourMotion = IM_COL32(255, 255, 255, scrollArrowAlphaMotion); ImVec2 scrollArrowTopMin = min; ImVec2 scrollArrowTopMax = { scrollArrowTopMin.x + scale, scrollArrowTopMin.y + scale }; ImVec2 scrollArrowBottomMin = { scrollArrowTopMin.x, max.y - scale }; ImVec2 scrollArrowBottomMax = { scrollArrowTopMax.x, scrollArrowBottomMin.y + scale }; if (top) AddImageFlipped(g_upTexMainMenu1.get(), scrollArrowTopMin, scrollArrowTopMax, GET_UV_COORDS(scrollArrowUVs), scrollArrowColourMotion, false, true); if (bottom) drawList->AddImage(g_upTexMainMenu1.get(), scrollArrowBottomMin, scrollArrowBottomMax, GET_UV_COORDS(scrollArrowUVs), scrollArrowColourMotion); } // Taken from ImGui because we need to modify to break for '\u200B\ too // Simple word-wrapping for English, not full-featured. Please submit failing cases! // This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end. // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) const char* CalcWordWrapPositionA(const ImFont* font, float scale, const char* text, const char* text_end, float wrap_width) { // For references, possible wrap point marked with ^ // "aaa bbb, ccc,ddd. eee fff. ggg!" // ^ ^ ^ ^ ^__ ^ ^ // List of hardcoded separators: .,;!?'" // Skip extra blanks after a line returns (that includes not counting them in width computation) // e.g. "Hello world" --> "Hello" "World" // Cut words that cannot possibly fit within one line. // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" float line_width = 0.0f; float word_width = 0.0f; float blank_width = 0.0f; wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters const char* word_end = text; const char* prev_word_end = NULL; bool inside_word = true; const char* s = text; IM_ASSERT(text_end != NULL); while (s < text_end) { unsigned int c = (unsigned int)*s; const char* next_s; if (c < 0x80) next_s = s + 1; else next_s = s + ImTextCharFromUtf8(&c, s, text_end); if (c < 32) { if (c == '\n') { line_width = word_width = blank_width = 0.0f; inside_word = true; s = next_s; continue; } if (c == '\r') { s = next_s; continue; } } const float char_width = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX); if (ImCharIsBlankW(c) || c == 0x200B) { if (inside_word) { line_width += blank_width; blank_width = 0.0f; word_end = s; } blank_width += char_width; inside_word = false; } else { word_width += char_width; if (inside_word) { word_end = next_s; } else { prev_word_end = word_end; line_width += word_width + blank_width; word_width = blank_width = 0.0f; } // Allow wrapping after punctuation. inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"'); } // We ignore blank width at the end of the line (they can be skipped) if (line_width + word_width > wrap_width) { // Words that cannot possibly fit within an entire line will be cut anywhere. if (word_width < wrap_width) s = prev_word_end ? prev_word_end : word_end; break; } s = next_s; } // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol). if (s == text && text < text_end) return s + 1; return s; } static std::vector ParseInterpolatedString(const char* str, bool includeTokens = true) { std::vector result; // Skip nullptr or strings that cannot possibly // contain variables (e.g. "${}" bare minimum). if (!str || strlen(str) <= 3) return result; auto start = str; auto ptr = str; while (*ptr) { if (ptr[0] == '$' && ptr[1] == '{') { // Add leading text. if (ptr > start) result.emplace_back(start, ptr - start); auto tokenStart = ptr; // Skip "${". ptr += 2; // Seek to end token. while (*ptr && *ptr != '}') ++ptr; if (*ptr == '}') { auto curStrStart = tokenStart; auto curStrEnd = ++ptr; if (!includeTokens) { curStrStart += 2; --curStrEnd; } // Add variable. result.emplace_back(curStrStart, curStrEnd - curStrStart); start = ptr; } else { break; } } else { ++ptr; } } // Add trailing text. if (ptr > start) result.emplace_back(start, ptr - start); return result; } static bool IsInterpolatedString(std::string_view str) { return str.length() >= 3 && str[0] == '$' && str[1] == '{' && str.back() == '}'; } ImVec2 MeasureInterpolatedText(const ImFont* pFont, float fontSize, const char* pText, ImGuiTextInterpData* pInterpData) { ImVec2 result{}; auto parsed = ParseInterpolatedString(pText); auto measureText = [&](float paddingX, std::string_view str) { auto textSize = pFont->CalcTextSizeA(fontSize, FLT_MAX, 0, str.data()); result.x += textSize.x + paddingX; result.y = std::max(result.y, textSize.y); }; for (size_t i = 0; i < parsed.size(); i++) { auto& str = parsed[i]; auto paddingX = i == parsed.size() - 1 ? 0 : Scale(2, true); if (IsInterpolatedString(str)) { auto parsedSingleNoTokens = ParseInterpolatedString(str.data(), false); if (!parsedSingleNoTokens.size()) continue; auto variableMapSingle = MapTextVariables(parsedSingleNoTokens[0].data()); if (!variableMapSingle.size()) continue; auto& variable = variableMapSingle[0]; auto& variableType = variable.first; auto& variableValue = variable.second; if (variableType == "picture") { auto& pictureName = variableValue; auto pictureNameHash = HashStr(pictureName); if (!pInterpData || !pInterpData->Picture.pupTexture || !pInterpData->pPictureCrops || !pInterpData->pPictureCrops->contains(pictureNameHash)) { auto placeholderScale = Scale(28, true); result.x += placeholderScale; result.y = std::max(result.y, placeholderScale); continue; } auto pictureCrop = FindHash(*pInterpData->pPictureCrops, pictureNameHash); auto pictureWidth = Scale(pictureCrop.Width, true); auto pictureHeight = Scale(pictureCrop.Height, true); result.x += pictureWidth + paddingX; result.y = std::max(result.y, pictureHeight); } else if (variableType == "locale") { auto& localeName = variableValue; measureText(paddingX, Localise(localeName)); } } else { measureText(paddingX, str); } } return result; } void DrawInterpolatedText(const ImFont* pFont, float fontSize, const ImVec2& pos, ImU32 colour, const char* pText, ImGuiTextInterpData* pInterpData) { auto drawList = ImGui::GetBackgroundDrawList(); auto parsed = ParseInterpolatedString(pText); auto advanceX = 0.0f; auto marginY = Config::Language == ELanguage::Japanese ? 0 : Scale(1, true); auto drawText = [&](const ImVec2& pos, float paddingX, std::string_view str) { auto textSize = pFont->CalcTextSizeA(fontSize, FLT_MAX, 0, str.data()); drawList->AddText(pFont, fontSize, pos, colour, str.data()); advanceX += textSize.x + paddingX; }; for (size_t i = 0; i < parsed.size(); i++) { auto& str = parsed[i]; auto curPos = ImVec2{ pos.x + advanceX, pos.y + marginY }; auto paddingX = i == parsed.size() - 1 ? 0 : Scale(2, true); if (IsInterpolatedString(str)) { auto parsedSingleNoTokens = ParseInterpolatedString(str.data(), false); if (!parsedSingleNoTokens.size()) continue; auto variableMapSingle = MapTextVariables(parsedSingleNoTokens[0].data()); if (!variableMapSingle.size()) continue; auto& variable = variableMapSingle[0]; auto& variableType = variable.first; auto& variableValue = variable.second; if (variableType == "picture") { auto& pictureName = variableValue; auto pictureNameHash = HashStr(pictureName); if (!pInterpData || !pInterpData->Picture.pupTexture || !pInterpData->pPictureCrops || !pInterpData->pPictureCrops->contains(pictureNameHash)) { auto placeholderScale = Scale(28, true); ImVec2 placeholderMin = { pos.x + advanceX, pos.y }; ImVec2 placeholderMax = { placeholderMin.x + placeholderScale, placeholderMin.y + placeholderScale }; advanceX += placeholderScale; drawList->AddRectFilled(placeholderMin, placeholderMax, IM_COL32(255, 0, 0, 255)); continue; } auto pictureCrop = FindHash(*pInterpData->pPictureCrops, pictureNameHash); auto pictureUVs = PIXELS_TO_UV_COORDS(pInterpData->Picture.Width, pInterpData->Picture.Height, pictureCrop.X, pictureCrop.Y, pictureCrop.Width, pictureCrop.Height); auto pictureWidth = Scale(pictureCrop.Width, true); auto pictureHeight = Scale(pictureCrop.Height, true); ImVec2 pictureMin = { pos.x + advanceX, pos.y }; ImVec2 pictureMax = { pictureMin.x + pictureWidth, pictureMin.y + pictureHeight }; advanceX += pictureWidth + paddingX; drawList->AddImage(pInterpData->Picture.pupTexture->get(), pictureMin, pictureMax, GET_UV_COORDS(pictureUVs), colour); } else if (variableType == "locale") { auto& localeName = variableValue; drawText(curPos, paddingX, Localise(localeName)); } } else { drawText(curPos, paddingX, str); } } } ImGuiTextInterpData GetHidInterpTextData() { auto buttonTexture = &g_upTexController; auto buttonTextureWidth = uint16_t(256); auto buttonTextureHeight = uint16_t(128); auto buttonCrops = Config::IsControllerIconsPS3() ? &g_buttonCropsPS3 : &g_buttonCropsXenon; if (!App::s_isInit) { if (hid::g_inputDevice == hid::EInputDevice::Keyboard || hid::g_inputDevice == hid::EInputDevice::Mouse) { buttonTexture = &g_upTexKbm; buttonTextureWidth = uint16_t(84); buttonTextureHeight = uint16_t(28); } if (hid::g_inputDevice == hid::EInputDevice::Keyboard) { buttonCrops = &g_buttonCropsKeyboard; } else if (hid::g_inputDevice == hid::EInputDevice::Mouse) { buttonCrops = &g_buttonCropsMouse; } } return { { buttonTexture, buttonTextureWidth, buttonTextureHeight }, buttonCrops }; } const xxHashMap g_buttonCropsXenon = { { HashStr("button_a"), { 0, 0, 28, 28 } }, { HashStr("button_b"), { 28, 0, 28, 28 } }, { HashStr("button_x"), { 56, 0, 28, 28 } }, { HashStr("button_y"), { 84, 0, 28, 28 } }, { HashStr("button_lb"), { 112, 0, 53, 28 } }, { HashStr("button_rb"), { 168, 0, 53, 28 } }, { HashStr("button_lt"), { 56, 29, 55, 28 } }, { HashStr("button_rt"), { 0, 29, 55, 28 } }, { HashStr("button_start"), { 112, 28, 28, 28 } }, { HashStr("button_back"), { 140, 28, 28, 28 } } }; const xxHashMap g_buttonCropsPS3 = { { HashStr("button_a"), { 0, 56, 28, 28 } }, { HashStr("button_b"), { 28, 56, 28, 28 } }, { HashStr("button_x"), { 56, 56, 28, 28 } }, { HashStr("button_y"), { 84, 56, 28, 28 } }, { HashStr("button_lb"), { 112, 56, 48, 28 } }, { HashStr("button_rb"), { 0, 84, 48, 28 } }, { HashStr("button_lt"), { 168, 56, 48, 28 } }, { HashStr("button_rt"), { 56, 84, 48, 28 } }, { HashStr("button_start"), { 140, 84, 28, 28 } }, { HashStr("button_back"), { 112, 84, 28, 28 } } }; const xxHashMap g_buttonCropsMouse = { { HashStr("button_a"), { 0, 0, 28, 28 } }, { HashStr("button_b"), { 56, 0, 28, 28 } } }; const xxHashMap g_buttonCropsKeyboard = { { HashStr("button_a"), { 28, 0, 28, 28 } }, { HashStr("button_b"), { 56, 0, 28, 28 } } }; ================================================ FILE: MarathonRecomp/ui/imgui_utils.h ================================================ #pragma once #include #include #include #define PIXELS_TO_UV_COORDS(textureWidth, textureHeight, x, y, width, height) \ std::make_tuple(ImVec2((float)x / (float)textureWidth, (float)y / (float)textureHeight), \ ImVec2(((float)x + (float)width) / (float)textureWidth, ((float)y + (float)height) / (float)textureHeight)) #define GET_UV_COORDS(tuple) std::get<0>(tuple), std::get<1>(tuple) #define CENTRE_TEXT_HORZ(min, max, textSize) min.x + ((max.x - min.x) - textSize.x) / 2 #define CENTRE_TEXT_VERT(min, max, textSize) min.y + ((max.y - min.y) - textSize.y) / 2 #define BREATHE_MOTION(start, end, time, rate) Lerp(start, end, (sin((ImGui::GetTime() - time) * (2.0f * M_PI / rate)) + 1.0f) / 2.0f) #define IM_COL32_WHITE_TRANS IM_COL32(255, 255, 255, 0) extern ImFont* g_pFntRodin; extern ImFont* g_pFntNewRodin; extern float g_fntRodinSize; extern std::unique_ptr g_upTexButtonWindow; extern std::unique_ptr g_upTexController; extern std::unique_ptr g_upTexKbm; extern std::unique_ptr g_upTexWindow; extern std::unique_ptr g_upTexSelectArrow; extern std::unique_ptr g_upTexMainMenu1; extern std::unique_ptr g_upTexMainMenu7; extern std::unique_ptr g_upTexMainMenu8; struct ImGuiTextPicture { std::unique_ptr* pupTexture{}; uint16_t Width{}; uint16_t Height{}; }; struct ImGuiTextPictureCrop { uint16_t X{}; uint16_t Y{}; uint16_t Width{}; uint16_t Height{}; }; extern const xxHashMap g_buttonCropsXenon; extern const xxHashMap g_buttonCropsPS3; extern const xxHashMap g_buttonCropsMouse; extern const xxHashMap g_buttonCropsKeyboard; struct ImGuiTextInterpData { ImGuiTextPicture Picture{}; const xxHashMap* pPictureCrops{}; }; void InitImGuiUtils(); void UpdateImGuiUtils(); void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom); void SetHorizontalGradient(const ImVec2& min, const ImVec2& max, ImU32 left, ImU32 right); void SetVerticalGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom); void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 topLeft, ImU32 topRight, ImU32 bottomRight, ImU32 bottomLeft); void ResetGradient(); void SetShaderModifier(uint32_t shaderModifier); void SetOrigin(ImVec2 origin); void SetScale(ImVec2 scale); void SetTextSkew(float yCenter, float skewScale); void ResetTextSkew(); void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleLeft, float fadeScaleRight); void SetHorizontalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale); void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScaleTop, float fadeScaleBottom); void SetVerticalMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale); void ResetMarqueeFade(); void SetOutline(float outline); void ResetOutline(); void SetProceduralOrigin(ImVec2 proceduralOrigin); void ResetProceduralOrigin(); void SetAdditive(bool enabled); void ResetAdditive(); void AddImageFlipped(ImTextureID texture, const ImVec2& min, const ImVec2& max, const ImVec2& uvMin = { 0, 0 }, const ImVec2& uvMax = { 0, 0 }, ImU32 col = IM_COL32_WHITE, bool flipHorz = false, bool flipVert = false); float Scale(float size, bool useGameplayScale = false); double ComputeLoopMotion(double time, double offset, double total); double ComputeLinearMotion(double time, double offset, double total, bool reverse = false); double ComputeMotion(double time, double offset, double total, bool reverse = false); void DrawArrows(ImVec2 min, ImVec2 max, double& time); void DrawArrowCursor(ImVec2 pos, double time, bool isIntroAnim = true, bool isBlinkingAnim = true, bool isReversed = false); double ImValueDebug(double& value, double increment = 1.0); void DrawContainerBox(ImVec2 min, ImVec2 max, float alpha = 1); void DrawTextBasic(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text); void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& position, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed); void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 colour, const char* text, double time, double delay, double speed, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)); void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier = IMGUI_SHADER_MODIFIER_NONE); void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)); void DrawTextParagraph(const ImFont* font, float fontSize, float maxWidth, const ImVec2& pos, float lineMargin, const char* text, std::function drawMethod, bool isCentred = false); float CalcWidestTextSize(const ImFont* font, float fontSize, std::span strs); std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis = true, bool usePrefixEllipsis = false); std::vector Split(const char* strStart, const ImFont* font, float fontSize, float maxWidth); ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, const std::vector& lines); ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text); float Lerp(float a, float b, float t); float Cubic(float a, float b, float t); float Hermite(float a, float b, float t); ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t); ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t); void DrawVersionString(const ImU32 colour = IM_COL32(255, 255, 255, 70)); double DrawWindow(const ImVec2 min, const ImVec2 max, bool isAnimated = false, double time = 0.0, bool isClosing = false); void DrawScrollArrows(ImVec2 min, ImVec2 max, float scale, double& time, bool top = true, bool bottom = true); const char* CalcWordWrapPositionA(const ImFont* font, float scale, const char* text, const char* text_end, float wrap_width); ImVec2 MeasureInterpolatedText(const ImFont* pFont, float fontSize, const char* pText, ImGuiTextInterpData* pInterpData = nullptr); void DrawInterpolatedText(const ImFont* pFont, float fontSize, const ImVec2& pos, ImU32 colour, const char* pText, ImGuiTextInterpData* pInterpData = nullptr); ImGuiTextInterpData GetHidInterpTextData(); ================================================ FILE: MarathonRecomp/ui/installer_wizard.cpp ================================================ #include "installer_wizard.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr float COMMON_MENU_INTRO_TIME = 10.0f; static constexpr float COMMON_FADE_TIME = 25.0f; static constexpr float NAV_BUTTON_OFFSET_Y = 39.0f; static constexpr float NAV_BUTTON_MARGIN = 48.0f; static CommonMenu g_commonMenu{}; static std::array, 8> g_installTextures{}; static std::unique_ptr g_upTexSonicNextDev{}; static double g_chevronTime{}; static double g_cursorArrowsTime{}; static double g_appearTime{}; static double g_alphaTime{}; static double g_alphaMotion{}; static bool g_isIntroAnim{ true }; static bool g_isDisappearing{}; static bool g_isQuitting{}; static std::filesystem::path g_installPath{}; static std::filesystem::path g_gameSourcePath{}; static std::array g_dlcSourcePaths{}; static std::array g_dlcInstalled{}; static Journal g_installerJournal{}; static Installer::Sources g_installerSources{}; static uint64_t g_installerAvailableSize{}; static std::unique_ptr g_installerThread{}; static double g_installerStartTime{}; static double g_installerEndTime{ DBL_MAX }; static float g_installerProgressRatioCurrent{}; static std::atomic g_installerProgressRatioTarget{}; static std::atomic g_installerFinished{}; static std::atomic g_installerHalted{}; static std::atomic g_installerCancelled{}; static bool g_installerFailed{}; static std::string g_installerErrorMessage{}; static std::array g_installTexturePositions = { ImVec2(110.0f, 90.0f), // Sonic ImVec2(-20.0f, 90.0f), // Shadow ImVec2(110.0f, 10.0f), // Silver ImVec2(25.0f, 80.0f), // Tails ImVec2(100.0f, 90.0f), // Rouge ImVec2(110.0f, 90.0f), // Amy ImVec2(180.0f, 140.0f), // Elise ImVec2(0.0f, 140.0f) // Eggman }; static const int g_installTextureIndices[] = { 0, // SelectLanguage 1, // Introduction 0, // SelectGame -- this page doesn't display a character. 0, // SelectDLC --- this page doesn't display a character. 2, // CheckSpace 3, // Installing 6, // InstallSucceeded -- force Elise. 7 // InstallFailed ----- force Eggman. }; const char* g_languageText[] = { "English", // English "日本語", // Japanese "Deutsch", // German "Français", // French "Español", // Spanish "Italiano" // Italian }; const ELanguage g_languageEnum[] = { ELanguage::English, ELanguage::Japanese, ELanguage::German, ELanguage::French, ELanguage::Spanish, ELanguage::Italian }; const char* g_dlcText[] = { "Sonic Boss Attack", "Shadow Boss Attack", "Silver Boss Attack", "Team Attack Amigo", "Sonic/Very Hard", "Shadow/Very Hard", "Silver/Very Hard", }; enum class WizardPage { SelectLanguage, Introduction, SelectGame, SelectDLC, CheckSpace, Installing, InstallSucceeded, InstallFailed, }; enum class MessagePromptSource { Unknown, Next, Back }; static WizardPage g_firstPage{}; static WizardPage g_currentPage = g_firstPage; static std::string g_currentMessagePrompt{}; static MessagePromptSource g_currentMessagePromptSource{}; static bool g_currentMessagePromptConfirmation{}; static int g_currentMessageResult{ -1 }; static std::list g_currentPickerResults{}; static std::atomic g_currentPickerResultsReady{}; static std::string g_currentPickerErrorMessage{}; static std::unique_ptr g_currentPickerThread{}; static bool g_pickerTutorialCleared[2]{}; static bool g_pickerTutorialTriggered{}; static bool g_pickerTutorialFolderMode{}; static bool g_currentPickerVisible{}; static bool g_currentPickerFolderMode{}; static int g_currentCursorIndex{ -1 }; static int g_currentCursorDefault{}; static bool g_currentCursorAccepted{}; static bool g_currentCursorBack{}; static std::vector> g_currentCursorRects{}; static std::string g_creditsStr{}; class SDLEventListenerForInstaller : public SDLEventListener { ImVec2 m_joypadAxis = {}; public: bool OnSDLEvent(SDL_Event* event) override { if (!InstallerWizard::s_isVisible || g_isDisappearing) return false; auto noModals = g_currentMessagePrompt.empty() && !g_currentPickerVisible; if (event->type == SDL_QUIT && g_currentPage == WizardPage::Installing) { // Pretend the back button was pressed if the user tried quitting during installation. // This condition is above the rest of the event processing as we want to block the exit // button while there's confirmation message is open as well. if (noModals) g_currentCursorBack = true; return true; } if (!noModals || !hid::IsInputAllowed()) return false; constexpr auto axisValueRange = 32767.0f; constexpr auto axisTapRange = 0.5f; auto newCursorIndex = -1; auto tapDirection = ImVec2(); switch (event->type) { case SDL_KEYDOWN: { switch (event->key.keysym.scancode) { case SDL_SCANCODE_LEFT: case SDL_SCANCODE_RIGHT: tapDirection.x = (event->key.keysym.scancode == SDL_SCANCODE_RIGHT) ? 1.0f : -1.0f; break; case SDL_SCANCODE_UP: case SDL_SCANCODE_DOWN: tapDirection.y = (event->key.keysym.scancode == SDL_SCANCODE_DOWN) ? 1.0f : -1.0f; break; case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: g_currentCursorAccepted = (g_currentCursorIndex >= 0); break; case SDL_SCANCODE_ESCAPE: g_currentCursorBack = true; break; } break; } case SDL_CONTROLLERBUTTONDOWN: { switch (event->cbutton.button) { case SDL_CONTROLLER_BUTTON_DPAD_LEFT: tapDirection = { -1.0f, 0.0f }; break; case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: tapDirection = { 1.0f, 0.0f }; break; case SDL_CONTROLLER_BUTTON_DPAD_UP: tapDirection = { 0.0f, -1.0f }; break; case SDL_CONTROLLER_BUTTON_DPAD_DOWN: tapDirection = { 0.0f, 1.0f }; break; case SDL_CONTROLLER_BUTTON_A: g_currentCursorAccepted = (g_currentCursorIndex >= 0); break; case SDL_CONTROLLER_BUTTON_B: g_currentCursorBack = true; break; } break; } case SDL_CONTROLLERAXISMOTION: { if (event->caxis.axis < 2) { auto newAxisValue = event->caxis.value / axisValueRange; auto sameDirection = (newAxisValue * m_joypadAxis[event->caxis.axis]) > 0.0f; auto wasInRange = abs(m_joypadAxis[event->caxis.axis]) > axisTapRange; auto isInRange = abs(newAxisValue) > axisTapRange; if (sameDirection && !wasInRange && isInRange) tapDirection[event->caxis.axis] = newAxisValue; m_joypadAxis[event->caxis.axis] = newAxisValue; } break; } case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEMOTION: { for (size_t i = 0; i < g_currentCursorRects.size(); i++) { auto ¤tRect = g_currentCursorRects[i]; if (ImGui::IsMouseHoveringRect(currentRect.first, currentRect.second, false)) { newCursorIndex = int(i); if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_LEFT) g_currentCursorAccepted = true; break; } } if (newCursorIndex < 0) g_currentCursorIndex = -1; break; } } if (tapDirection.x != 0.0f || tapDirection.y != 0.0f) { if (g_currentCursorIndex >= g_currentCursorRects.size() || g_currentCursorIndex < 0) { newCursorIndex = g_currentCursorDefault; } else { auto& currentRect = g_currentCursorRects[g_currentCursorIndex]; auto currentPoint = ImVec2 ( (currentRect.first.x + currentRect.second.x) / 2.0f + tapDirection.x * (currentRect.second.x - currentRect.first.x) / 2.0f, (currentRect.first.y + currentRect.second.y) / 2.0f + tapDirection.y * (currentRect.second.y - currentRect.first.y) / 2.0f ); auto closestDistance = FLT_MAX; for (size_t i = 0; i < g_currentCursorRects.size(); i++) { if (g_currentCursorIndex == i) continue; auto& targetRect = g_currentCursorRects[i]; auto targetPoint = ImVec2 ( (targetRect.first.x + targetRect.second.x) / 2.0f + tapDirection.x * (targetRect.first.x - targetRect.second.x) / 2.0f, (targetRect.first.y + targetRect.second.y) / 2.0f + tapDirection.y * (targetRect.first.y - targetRect.second.y) / 2.0f ); auto delta = ImVec2(targetPoint.x - currentPoint.x, targetPoint.y - currentPoint.y); auto projectedDistance = delta.x * tapDirection.x + delta.y * tapDirection.y; auto manhattanDistance = abs(delta.x) + abs(delta.y); if (projectedDistance > 0.0f && manhattanDistance < closestDistance) { newCursorIndex = int(i); closestDistance = manhattanDistance; } } } } if (newCursorIndex >= 0) { if (g_currentCursorIndex != newCursorIndex) { Game_PlaySound("move"); g_cursorArrowsTime = ImGui::GetTime(); } g_currentCursorIndex = newCursorIndex; } return false; } } g_sdlEventListenerForInstaller; static void LeaveInstallerWizard(bool isQuitting = false) { g_isDisappearing = true; Fader::FadeOut ( 1.0f, [=]() { g_isQuitting = isQuitting; InstallerWizard::s_isVisible = false; } ); } static int DLCIndex(DLC dlc) { assert(dlc != DLC::Unknown); return (int)(dlc) - 1; } static void SetCurrentPage(WizardPage page) { g_currentPage = page; g_cursorArrowsTime = ImGui::GetTime(); if (g_currentPage == WizardPage::InstallSucceeded) { ButtonWindow::Open("Button_Select"); } else if (g_currentPage != WizardPage::Installing) { auto key = "Button_SelectBack"; if (g_currentPage == g_firstPage || g_currentPage == WizardPage::InstallFailed) key = "Button_SelectQuit"; ButtonWindow::Open(key); } else if (g_currentPage == WizardPage::Installing) { ButtonWindow::Open("Button_Cancel"); } else { ButtonWindow::Close(); } } static bool PushCursorRect(ImVec2 min, ImVec2 max, bool& isPressed, bool isDefault = false) { auto currentIndex = int(g_currentCursorRects.size()); g_currentCursorRects.emplace_back(min, max); if (isDefault) g_currentCursorDefault = currentIndex; if (g_currentCursorIndex == currentIndex) { if (g_currentCursorAccepted) { isPressed = true; g_currentCursorAccepted = false; } return true; } return false; } static void DrawProgressBar(ImVec2 originMin, ImVec2 originMax, float progress) { auto& res = ImGui::GetIO().DisplaySize; auto drawList = ImGui::GetBackgroundDrawList(); auto pos = ImVec2(originMin.x - Scale(8, true), originMax.y - Scale(NAV_BUTTON_OFFSET_Y, true)); auto edgeUVs = PIXELS_TO_UV_COORDS(256, 256, 1, 0, 51, 45); auto stretchUVs = PIXELS_TO_UV_COORDS(256, 256, 51, 0, 51, 45); auto edgeWidth = Scale(50, true); auto edgeHeight = Scale(45, true); auto colour = IM_COL32(255, 255, 255, 255 * g_alphaMotion); auto leftEdgeMin = pos; auto leftEdgeMax = ImVec2(leftEdgeMin.x + edgeWidth, leftEdgeMin.y + edgeHeight); auto stretchMin = ImVec2(leftEdgeMax.x, leftEdgeMin.y); auto stretchMax = ImVec2(stretchMin.x + Scale(400, true), leftEdgeMax.y); auto rightEdgeMin = ImVec2(stretchMax.x, stretchMin.y); auto rightEdgeMax = ImVec2(rightEdgeMin.x + edgeWidth, rightEdgeMin.y + edgeHeight); drawList->AddImage(g_upTexMainMenu8.get(), leftEdgeMin, leftEdgeMax, GET_UV_COORDS(edgeUVs), colour); drawList->AddImage(g_upTexMainMenu8.get(), stretchMin, stretchMax, GET_UV_COORDS(stretchUVs), colour); AddImageFlipped(g_upTexMainMenu8.get(), rightEdgeMin, rightEdgeMax, GET_UV_COORDS(edgeUVs), colour, true); auto gaugeOffsetX = Scale(41, true); auto gaugeHeight = Scale(11, true); auto gaugeMin = ImVec2(leftEdgeMin.x + gaugeOffsetX, (rightEdgeMin.y + edgeHeight / 2) - (gaugeHeight / 2) + Scale(1, true)); auto gaugeMax = ImVec2(rightEdgeMax.x - gaugeOffsetX, gaugeMin.y + gaugeHeight / 2); drawList->AddRectFilled(gaugeMin, gaugeMax, IM_COL32(0, 0, 0, 255 * g_alphaMotion), Scale(10, true)); drawList->AddRectFilled(gaugeMin, { gaugeMin.x + (gaugeMax.x - gaugeMin.x) * progress, gaugeMax.y }, IM_COL32(112, 250, 255, 255 * g_alphaMotion), Scale(10, true)); } static bool ConvertPathSet(const nfdpathset_t *pathSet, std::list &filePaths) { nfdpathsetsize_t pathSetCount = 0; if (NFD_PathSet_GetCount(pathSet, &pathSetCount) != NFD_OKAY) return false; for (nfdpathsetsize_t i = 0; i < pathSetCount; i++) { nfdnchar_t* pathSetPath = nullptr; if (NFD_PathSet_GetPathN(pathSet, i, &pathSetPath) != NFD_OKAY) { filePaths.clear(); return false; } filePaths.emplace_back(std::filesystem::path(pathSetPath)); NFD_PathSet_FreePathN(pathSetPath); } return true; } static void PickerThreadProcess() { auto result = NFD_ERROR; const nfdpathset_t* pathSet; if (g_currentPickerFolderMode) { result = NFD_PickFolderMultipleN(&pathSet, nullptr); } else { result = NFD_OpenDialogMultipleN(&pathSet, nullptr, 0, nullptr); } if (result == NFD_OKAY) { auto pathsConverted = ConvertPathSet(pathSet, g_currentPickerResults); NFD_PathSet_Free(pathSet); } else if (result == NFD_ERROR) { g_currentPickerErrorMessage = NFD_GetError(); } g_currentPickerResultsReady = true; } static void PickerStart(bool folderMode) { if (g_currentPickerThread != nullptr) { g_currentPickerThread->join(); g_currentPickerThread.reset(); } g_currentPickerResults.clear(); g_currentPickerFolderMode = folderMode; g_currentPickerResultsReady = false; g_currentPickerVisible = true; // Optional single thread mode for testing on systems // that do not interact well with the separate thread // being used for NFD. #ifdef __APPLE__ constexpr bool singleThreadMode = true; #else constexpr bool singleThreadMode = false; #endif if (singleThreadMode) PickerThreadProcess(); else g_currentPickerThread = std::make_unique(PickerThreadProcess); } static void PickerShow(bool folderMode) { if (g_pickerTutorialCleared[folderMode]) { PickerStart(folderMode); } else { g_currentMessagePrompt = Localise(folderMode ? "Installer_Message_FolderPickerTutorial" : "Installer_Message_FilePickerTutorial"); g_currentMessagePromptConfirmation = false; g_pickerTutorialTriggered = true; g_pickerTutorialFolderMode = folderMode; } } static bool ParseSourcePaths(std::list &paths) { assert((g_currentPage == WizardPage::SelectGame) || (g_currentPage == WizardPage::SelectDLC)); constexpr auto failedPathLimit = 5; auto isFailedPathsOverLimit = false; std::list failedPaths{}; if (g_currentPage == WizardPage::SelectGame) { for (const auto& path : paths) { if (Installer::parseGame(path)) { g_gameSourcePath = path; } else if (failedPaths.size() < failedPathLimit) { failedPaths.push_back(path); } else { isFailedPathsOverLimit = true; } } } else if (g_currentPage == WizardPage::SelectDLC) { for (const auto& path : paths) { auto dlc = Installer::parseDLC(path); if (dlc != DLC::Unknown) { g_dlcSourcePaths[DLCIndex(dlc)] = path; } else if (failedPaths.size() < failedPathLimit) { failedPaths.push_back(path); } } } if (!failedPaths.empty()) { std::stringstream stringStream{}; stringStream << Localise("Installer_Message_InvalidFilesList") << std::endl; for (const auto& path : failedPaths) { std::u8string filenameU8 = path.filename().u8string(); stringStream << std::endl << "- " << Truncate(std::string(filenameU8.begin(), filenameU8.end()), 32, true, true); } if (isFailedPathsOverLimit) stringStream << std::endl << "- [...]"; g_currentMessagePrompt = stringStream.str(); g_currentMessagePromptConfirmation = false; } return failedPaths.empty(); } static void InstallerThread() { auto result = Installer::install(g_installerSources, g_installPath, false, g_installerJournal, std::chrono::seconds(1), // [&]() { g_installerProgressRatioTarget = float(double(g_installerJournal.progressCounter) / double(g_installerJournal.progressTotal)); // If user is being asked for confirmation on cancelling // the installation, halt the installer from progressing further. g_installerHalted.wait(true); // If user has confirmed they wish to cancel the // installation, return false to indicate the installer // should fail and stop. return !g_installerCancelled.load(); } ); if (!result) { g_installerFailed = true; g_installerErrorMessage = g_installerJournal.lastErrorMessage; // Delete all files that were copied. Installer::rollback(g_installerJournal); } g_installerFinished = true; g_installerCancelled = false; } static void InstallerStart() { SetCurrentPage(WizardPage::Installing); g_installerStartTime = ImGui::GetTime(); g_installerEndTime = DBL_MAX; g_installerProgressRatioCurrent = 0.0f; g_installerProgressRatioTarget = 0.0f; g_installerFailed = false; g_installerFinished = false; g_installerThread = std::make_unique(InstallerThread); g_commonMenu.SetTitle(Localise("Installer_Header_Installing")); } static bool InstallerParseSources(std::string &errorMessage) { std::error_code spaceInfoErrorCode; auto spaceInfo = std::filesystem::space(g_installPath, spaceInfoErrorCode); if (!spaceInfoErrorCode) g_installerAvailableSize = spaceInfo.available; Installer::Input installerInput{}; installerInput.gameSource = g_gameSourcePath; for (auto& path : g_dlcSourcePaths) { if (path.empty()) continue; installerInput.dlcSources.push_back(path); } auto sourcesParsed = Installer::parseSources(installerInput, g_installerJournal, g_installerSources); errorMessage = g_installerJournal.lastErrorMessage; return sourcesParsed; } static void CheckCancelAction() { if (!g_currentCursorBack) return; g_currentCursorBack = false; if (g_currentPage == WizardPage::InstallSucceeded) { // Nothing to back out on this page. return; } if (g_currentPage == WizardPage::Installing && g_installerCancelled) { // Installer's already been cancelled, // no need for more confirmations. return; } Game_PlaySound("window_close"); if (g_currentPage == g_firstPage || g_currentPage == WizardPage::InstallFailed) { // Ask for confirmation if user wants to quit the installer. g_currentMessagePrompt = Localise("Installer_Message_Quit"); g_currentMessagePromptSource = MessagePromptSource::Back; g_currentMessagePromptConfirmation = true; } else if (g_currentPage == WizardPage::Installing) { // Ask for confirmation if the user wants to cancel the installation. g_currentMessagePrompt = Localise("Installer_Message_Cancel"); g_currentMessagePromptSource = MessagePromptSource::Back; g_currentMessagePromptConfirmation = true; // Indicate to the installer that all progress should // stop until the user confirms if they wish to cancel. g_installerHalted = true; } else if (int(g_currentPage) > 0) { // Just go back to the previous page. SetCurrentPage(WizardPage(int(g_currentPage) - 1)); } } static void DrawMessagePrompt() { static auto s_messageWindowOpened = false; if (g_currentMessagePrompt.empty()) return; auto messageWindowReturned = false; if (!s_messageWindowOpened) { // Update alpha time to fade out. g_alphaTime = ImGui::GetTime(); s_messageWindowOpened = true; } if (g_currentMessagePromptConfirmation) { std::array buttons = { Localise("Common_Yes"), Localise("Common_No") }; messageWindowReturned = MessageWindow::Open(g_currentMessagePrompt, &g_currentMessageResult, buttons, 1); } else { messageWindowReturned = MessageWindow::Open(g_currentMessagePrompt, &g_currentMessageResult); } if (messageWindowReturned) { if (g_currentMessagePromptConfirmation && !g_currentMessageResult) { if (g_currentMessagePromptSource == MessagePromptSource::Back) { if (g_currentPage == WizardPage::Installing) { // If user confirms they wish to cancel the // installation, notify the installer thread // it must finish as soon as possible. g_installerCancelled = true; } else { // In all cases, proceed to just quit the application. LeaveInstallerWizard(true); } } else if (g_currentPage == WizardPage::SelectDLC) { // If user confirms the message prompt that // they wish to skip installing the DLC, proceed // to the next step. SetCurrentPage(WizardPage::CheckSpace); } } if (g_currentMessagePromptSource == MessagePromptSource::Back) { // Regardless of the confirmation, the // installation thread must be resumed. g_installerHalted = false; g_installerHalted.notify_all(); } g_currentMessagePrompt.clear(); g_currentMessagePromptSource = MessagePromptSource::Unknown; g_currentMessageResult = -1; // Update alpha time to fade in. g_alphaTime = ImGui::GetTime(); s_messageWindowOpened = false; } } static void PickerDrawForeground() { if (!g_currentPickerVisible) return; auto drawList = ImGui::GetBackgroundDrawList(); drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 190)); } static void PickerCheckTutorial() { if (!g_pickerTutorialTriggered || !g_currentMessagePrompt.empty()) return; PickerStart(g_pickerTutorialFolderMode); g_pickerTutorialTriggered = false; } static void PickerCheckResults() { if (!g_currentPickerResultsReady) return; if (!g_currentPickerErrorMessage.empty()) { g_currentMessagePrompt = g_currentPickerErrorMessage; g_currentMessagePromptConfirmation = false; g_currentPickerErrorMessage.clear(); } if (!g_currentPickerResults.empty() && ParseSourcePaths(g_currentPickerResults)) g_pickerTutorialCleared[g_pickerTutorialFolderMode] = true; g_currentPickerResultsReady = false; g_currentPickerVisible = false; } static void DrawLeftImage() { // Don't display character renders at game and DLC // select pages, as we draw the sources on the left side. if (g_currentPage == WizardPage::SelectGame || g_currentPage == WizardPage::SelectDLC) return; auto& res = ImGui::GetIO().DisplaySize; auto drawList = ImGui::GetBackgroundDrawList(); auto installTextureIndex = g_installTextureIndices[int(g_currentPage)]; // Cycle through the available images while time passes during installation. if (g_currentPage == WizardPage::Installing) { constexpr auto installSpeed = 1.0 / 15.0; auto installTime = (ImGui::GetTime() - g_installerStartTime) * installSpeed; installTextureIndex += int(installTime); } auto pTexture = g_installTextures[installTextureIndex % g_installTextures.size()].get(); constexpr float imageSize = 1000.0f; auto pos = g_installTexturePositions[installTextureIndex % g_installTextures.size()]; auto min = ImVec2(g_aspectRatioOffsetX + Scale(pos.x, true), g_aspectRatioOffsetY + Scale(pos.y, true)); auto max = ImVec2(min.x + Scale(imageSize, true), min.y + Scale(imageSize, true)); drawList->AddImage(pTexture, min, max, { 0, 0 }, { 1, 1 }, IM_COL32(255, 255, 255, std::lround(140.0 * g_alphaMotion))); } static std::string& GetWizardText(WizardPage page) { switch (page) { case WizardPage::SelectLanguage: return Localise("Installer_Page_SelectLanguage"); case WizardPage::Introduction: return Localise("Installer_Page_Introduction"); case WizardPage::SelectGame: return Localise("Installer_Page_SelectGame"); case WizardPage::SelectDLC: return Localise("Installer_Page_SelectDLC"); case WizardPage::CheckSpace: return Localise("Installer_Page_CheckSpace"); case WizardPage::Installing: return Localise("Installer_Page_Installing"); case WizardPage::InstallSucceeded: return Localise("Installer_Page_InstallSucceeded"); case WizardPage::InstallFailed: return Localise("Installer_Page_InstallFailed"); } return g_localeMissing; } static bool DrawButton(ImVec2 pos, const char* text, bool& isHovered, bool isEnabled, bool isDefault = false) { auto& res = ImGui::GetIO().DisplaySize; auto drawList = ImGui::GetBackgroundDrawList(); auto edgeUVs = PIXELS_TO_UV_COORDS(256, 256, 1, 0, 51, 45); auto stretchUVs = PIXELS_TO_UV_COORDS(256, 256, 51, 0, 51, 45); auto width = Scale(50, true); auto height = Scale(45, true); auto colourMotion = isHovered && isEnabled ? 255 : 0; auto colour = IM_COL32(colourMotion, colourMotion, colourMotion, 245 * g_alphaMotion); auto fadeColour = IM_COL32(colourMotion, colourMotion, colourMotion, 45 * g_alphaMotion); auto edgeMin = ImVec2(pos.x - Scale(8, true), pos.y); auto edgeMax = ImVec2(edgeMin.x + width, edgeMin.y + height); auto stretchMin = ImVec2(edgeMax.x, edgeMin.y); auto stretchMax = ImVec2(res.x, edgeMax.y); auto fadeMin = ImVec2(stretchMax.x - ((stretchMax.x - edgeMin.x) / 2), stretchMax.y); auto fadeMax = stretchMax; SetHorizontalGradient(fadeMin, fadeMax, colour, fadeColour); drawList->AddImage(g_upTexMainMenu8.get(), edgeMin, edgeMax, GET_UV_COORDS(edgeUVs), colour); drawList->AddImage(g_upTexMainMenu8.get(), stretchMin, stretchMax, GET_UV_COORDS(stretchUVs), colour); ResetGradient(); auto textPos = ImVec2(edgeMin.x + Scale(51, true), edgeMin.y + Scale(8, true)); auto textColour = isEnabled ? IM_COL32(255, 255, 255, 255 * g_alphaMotion) : IM_COL32(137, 137, 137, 255 * g_alphaMotion); drawList->AddText(g_pFntRodin, g_fntRodinSize, textPos, textColour, text); if (isHovered && g_alphaMotion >= 1.0f) DrawArrowCursor({ edgeMin.x + Scale(8, true), edgeMin.y + Scale(9, true) }, g_cursorArrowsTime, true, false); auto isPressed = false; isHovered = PushCursorRect(edgeMin, fadeMax, isPressed, isDefault); if (isPressed) Game_PlaySound(isEnabled ? "main_deside" : "cannot_deside"); return isEnabled && isPressed; } static void DrawSource(ImVec2 pos, const char* text, bool isEnabled) { auto& res = ImGui::GetIO().DisplaySize; auto drawList = ImGui::GetBackgroundDrawList(); auto selectedUVs = PIXELS_TO_UV_COORDS(1024, 1024, 443, 524, 560, 47); auto unselectedUVs = PIXELS_TO_UV_COORDS(1024, 1024, 443, 579, 560, 47); auto categoryWidth = Scale(560, true); auto categoryHeight = Scale(47, true); auto colour = IM_COL32(255, 255, 255, 255 * g_alphaMotion); ImVec2 categoryMin = { pos.x + Scale(42, true), pos.y + Scale(147, true) }; ImVec2 categoryMax = { categoryMin.x + categoryWidth, categoryMin.y + categoryHeight }; SetHorizontalGradient(categoryMin, categoryMax, colour, IM_COL32_WHITE_TRANS); drawList->AddImage(g_upTexMainMenu7.get(), categoryMin, categoryMax, GET_UV_COORDS(isEnabled ? selectedUVs : unselectedUVs)); ResetGradient(); drawList->AddText(g_pFntRodin, g_fntRodinSize, { categoryMin.x + Scale(129, true), categoryMin.y + Scale(6, true) }, colour, text); if (isEnabled) { auto cursorOffsetX = Scale(80, true); auto cursorOffsetY = Scale(8, true); DrawArrowCursor({ categoryMin.x + cursorOffsetX, categoryMin.y + cursorOffsetY }, 0, false, true); } } static ImVec2 GetNavButtonPosition(ImVec2 originMin, ImVec2 originMax, int index) { return { originMin.x, originMax.y - Scale(NAV_BUTTON_OFFSET_Y + (NAV_BUTTON_MARGIN * index), true) }; } static void DrawSelectLanguagePage(ImVec2 originMin, ImVec2 originMax) { auto languageBtnIdx = 5; for (auto& language : g_languageEnum) { auto isHovered = Config::Language == language; auto isPressed = DrawButton(GetNavButtonPosition(originMin, originMax, languageBtnIdx), g_languageText[int(language) - 1], isHovered, true); if (isHovered) { Config::Language = language; g_commonMenu.SetTitle(Localise("Installer_Header_Installer"), false); } if (isPressed) SetCurrentPage(WizardPage::Introduction); languageBtnIdx--; } } static void DrawSourcePickerPage(ImVec2 min, ImVec2 max, ImVec2 originMin, ImVec2 originMax) { if (g_currentPage == WizardPage::SelectGame) { DrawSource(min, Localise("Installer_Step_Game").c_str(), !g_gameSourcePath.empty()); } else if (g_currentPage == WizardPage::SelectDLC) { auto offsetY = 0.0f; for (int i = 0; i < 7; i++) { DrawSource({ min.x, min.y + offsetY }, g_dlcText[i], !g_dlcSourcePaths[i].empty() || g_dlcInstalled[i]); offsetY += Scale(NAV_BUTTON_MARGIN, true); } } static auto s_isAddFilesHovered = false; static auto s_isAddFolderHovered = false; auto isAddFilesPressed = DrawButton(GetNavButtonPosition(originMin, originMax, 2), Localise("Installer_Button_AddFiles").c_str(), s_isAddFilesHovered, true); auto isAddFolderPressed = DrawButton(GetNavButtonPosition(originMin, originMax, 1), Localise("Installer_Button_AddFolder").c_str(), s_isAddFolderHovered, true); if (isAddFilesPressed) PickerShow(false); if (isAddFolderPressed) PickerShow(true); } static void DrawInstallingPage(ImVec2 originMin, ImVec2 originMax) { constexpr auto progressSpeed = 0.1f; auto ratioTarget = g_installerProgressRatioTarget.load(); g_installerProgressRatioCurrent += std::min(ratioTarget - g_installerProgressRatioCurrent, progressSpeed * ImGui::GetIO().DeltaTime); DrawProgressBar(originMin, originMax, g_installerProgressRatioCurrent); if (g_installerFinished) { g_installerThread->join(); g_installerThread.reset(); g_installerEndTime = ImGui::GetTime(); SetCurrentPage(g_installerFailed ? WizardPage::InstallFailed : WizardPage::InstallSucceeded); g_commonMenu.SetTitle(Localise("Installer_Header_Installer")); } } static void DrawInstallSucceededPage(ImVec2 originMin, ImVec2 originMax, ImVec2 descriptionTextSize) { auto drawList = ImGui::GetBackgroundDrawList(); auto marqueeTextSize = g_pFntRodin->CalcTextSizeA(g_fntRodinSize, FLT_MAX, 0, g_creditsStr.c_str()); auto marqueeTextMarginX = Scale(5, true); auto marqueeTextMarginY = Scale(65, true); auto marqueeTextPos = ImVec2(originMax.x, originMax.y - marqueeTextSize.y - marqueeTextMarginY); auto marqueeTextMin = ImVec2(originMin.x, marqueeTextPos.y); auto marqueeTextMax = ImVec2(originMax.x, originMax.y); // NOTE (Hyper): shifting the first four rows of pixels out of view // fixes a dark line appearing at the top of the logo, along with a // couple chunks being taken away towards the upper right. // // This issue seems to occur a lot with various other textures, // needing at least one or two pixels shifted right or down to // prevent this error. Weird. auto imageUVs = PIXELS_TO_UV_COORDS(5243, 450, 0, 4, 5243, 446); auto imageWidth = Scale(524, true); auto imageHeight = Scale(45, true); auto imageRegionMin = ImVec2(originMin.x, originMin.y + descriptionTextSize.y); auto imageRegionMax = ImVec2(originMax.x, originMax.y - (marqueeTextMax.y - marqueeTextMin.y)); auto imageMin = ImVec2 ( /* X */ imageRegionMin.x + ((imageRegionMax.x - imageRegionMin.x) / 2) - (imageWidth / 2), /* Y */ imageRegionMin.y + ((imageRegionMax.y - imageRegionMin.y) / 2) - (imageHeight / 2) ); auto imageMax = ImVec2(imageMin.x + imageWidth, imageMin.y + imageHeight); drawList->AddImage(g_upTexSonicNextDev.get(), imageMin, imageMax, GET_UV_COORDS(imageUVs), IM_COL32_WHITE); SetHorizontalMarqueeFade(marqueeTextMin, marqueeTextMax, Scale(32, true)); DrawTextWithMarquee(g_pFntRodin, g_fntRodinSize, marqueeTextPos, marqueeTextMin, marqueeTextMax, IM_COL32_WHITE, g_creditsStr.c_str(), g_installerEndTime, 0.8, Scale(250, true)); ResetMarqueeFade(); } static void DrawMusicCredits() { auto drawList = ImGui::GetBackgroundDrawList(); constexpr auto fadeTime = COMMON_FADE_TIME; // Wait 12 seconds before displaying music credits, // as that's when the main melody begins. constexpr auto fadeInOffset = 738; // Wait 10 seconds after fade in before fading out music credits. constexpr auto fadeOutOffset = fadeInOffset + fadeTime + 600; auto fadeInMotion = ComputeLinearMotion(g_appearTime, fadeInOffset, fadeTime); auto fadeOutMotion = ComputeLinearMotion(g_appearTime, fadeOutOffset, fadeTime); auto fadeMotion = fadeInMotion - fadeOutMotion; auto fadeAlphaMotion = IM_COL32(0, 0, 0, 70 * fadeMotion); auto fontSize = Scale(12, true); auto offsetX = Scale(8, true); auto offsetY = Scale(5, true); if (g_aspectRatio < NARROW_ASPECT_RATIO) offsetY += g_vertCentre; drawList->AddText(g_pFntNewRodin, fontSize, { offsetX, offsetY }, fadeAlphaMotion, Localise("Installer_MusicCredits").c_str()); } void InstallerWizard::Init() { g_commonMenu = CommonMenu(Localise("Installer_Header_Installer"), "", true); g_installTextures[0] = LOAD_ZSTD_TEXTURE(g_install_001); g_installTextures[1] = LOAD_ZSTD_TEXTURE(g_install_002); g_installTextures[2] = LOAD_ZSTD_TEXTURE(g_install_003); g_installTextures[3] = LOAD_ZSTD_TEXTURE(g_install_004); g_installTextures[4] = LOAD_ZSTD_TEXTURE(g_install_005); g_installTextures[5] = LOAD_ZSTD_TEXTURE(g_install_006); g_installTextures[6] = LOAD_ZSTD_TEXTURE(g_install_007); g_installTextures[7] = LOAD_ZSTD_TEXTURE(g_install_008); g_upTexSonicNextDev = LOAD_ZSTD_TEXTURE(g_sonicnextdev); // Add whitespace between credits for marquee. for (int i = 0; i < g_credits.size(); i++) { g_creditsStr += g_credits[i]; g_creditsStr += " "; } } void InstallerWizard::Draw() { if (!s_isVisible) return; g_alphaMotion = ComputeLinearMotion(g_alphaTime, g_isIntroAnim ? COMMON_MENU_INTRO_TIME : 0, COMMON_MENU_INTRO_TIME, !g_currentMessagePrompt.empty()); // Only offset fade motion for common menu intro. if (g_alphaMotion >= 1.0) g_isIntroAnim = false; auto& res = ImGui::GetIO().DisplaySize; auto drawList = ImGui::GetBackgroundDrawList(); auto min = ImVec2(g_horzCentre + g_aspectRatioNarrowMargin, g_vertCentre); auto max = ImVec2(res.x - min.x, res.y - min.y); auto isMessageWindowOpen = !g_currentMessagePrompt.empty(); const auto bgGradientTop = IM_COL32(0, 103, 255, 255); const auto bgGradientBottom = IM_COL32(0, 40, 100, 255); drawList->AddRectFilledMultiColor({ 0.0, 0.0 }, res, bgGradientTop, bgGradientTop, bgGradientBottom, bgGradientBottom); // Reset cursor rects. g_currentCursorDefault = 0; g_currentCursorRects.clear(); DrawArrows({ 0, 0 }, res, g_chevronTime); DrawLeftImage(); g_commonMenu.Draw(); DrawMusicCredits(); auto containerWidth = Scale(640, true); auto containerHeight = Scale(405, true); auto containerOffsetY = Scale(135, true); auto containerMin = ImVec2(max.x - containerWidth, min.y + containerOffsetY); auto containerMax = ImVec2(res.x, containerMin.y + containerHeight); DrawContainerBox(containerMin, containerMax, g_alphaMotion); auto originMargin = Scale(38, true); auto originMin = ImVec2(containerMin.x + originMargin, containerMin.y + originMargin); auto originMax = ImVec2(max.x - originMargin, containerMax.y - originMargin); auto text = GetWizardText(g_currentPage); auto textMaxWidth = originMax.x - (g_fntRodinSize / 2.0f) - originMin.x; auto textMargin = 5.0f; auto textSize = MeasureCentredParagraph(g_pFntRodin, g_fntRodinSize, textMaxWidth, textMargin, text.c_str()); if (g_currentPage == WizardPage::Installing) { DrawInstallingPage(originMin, originMax); } else { auto isNavButtonSkip = false; auto isNavButtonEnabled = true; std::function navButtonFunction = []() { SetCurrentPage(WizardPage(int(g_currentPage) + 1)); }; switch (g_currentPage) { case WizardPage::SelectLanguage: DrawSelectLanguagePage(originMin, originMax); break; case WizardPage::SelectGame: DrawSourcePickerPage(min, max, originMin, originMax); isNavButtonEnabled = !g_gameSourcePath.empty(); break; case WizardPage::SelectDLC: { DrawSourcePickerPage(min, max, originMin, originMax); isNavButtonSkip = std::all_of(g_dlcSourcePaths.begin(), g_dlcSourcePaths.end(), [](const std::filesystem::path& path) { return path.empty(); }); navButtonFunction = [&]() { auto isDLCInstallerMode = g_gameSourcePath.empty(); std::string sourcesErrorMessage{}; if (!InstallerParseSources(sourcesErrorMessage)) { // Some of the sources that were provided to the installer // are not valid. Restart the file selection process. std::stringstream stringStream{}; stringStream << Localise("Installer_Message_InvalidFiles"); if (!sourcesErrorMessage.empty()) stringStream << std::endl << std::endl << sourcesErrorMessage; g_currentMessagePrompt = stringStream.str(); g_currentMessagePromptConfirmation = false; SetCurrentPage(isDLCInstallerMode ? WizardPage::SelectDLC : WizardPage::SelectGame); } else if (isNavButtonSkip && isDLCInstallerMode) { LeaveInstallerWizard(); } else { SetCurrentPage(WizardPage::CheckSpace); } }; break; } case WizardPage::CheckSpace: { char descriptionText[512]{}; char requiredSpaceText[128]{}; char availableSpaceText[128]{}; constexpr auto divisor = 1024.0 * 1024.0 * 1024.0; auto requiredGiB = double(g_installerSources.totalSize) / divisor; auto availableGiB = double(g_installerAvailableSize) / divisor; snprintf(requiredSpaceText, sizeof(requiredSpaceText), Localise("Installer_Step_RequiredSpace").c_str(), requiredGiB); snprintf(availableSpaceText, sizeof(availableSpaceText), g_installerAvailableSize > 0 ? Localise("Installer_Step_AvailableSpace").c_str() : "", availableGiB); snprintf(descriptionText, sizeof(descriptionText), "%s%s\n%s", text.c_str(), requiredSpaceText, availableSpaceText); text = descriptionText; navButtonFunction = []() { InstallerStart(); }; break; } case WizardPage::InstallSucceeded: DrawInstallSucceededPage(originMin, originMax, textSize); navButtonFunction = []() { LeaveInstallerWizard(); }; break; case WizardPage::InstallFailed: text += g_installerErrorMessage.c_str(); navButtonFunction = []() { SetCurrentPage(g_firstPage); }; break; } if (g_currentPage != WizardPage::SelectLanguage) { std::string navButtonText{}; if (isNavButtonSkip) navButtonText = Localise("Installer_Button_Skip"); else navButtonText = Localise("Installer_Button_Next"); static auto s_isNavButtonHovered = false; if (DrawButton({ originMin.x, originMax.y - Scale(NAV_BUTTON_OFFSET_Y, true) }, navButtonText.c_str(), s_isNavButtonHovered, isNavButtonEnabled, true) && navButtonFunction) navButtonFunction(); } } DrawTextParagraph(g_pFntRodin, g_fntRodinSize, textMaxWidth, originMin, textMargin, text.c_str(), [&](const char* str, ImVec2 pos) { DrawTextBasic(g_pFntRodin, g_fntRodinSize, pos, IM_COL32(255, 255, 255, 255 * g_alphaMotion), str); }); CheckCancelAction(); DrawMessagePrompt(); PickerDrawForeground(); PickerCheckTutorial(); PickerCheckResults(); } void InstallerWizard::Shutdown() { // Wait for and reset the threads. if (g_installerThread != nullptr) { g_installerThread->join(); g_installerThread.reset(); } if (g_currentPickerThread != nullptr) { g_currentPickerThread->join(); g_currentPickerThread.reset(); } // Free the sources. g_installerSources.game.reset(); g_installerSources.dlc.clear(); // Make sure the GPU is not currently active before deleting textures. Video::WaitForGPU(); // Free the textures. for (auto &texture : g_installTextures) texture.reset(); g_upTexSonicNextDev.reset(); } bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame) { g_installPath = installPath; EmbeddedPlayer::Init(); NFD_Init(); // Guarantee that one controller is initialised. // We'll rely on SDL's event loop to get the controller events. XAMINPUT_STATE inputState{}; hid::GetState(0, &inputState); if (skipGame) { for (int i = 0; i < int(DLC::Count); i++) g_dlcInstalled[i] = Installer::checkDLCInstall(g_installPath, DLC(i + 1)); g_firstPage = WizardPage::SelectDLC; } SetCurrentPage(g_firstPage); GameWindow::SetFullscreenCursorVisibility(true); s_isVisible = true; while (s_isVisible) { Video::WaitOnSwapChain(); EmbeddedPlayer::PlayMusic(); SDL_PumpEvents(); SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); GameWindow::Update(); Video::Present(); } Fader::FadeIn(0); ButtonWindow::Close(); GameWindow::SetFullscreenCursorVisibility(false); NFD_Quit(); EmbeddedPlayer::Shutdown(); InstallerWizard::Shutdown(); return !g_isQuitting; } ================================================ FILE: MarathonRecomp/ui/installer_wizard.h ================================================ #pragma once struct InstallerWizard { static inline bool s_isVisible = false; static void Init(); static void Draw(); static void Shutdown(); static bool Run(std::filesystem::path installPath, bool skipGame); }; ================================================ FILE: MarathonRecomp/ui/message_window.cpp ================================================ #include "message_window.h" #include #include #include #include #include #include #include #include #include #include #include static bool g_isAwaitingResult{}; static bool g_isClosing{}; static int g_selectedRowIndex{}; static int g_prevSelectedRowIndex{}; static bool g_upWasHeld{}; static bool g_downWasHeld{}; static ImVec2 g_joypadAxis = {}; static bool g_isAccepted{}; static bool g_isDeclined{}; static double g_time{}; static std::string g_text{}; static int g_result{}; static std::vector g_buttons{}; static int g_defaultButtonIndex{}; static int g_cancelButtonIndex{}; class SDLEventListenerForMessageWindow : public SDLEventListener { public: bool OnSDLEvent(SDL_Event* event) override { if (App::s_isInit || !MessageWindow::s_isVisible || !hid::IsInputAllowed()) return false; constexpr float axisValueRange = 32767.0f; constexpr float axisTapRange = 0.5f; ImVec2 tapDirection = {}; switch (event->type) { case SDL_KEYDOWN: { switch (event->key.keysym.scancode) { case SDL_SCANCODE_UP: g_joypadAxis.y = 1.0f; break; case SDL_SCANCODE_DOWN: g_joypadAxis.y = -1.0f; break; case SDL_SCANCODE_RETURN: case SDL_SCANCODE_KP_ENTER: g_isAccepted = true; break; case SDL_SCANCODE_ESCAPE: g_isDeclined = true; break; } break; } case SDL_MOUSEBUTTONDOWN: { // Only accept left mouse button. if (event->button.button != SDL_BUTTON_LEFT) break; // Only accept mouse buttons when an item is selected. if (g_selectedRowIndex == -1) break; g_isAccepted = true; break; } case SDL_CONTROLLERBUTTONDOWN: { switch (event->cbutton.button) { case SDL_CONTROLLER_BUTTON_DPAD_UP: g_joypadAxis = { 0.0f, 1.0f }; break; case SDL_CONTROLLER_BUTTON_DPAD_DOWN: g_joypadAxis = { 0.0f, -1.0f }; break; case SDL_CONTROLLER_BUTTON_A: g_isAccepted = true; break; case SDL_CONTROLLER_BUTTON_B: g_isDeclined = true; break; } break; } case SDL_CONTROLLERAXISMOTION: { if (event->caxis.axis < 2) { float newAxisValue = -(event->caxis.value / axisValueRange); bool sameDirection = (newAxisValue * g_joypadAxis[event->caxis.axis]) > 0.0f; bool wasInRange = abs(g_joypadAxis[event->caxis.axis]) > axisTapRange; bool isInRange = abs(newAxisValue) > axisTapRange; if (sameDirection && !wasInRange && isInRange) tapDirection[event->caxis.axis] = newAxisValue; g_joypadAxis[event->caxis.axis] = newAxisValue; } break; } } return false; } } g_sdlEventListenerForMessageWindow; void DrawButton(int rowIndex, float yOffset, float yPadding, float width, float height, std::string& text) { auto drawList = ImGui::GetBackgroundDrawList(); auto clipRectMin = drawList->GetClipRectMin(); auto clipRectMax = drawList->GetClipRectMax(); ImVec2 min = { clipRectMin.x + ((clipRectMax.x - clipRectMin.x) - width) / 2, clipRectMin.y + (height * rowIndex) + yOffset + (yPadding * rowIndex) }; ImVec2 max = { min.x + width, min.y + height }; auto textColour = IM_COL32_WHITE; if (rowIndex == g_selectedRowIndex) { auto gb = 255 * BREATHE_MOTION(1.0f, 0.0f, g_time, (g_isClosing ? 0.1f : 0.9f)); textColour = IM_COL32(255, gb, gb, 255); if (!g_isClosing) DrawArrowCursor(min, g_time, false, true); } auto textSize = g_pFntRodin->CalcTextSizeA(g_fntRodinSize, FLT_MAX, 0, text.c_str()); // Show low quality text in-game. if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); DrawTextBasic ( g_pFntRodin, g_fntRodinSize, { /* X */ min.x + ((max.x - min.x) - textSize.x) / 2, /* Y */ min.y + ((max.y - min.y) - textSize.y) / 2 }, textColour, text.c_str() ); // Reset the shader modifier. if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); } static void ResetSelection() { /* Always use -1 for mouse input to prevent the selection cursor from erroneously appearing where it shouldn't. */ g_selectedRowIndex = hid::g_inputDevice == hid::EInputDevice::Mouse ? -1 : g_defaultButtonIndex; g_upWasHeld = false; g_downWasHeld = false; g_joypadAxis = {}; g_isAccepted = false; g_isDeclined = false; } void MessageWindow::Draw() { if (!s_isVisible) return; if (g_isClosing && (ImGui::GetTime() - g_time) > (1.0 / 60.0) * 30.0) { g_isAwaitingResult = false; s_isVisible = false; return; } auto drawList = ImGui::GetBackgroundDrawList(); auto isController = hid::IsInputDeviceController(); auto isKeyboard = hid::g_inputDevice == hid::EInputDevice::Keyboard; // Handle controller input when the game is booted. if (App::s_isInit) { // Always assume keyboard to prevent mouse from blocking control in-game. isKeyboard = true; if (auto& spInputManager = App::s_pApp->m_pDoc->m_vspInputManager[0]) { auto& rPadState = spInputManager->m_PadState; g_joypadAxis.y = -rPadState.LeftStickVertical; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_DPadUp)) g_joypadAxis.y = 1.0f; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_DPadDown)) g_joypadAxis.y = -1.0f; g_isAccepted = rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_A); g_isDeclined = rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_B); if (isKeyboard) g_isAccepted = g_isAccepted || rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_Start); } } ImVec2 msgMin = { g_horzCentre + Scale(96, true), g_vertCentre + Scale(96, true) }; ImVec2 msgMax = { msgMin.x + Scale(1088, true), msgMin.y + Scale(384, true) }; ImVec2 msgCentre = { (msgMin.x / 2) + (msgMax.x / 2), (msgMin.y / 2) + (msgMax.y / 2) }; DrawWindow(msgMin, msgMax); // Use low quality text when the game is booted to not clash with existing UI. if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); auto textMargin = Scale(270, true); auto textMaxWidth = msgMax.x - msgMin.x - textMargin; auto textSize = g_pFntRodin->CalcTextSizeA(g_fntRodinSize, textMaxWidth, textMaxWidth, g_text.c_str()); DrawTextParagraph ( g_pFntRodin, g_fntRodinSize, textMaxWidth, { msgCentre.x - textSize.x / 2, msgCentre.y - textSize.y / 2 }, 1.0f, g_text.c_str(), [&](const char* str, ImVec2 pos) { DrawTextBasic(g_pFntRodin, g_fntRodinSize, pos, IM_COL32_WHITE, str); } ); // Reset the shader modifier. if (App::s_isInit) SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); drawList->PopClipRect(); ImVec2 selMin = { msgMin.x, msgMax.y + ((msgMin.y - g_vertCentre) / 2) }; ImVec2 selMax = { msgMax.x, selMin.y + Scale(128, true) }; DrawWindow(selMin, selMax); auto rowCount = 0; auto windowMarginX = Scale(36, true); auto itemWidth = msgMax.x - msgMin.x - windowMarginX; auto itemHeight = Scale(25, true); auto itemPadding = Scale(18, true); auto windowMarginY = ((selMax.y - selMin.y) / 2) - (((itemHeight + itemPadding) / 2) * g_buttons.size()); for (auto& button : g_buttons) DrawButton(rowCount++, windowMarginY, itemPadding, itemWidth, itemHeight, button); if (isController || isKeyboard) { auto upIsHeld = g_joypadAxis.y > 0.5f; auto downIsHeld = g_joypadAxis.y < -0.5f; auto scrollUp = !g_upWasHeld && upIsHeld; auto scrollDown = !g_downWasHeld && downIsHeld; auto prevSelectedRowIndex = g_selectedRowIndex; if (scrollUp) { if (g_selectedRowIndex > 0) --g_selectedRowIndex; } else if (scrollDown) { if (g_selectedRowIndex < rowCount - 1) ++g_selectedRowIndex; } if (scrollUp || scrollDown) { Game_PlaySound("move"); g_prevSelectedRowIndex = prevSelectedRowIndex; g_joypadAxis.y = 0; } g_upWasHeld = upIsHeld; g_downWasHeld = downIsHeld; if (g_isDeclined) { if (g_buttons.size() == 1) { Game_PlaySound("window_close"); } else { if (g_selectedRowIndex == g_cancelButtonIndex) { Game_PlaySound("window_close"); } else { Game_PlaySound("move"); } g_selectedRowIndex = g_cancelButtonIndex; } g_isDeclined = false; } } else if (!App::s_isInit) // Only accept mouse input during installer. { auto clipRectMin = drawList->GetClipRectMin(); auto clipRectMax = drawList->GetClipRectMax(); ImVec2 listMin = { clipRectMin.x, clipRectMin.y + windowMarginY }; ImVec2 listMax = { clipRectMax.x, clipRectMin.y + windowMarginY + (itemHeight * rowCount) + (itemPadding * rowCount) }; // Invalidate index if the mouse cursor is outside of the list box. if (!ImGui::IsMouseHoveringRect(listMin, listMax, false)) g_selectedRowIndex = -1; for (int i = 0; i < rowCount; i++) { auto currentHeight = itemHeight * i; auto currentPadding = itemPadding * i; ImVec2 itemMin = { listMin.x, listMin.y + currentHeight + currentPadding }; ImVec2 itemMax = { listMax.x, clipRectMin.y + windowMarginY + currentHeight + itemHeight + currentPadding }; if (ImGui::IsMouseHoveringRect(itemMin, itemMax, false)) { if (g_selectedRowIndex != i) Game_PlaySound("move"); g_selectedRowIndex = i; break; } } } if (g_selectedRowIndex != -1 && g_isAccepted && !g_isClosing) { g_result = g_selectedRowIndex; Game_PlaySound("main_deside"); MessageWindow::Close(); } drawList->PopClipRect(); } bool MessageWindow::Open(std::string text, int* result, std::span buttons, int defaultButtonIndex, int cancelButtonIndex) { if (!g_isAwaitingResult && *result == -1) { s_isVisible = true; g_isClosing = false; g_time = ImGui::GetTime(); g_text = text; g_buttons = std::vector(buttons.begin(), buttons.end()); g_defaultButtonIndex = defaultButtonIndex; g_cancelButtonIndex = cancelButtonIndex; if (g_buttons.empty()) g_buttons.push_back(Localise("Common_OK")); ResetSelection(); g_isAwaitingResult = true; } *result = g_result; // Returns true when the message window is closed. return !g_isAwaitingResult; } void MessageWindow::Close() { if (g_isClosing) return; g_time = ImGui::GetTime(); g_isClosing = true; } ================================================ FILE: MarathonRecomp/ui/message_window.h ================================================ #pragma once #define MSG_OPEN (false) #define MSG_CLOSED (true) class MessageWindow { public: static inline bool s_isVisible = false; static void Draw(); static bool Open(std::string text, int* result, std::span buttons = {}, int defaultButtonIndex = 0, int cancelButtonIndex = 1); static void Close(); }; ================================================ FILE: MarathonRecomp/ui/options_menu.cpp ================================================ #include "options_menu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr int MAX_VISIBLE_ROWS = 3; static bool g_isClosingButtonWindow{}; static double g_stateTime{}; static double g_flowStateTime{}; static double g_chevronTime{}; static double g_categoryTime{}; static double g_cursorArrowsTime{}; static double g_scrollArrowsTime{}; static double g_lastIncrementTime{}; static double g_lastTappedTime{}; static bool g_up{}; static bool g_upWasHeld{}; static bool g_down{}; static bool g_downWasHeld{}; static bool g_left{}; static bool g_leftWasHeld{}; static bool g_right{}; static bool g_rightWasHeld{}; static bool g_isAccepted{}; static bool g_isDeclined{}; static bool g_isReset{}; static int g_categoryIndex{ -1 }; static int g_optionIndex{}; static int g_optionCount{}; static IConfigDef* g_optionCurrent{}; static bool g_optionCanReset{}; static std::unique_ptr g_upTexMainMenu9{}; static std::string& GetCategoryName(OptionsMenuCategory category) { switch (category) { case OptionsMenuCategory::System: return Localise("Options_Category_System"); case OptionsMenuCategory::Input: return Localise("Options_Category_Input"); case OptionsMenuCategory::Audio: return Localise("Options_Category_Audio"); case OptionsMenuCategory::Video: return Localise("Options_Category_Video"); case OptionsMenuCategory::Debug: return Localise("Options_Category_Debug"); } return g_localeMissing; } static std::string& GetCategoryDescription(OptionsMenuCategory category) { switch (category) { case OptionsMenuCategory::System: return Localise("Options_Desc_Category_System"); case OptionsMenuCategory::Input: return Localise("Options_Desc_Category_Input"); case OptionsMenuCategory::Audio: return Localise("Options_Desc_Category_Audio"); case OptionsMenuCategory::Video: return Localise("Options_Desc_Category_Video"); case OptionsMenuCategory::Debug: return Localise("Options_Desc_Category_Debug"); } return g_localeMissing; } static void ResetCategorySelection() { g_categoryIndex = -1; } static void ResetOptionSelection() { g_optionIndex = 0; g_optionCurrent = nullptr; g_optionCanReset = false; } static void ResetSelection() { auto time = ImGui::GetTime(); g_flowStateTime = time; ResetCategorySelection(); ResetOptionSelection(); } static bool CheckAndDiscard(bool& value) { if (value) { value = false; return true; } return false; } static void MoveCursor(int& cursorIndex, double& cursorTime, int min = 0, int max = INT_MAX, std::function onCursorMoved = nullptr) { auto time = ImGui::GetTime(); auto scrollUp = g_up; auto scrollDown = g_down; if (scrollUp || scrollDown) g_lastTappedTime = time; static constexpr auto FAST_SCROLL_THRESHOLD = 0.3; static constexpr auto FAST_SCROLL_SPEED = 1.0 / 6.5; auto fastScroll = (time - g_lastTappedTime) > FAST_SCROLL_THRESHOLD; if (fastScroll) { if ((time - g_lastIncrementTime) < FAST_SCROLL_SPEED) { fastScroll = false; } else { g_lastIncrementTime = time; scrollUp = g_upWasHeld; scrollDown = g_downWasHeld; } } if (scrollUp) { --cursorIndex; if (cursorIndex < min) cursorIndex = max - 1; } else if (scrollDown) { ++cursorIndex; if (cursorIndex >= max) cursorIndex = min; } if (scrollUp || scrollDown) { Game_PlaySound("move"); cursorTime = time; if (onCursorMoved) onCursorMoved(); } } static void DrawCategories(ImVec2 min, ImVec2 max) { auto drawList = ImGui::GetBackgroundDrawList(); auto selectedUVs = PIXELS_TO_UV_COORDS(1024, 1024, 443, 524, 560, 47); auto unselectedUVs = PIXELS_TO_UV_COORDS(1024, 1024, 443, 579, 560, 47); auto categoryWidth = Scale(560, true); auto categoryHeight = Scale(47, true); if (g_categoryIndex == -1) { g_categoryIndex = 0; OptionsMenu::s_commonMenu.SetDescription(GetCategoryDescription((OptionsMenuCategory)g_categoryIndex)); } for (size_t i = 0; i < (size_t)OptionsMenuCategory::Count; i++) { // Don't show debug category if locked. if (!OptionsMenu::s_isDebugUnlocked && (OptionsMenuCategory)i == OptionsMenuCategory::Debug) continue; auto isCurrent = i == g_categoryIndex; auto categoryOffsetY = Scale(48, true) * i; ImVec2 categoryMin = { min.x + Scale(42, true), min.y + Scale(147, true) + categoryOffsetY }; ImVec2 categoryMax = { categoryMin.x + categoryWidth, categoryMin.y + categoryHeight }; auto categoryMotionTime = ComputeLinearMotion(g_stateTime, 0, 5, OptionsMenu::s_state != OptionsMenuState::Idle); auto categoryMotion = IM_COL32(255, 255, 255, 255 * categoryMotionTime); SetHorizontalGradient(categoryMin, categoryMax, categoryMotion, IM_COL32_WHITE_TRANS); drawList->AddImage(g_upTexMainMenu7.get(), categoryMin, categoryMax, GET_UV_COORDS(isCurrent ? selectedUVs : unselectedUVs)); ResetGradient(); if (isCurrent && OptionsMenu::s_flowState == OptionsMenuFlowState::CategoryCursor) { auto cursorOffsetX = Scale(80, true); auto cursorOffsetY = Scale(8, true); DrawArrowCursor({ categoryMin.x + cursorOffsetX, categoryMin.y + cursorOffsetY }, g_cursorArrowsTime, true, false, OptionsMenu::s_state != OptionsMenuState::Idle); } auto text = GetCategoryName((OptionsMenuCategory)i); auto textSize = g_pFntRodin->CalcTextSizeA(g_fntRodinSize, FLT_MAX, 0.0f, text.c_str()); SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); drawList->AddText(g_pFntRodin, g_fntRodinSize, { categoryMin.x + Scale(129, true), categoryMin.y + Scale(6, true) }, categoryMotion, text.c_str()); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); } } void DrawSelectionArrows(ImVec2 min, ImVec2 max, bool isSelected) { if (!isSelected) return; auto drawList = ImGui::GetBackgroundDrawList(); static bool s_isLeftArrowMotion = false; static bool s_isRightArrowMotion = false; auto motionTime = (s_isLeftArrowMotion || s_isRightArrowMotion) ? ComputeLinearMotion(g_lastTappedTime, 0, 15) : 0; if (g_left) { s_isLeftArrowMotion = true; s_isRightArrowMotion = false; } if (g_right) { s_isLeftArrowMotion = false; s_isRightArrowMotion = true; } if (motionTime >= 1.0) { s_isLeftArrowMotion = false; s_isRightArrowMotion = false; } auto arrowUVs = PIXELS_TO_UV_COORDS(256, 256, 15, 46, 28, 26); auto arrowWidth = Scale(28, true); auto arrowHeight = Scale(26, true); auto arrowOffsetXMin = Scale(30, true); auto arrowOffsetXMax = Scale(20, true); auto arrowOffsetX = Lerp(arrowOffsetXMin, arrowOffsetXMax, sin(motionTime * M_PI)); auto arrowOffsetY = Scale(10, true); auto arrowLeftOffsetX = s_isLeftArrowMotion ? arrowOffsetX : arrowOffsetXMin; auto arrowRightOffsetX = s_isRightArrowMotion ? arrowOffsetX : arrowOffsetXMin; ImVec2 arrowLeftMin = { min.x + arrowLeftOffsetX, min.y + arrowOffsetY }; ImVec2 arrowLeftMax = { arrowLeftMin.x + arrowWidth, arrowLeftMin.y + arrowHeight }; ImVec2 arrowRightMin = { max.x - arrowWidth - arrowRightOffsetX, arrowLeftMin.y }; ImVec2 arrowRightMax = { arrowRightMin.x + arrowWidth, arrowLeftMax.y }; // Draw left arrow. AddImageFlipped(g_upTexMainMenu8.get(), arrowLeftMin, arrowLeftMax, GET_UV_COORDS(arrowUVs), IM_COL32_WHITE, true); // Draw right arrow. drawList->AddImage(g_upTexMainMenu8.get(), arrowRightMin, arrowRightMax, GET_UV_COORDS(arrowUVs)); }; template static void DrawOption ( int rowIndex, ConfigDef* config, bool isAccessible, std::string* inaccessibleReason = nullptr, T valueMin = T(0), T valueCentre = T(0.5), T valueMax = T(1), bool isSlider = true, bool isInterpolatedString = false ) { auto drawList = ImGui::GetBackgroundDrawList(); auto clipRectMin = drawList->GetClipRectMin(); auto clipRectMax = drawList->GetClipRectMax(); auto optionMotionTime = ComputeLinearMotion(g_categoryTime, 0, 5, OptionsMenu::s_state != OptionsMenuState::Idle); auto optionColourMotion = ColourLerp(IM_COL32_WHITE_TRANS, isAccessible ? IM_COL32_WHITE : IM_COL32(137, 137, 137, 255), optionMotionTime); auto optionHeight = Scale(106, true); auto offsetScroll = 0.0f; auto isMiddleRow = false; // Only scroll if page has more than three items. if (g_optionCount > MAX_VISIBLE_ROWS) { isMiddleRow = ((rowIndex - std::max(g_optionIndex - 1, 0)) % MAX_VISIBLE_ROWS) >= 1; if (g_optionIndex >= g_optionCount - (MAX_VISIBLE_ROWS - 1)) { // Stop scrolling near bottom to use cursor instead. offsetScroll = -(g_optionCount - MAX_VISIBLE_ROWS) * optionHeight; isMiddleRow = ((rowIndex - std::max(g_optionCount - MAX_VISIBLE_ROWS, 0)) % MAX_VISIBLE_ROWS) >= 1; } else if (g_optionIndex >= 1) { // Start scrolling from the middle item. offsetScroll = -(g_optionIndex - 1) * optionHeight; } } else { isMiddleRow = rowIndex >= 1; } auto offsetY = optionHeight * rowIndex + offsetScroll; auto isCurrent = g_optionIndex == rowIndex; auto isSelected = isCurrent && OptionsMenu::s_flowState == OptionsMenuFlowState::OptionSelected; auto bgEdgeUVs = PIXELS_TO_UV_COORDS(256, 256, 1, 0, 51, 45); auto bgStretchUVs = PIXELS_TO_UV_COORDS(256, 256, 51, 0, 51, 45); auto bgWidth = Scale(50, true); auto bgHeight = Scale(45, true); auto bgColourMotion = isSelected ? 255 : 0; auto bgColour = IM_COL32(bgColourMotion, bgColourMotion, bgColourMotion, 245 * optionMotionTime); auto bgFadeColour = IM_COL32(bgColourMotion, bgColourMotion, bgColourMotion, 45 * optionMotionTime); ImVec2 titleBgEdgeMin = { clipRectMin.x, clipRectMin.y + offsetY }; ImVec2 titleBgEdgeMax = { titleBgEdgeMin.x + bgWidth, titleBgEdgeMin.y + bgHeight }; ImVec2 titleBgStretchMin = { titleBgEdgeMax.x, titleBgEdgeMin.y }; ImVec2 titleBgStretchMax = { clipRectMax.x, titleBgEdgeMax.y }; ImVec2 titleBgFadeMin = { titleBgStretchMax.x - Scale(300, true), titleBgStretchMax.y }; ImVec2 titleBgFadeMax = titleBgStretchMax; SetHorizontalGradient(titleBgFadeMin, titleBgFadeMax, bgColour, bgFadeColour); drawList->AddImage(g_upTexMainMenu8.get(), titleBgEdgeMin, titleBgEdgeMax, GET_UV_COORDS(bgEdgeUVs), bgColour); drawList->AddImage(g_upTexMainMenu8.get(), titleBgStretchMin, titleBgStretchMax, GET_UV_COORDS(bgStretchUVs), bgColour); ResetGradient(); auto titleText = config->GetNameLocalised(Config::Language); auto titleTextSize = g_pFntRodin->CalcTextSizeA(g_fntRodinSize, FLT_MAX, 0, titleText.c_str()); auto titleFadeRightScale = Scale(40, true); auto titleFadeRightOffsetX = Scale(15, true); auto scrollArrowOffsetX = clipRectMax.x - Scale(84, true) - BlackBar::s_pillarboxWidth; ImVec2 titlePos = { titleBgEdgeMin.x + Scale(51, true), titleBgEdgeMin.y + Scale(8, true) }; ImVec2 titleSafeAreaMin = { titleBgEdgeMax.x, titleBgEdgeMin.y }; ImVec2 titleSafeAreaMax = { scrollArrowOffsetX - titleFadeRightOffsetX, titleBgEdgeMax.y }; // Recentre title vertically for larger Japanese font. if (Config::Language == ELanguage::Japanese) titlePos.y -= Scale(0.5, true); auto isTitleRightFade = !isMiddleRow && OptionsMenu::s_flowState == OptionsMenuFlowState::OptionCursor; // Don't fade right side of title if it fits within the safe area. if (titleSafeAreaMax.x - titleSafeAreaMin.x >= titleTextSize.x) isTitleRightFade = false; if (isTitleRightFade) { ImVec2 titleFadeRightMin = { titleSafeAreaMax.x - titleFadeRightScale, titleSafeAreaMin.y }; ImVec2 titleFadeRightMax = { titleFadeRightMin.x + titleFadeRightScale, titleSafeAreaMax.y }; SetHorizontalGradient(titleFadeRightMin, titleFadeRightMax, IM_COL32_WHITE, IM_COL32_WHITE_TRANS); } SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); drawList->AddText(g_pFntRodin, g_fntRodinSize, titlePos, optionColourMotion, titleText.c_str()); SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); if (isTitleRightFade) ResetGradient(); if (isCurrent) { if (OptionsMenu::s_flowState == OptionsMenuFlowState::OptionCursor) { if (config != g_optionCurrent) { auto reason = inaccessibleReason ? *inaccessibleReason : "DUMMY"; OptionsMenu::s_commonMenu.SetDescription(isAccessible ? config->GetDescription(Config::Language) : reason); } DrawArrowCursor({ titleBgEdgeMin.x + Scale(8, true), titleBgEdgeMin.y + Scale(9, true) }, g_cursorArrowsTime, true, false, OptionsMenu::s_state != OptionsMenuState::Idle); g_optionCurrent = config; g_optionCanReset = isAccessible && config->GetName().find("Language") == std::string::npos && (void*)config != (void*)&Config::WindowSize; } else { g_optionCurrent = nullptr; g_optionCanReset = false; } } auto ctrlBgOffsetXMotion = Lerp(Scale(30, true), Scale(5, true), optionMotionTime); auto ctrlBgColourMotion = ColourLerp(IM_COL32_WHITE_TRANS, optionColourMotion, optionMotionTime); ImVec2 ctrlBgLeftEdgeMin = { titleBgEdgeMin.x + ctrlBgOffsetXMotion, titleBgEdgeMin.y + Scale(54, true) }; ImVec2 ctrlBgLeftEdgeMax = { ctrlBgLeftEdgeMin.x + bgWidth, ctrlBgLeftEdgeMin.y + bgHeight }; ImVec2 ctrlBgStretchMin = { ctrlBgLeftEdgeMax.x, ctrlBgLeftEdgeMin.y }; ImVec2 ctrlBgStretchMax = { ctrlBgStretchMin.x + Scale(300, true), ctrlBgLeftEdgeMax.y }; ImVec2 ctrlBgRightEdgeMin = { ctrlBgStretchMax.x, ctrlBgStretchMin.y }; ImVec2 ctrlBgRightEdgeMax = { ctrlBgRightEdgeMin.x + bgWidth, ctrlBgRightEdgeMin.y + bgHeight }; ImVec2 ctrlBgCentre = { ctrlBgLeftEdgeMin.x + ((ctrlBgRightEdgeMax.x - ctrlBgLeftEdgeMin.x) / 2), ctrlBgLeftEdgeMin.y + ((ctrlBgLeftEdgeMax.y - ctrlBgLeftEdgeMin.y) / 2) }; drawList->AddImage(g_upTexMainMenu8.get(), ctrlBgLeftEdgeMin, ctrlBgLeftEdgeMax, GET_UV_COORDS(bgEdgeUVs), ctrlBgColourMotion); drawList->AddImage(g_upTexMainMenu8.get(), ctrlBgStretchMin, ctrlBgStretchMax, GET_UV_COORDS(bgStretchUVs), ctrlBgColourMotion); AddImageFlipped(g_upTexMainMenu8.get(), ctrlBgRightEdgeMin, ctrlBgRightEdgeMax, GET_UV_COORDS(bgEdgeUVs), ctrlBgColourMotion, true); constexpr auto isSliderType = std::is_same_v || std::is_same_v; if constexpr (isSliderType) { if (isSlider) { auto gaugeOffsetX = Scale(41, true); auto gaugeHeight = Scale(11, true); ImVec2 gaugeMin = { ctrlBgLeftEdgeMin.x + gaugeOffsetX, (ctrlBgRightEdgeMin.y + bgHeight / 2) - (gaugeHeight / 2) + Scale(1, true) }; ImVec2 gaugeMax = { ctrlBgRightEdgeMax.x - gaugeOffsetX, gaugeMin.y + gaugeHeight / 2 }; drawList->AddRectFilled(gaugeMin, gaugeMax, IM_COL32(0, 0, 0, 255 * optionMotionTime), Scale(10, true)); auto handleUVs = PIXELS_TO_UV_COORDS(256, 256, 72, 49, 49, 19); auto handleWidth = Scale(49, true); auto handleHeight = Scale(19, true); auto handleOffsetX = Scale(12, true); auto handleOffsetFactor = 0.0f; if (config->Value <= valueCentre) { handleOffsetFactor = float(config->Value - valueMin) / (valueCentre - valueMin) * 0.5f; } else { handleOffsetFactor = 0.5f + float(config->Value - valueCentre) / (valueMax - valueCentre) * 0.5f; } handleOffsetX = ((gaugeMin.x + handleOffsetX) + ((gaugeMax.x - handleOffsetX) - (gaugeMin.x + handleOffsetX)) * handleOffsetFactor) - (handleWidth / 2); ImVec2 handleMin = { handleOffsetX, gaugeMin.y - (handleHeight / 2) + Scale(4.5, true) }; ImVec2 handleMax = { handleMin.x + handleWidth, handleMin.y + handleHeight }; drawList->AddImage(g_upTexMainMenu8.get(), handleMin, handleMax, GET_UV_COORDS(handleUVs), optionColourMotion); } else { DrawSelectionArrows(ctrlBgLeftEdgeMin, ctrlBgRightEdgeMax, isSelected); } } else { DrawSelectionArrows(ctrlBgLeftEdgeMin, ctrlBgRightEdgeMax, isSelected); } if (isCurrent) { auto setValueDescription = [=]() { auto valueDescription = config->GetValueDescription(Config::Language); auto isLanguageOption = (ConfigDef*)config == &Config::Language; if (valueDescription.empty()) { OptionsMenu::s_commonMenu.SetDescription(config->GetDescription(Config::Language), !isLanguageOption); } else { OptionsMenu::s_commonMenu.SetDescription(valueDescription, !isLanguageOption); } }; if (isAccessible) { static T s_oldValue; if (CheckAndDiscard(g_isAccepted)) { Game_PlaySound("main_deside"); if (OptionsMenu::s_flowState == OptionsMenuFlowState::OptionCursor) { setValueDescription(); s_oldValue = config->Value; if (config->LockCallback) config->LockCallback(config); OptionsMenu::SetFlowState(OptionsMenuFlowState::OptionSelected); isSelected = true; } else { if (config->Value != s_oldValue) { VideoConfigValueChangedCallback(config); if (config->ApplyCallback) config->ApplyCallback(config); } OptionsMenu::SetFlowState(OptionsMenuFlowState::OptionCursor); isSelected = false; } } if (CheckAndDiscard(g_isDeclined)) { Game_PlaySound("window_close"); if (config->Value != s_oldValue) { config->Value = s_oldValue; VideoConfigValueChangedCallback(config); if (config->Callback) config->Callback(config); if (config->ApplyCallback) config->ApplyCallback(config); } OptionsMenu::SetFlowState(OptionsMenuFlowState::OptionCursor); isSelected = false; } if (g_optionCanReset && CheckAndDiscard(g_isReset)) { Game_PlaySound("window_close"); if (!config->IsDefaultValue()) { config->MakeDefault(); VideoConfigValueChangedCallback(config); if (config->Callback) config->Callback(config); if (config->ApplyCallback) config->ApplyCallback(config); } } if (isSelected) { auto increment = g_right; auto decrement = g_left; if (increment || decrement) g_lastTappedTime = ImGui::GetTime(); if constexpr (std::is_enum_v) { auto it = config->EnumTemplateReverse.find(config->Value); if (decrement) { if (it == config->EnumTemplateReverse.begin()) it = config->EnumTemplateReverse.end(); --it; } else if (increment) { ++it; if (it == config->EnumTemplateReverse.end()) it = config->EnumTemplateReverse.begin(); } config->Value = it->first; config->SnapToNearestAccessibleValue(increment); if (increment || decrement) { setValueDescription(); Game_PlaySound("move"); if (config->Callback) config->Callback(config); } } else if constexpr (isSliderType) { auto time = ImGui::GetTime(); auto fastIncrement = isSlider && (g_leftWasHeld || g_rightWasHeld) && (time - g_lastTappedTime) > 0.5; auto playIncrementSound = true; static auto s_fastIncrementHoldTime = 0.0; static auto s_lastIncrementSoundTime = 0.0; static constexpr auto INCREMENT_TIME = 1.0 / 60.0; static constexpr auto INCREMENT_SOUND_TIME = 1.0 / 20.0; if (fastIncrement) { s_fastIncrementHoldTime += App::s_deltaTime; } else { s_fastIncrementHoldTime = 0.0; } if (fastIncrement) { playIncrementSound = (time - s_lastIncrementSoundTime) > INCREMENT_SOUND_TIME; if (s_fastIncrementHoldTime < INCREMENT_TIME) { fastIncrement = false; } else { g_lastIncrementTime = time; } } if (fastIncrement) { increment = g_rightWasHeld; decrement = g_leftWasHeld; } do { if constexpr (std::is_integral_v) { if (decrement) config->Value -= 1; else if (increment) config->Value += 1; } else { if (decrement) config->Value -= 0.01f; else if (increment) config->Value += 0.01f; } if (fastIncrement) s_fastIncrementHoldTime -= INCREMENT_TIME; } while (fastIncrement && s_fastIncrementHoldTime >= INCREMENT_TIME); auto isValueInBounds = config->Value >= valueMin && config->Value <= valueMax; if ((increment || decrement) && isValueInBounds && playIncrementSound) { Game_PlaySound("move"); s_lastIncrementSoundTime = time; } config->Value = std::clamp(config->Value, valueMin, valueMax); } else if constexpr (std::is_same_v) { if (increment || decrement) { Game_PlaySound("move"); config->Value = !config->Value; } } // Run standard callback if there's no callback on apply. if (!config->ApplyCallback) { if ((increment || decrement) && config->Callback) config->Callback(config); } } // Toggle BGM for master and music volume sliders. if (OptionsMenu::s_pBgmCue) OptionsMenu::s_pBgmCue->SetPause(!(isSelected && ((ConfigDef*)config == &Config::MasterVolume || (ConfigDef*)config == &Config::MusicVolume))); } else { if (CheckAndDiscard(g_isAccepted)) Game_PlaySound("cannot_deside"); } } std::string valueText; auto isValueCentred = false; if constexpr (std::is_same_v) { valueText = fmt::format("{}%", int(round(config->Value * 100.0f))); } else if constexpr (std::is_same_v) { if (config == &Config::WindowSize) { if (Config::Fullscreen) { int displayW, displayH; GameWindow::GetSizeInPixels(&displayW, &displayH); valueText = fmt::format("{}x{}", displayW, displayH); } else { auto displayModes = GameWindow::GetDisplayModes(); if (config->Value >= 0 && config->Value < displayModes.size()) { auto& displayMode = displayModes[config->Value]; valueText = fmt::format("{}x{}", displayMode.w, displayMode.h); } else { valueText = fmt::format("{}x{}", GameWindow::s_width, GameWindow::s_height); } } isValueCentred = true; } else if (config == &Config::Monitor) { valueText = fmt::format("{}", config->Value + 1); isValueCentred = true; } else { valueText = fmt::format("{}", config->Value); if (isSlider && config->Value >= valueMax) valueText = Localise("Options_Value_Max"); } } else { valueText = config->GetValueLocalised(Config::Language); isValueCentred = true; } SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT); if (isInterpolatedString) { auto interpData = GetHidInterpTextData(); auto valueTextSize = MeasureInterpolatedText(g_pFntRodin, g_fntRodinSize, valueText.c_str(), &interpData); ImVec2 valuePos = { ctrlBgCentre.x - (valueTextSize.x / 2), ctrlBgCentre.y - (valueTextSize.y / 2) - Scale(2, true) }; // Align text to right side of the background. if (!isValueCentred) valuePos.x = ctrlBgRightEdgeMax.x + Scale(10, true); DrawInterpolatedText(g_pFntRodin, g_fntRodinSize, valuePos, optionColourMotion, valueText.data(), &interpData); } else { auto valueTextSize = g_pFntRodin->CalcTextSizeA(g_fntRodinSize, FLT_MAX, 0.0f, valueText.data()); ImVec2 valuePos = { ctrlBgCentre.x - (valueTextSize.x / 2), ctrlBgCentre.y - (valueTextSize.y / 2) - Scale(2, true) }; // Align text to right side of the background. if (!isValueCentred) valuePos.x = ctrlBgRightEdgeMax.x + Scale(10, true); drawList->AddText(g_pFntRodin, g_fntRodinSize, valuePos, optionColourMotion, valueText.data()); } SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); } static void DrawOptions(ImVec2 min, ImVec2 max) { auto drawList = ImGui::GetBackgroundDrawList(); drawList->PushClipRect(min, max); auto rowCount = 0; auto cmnReason = &Localise("Options_Desc_NotAvailable"); auto devReason = &Localise("Options_Desc_NotImplemented"); #define ENUM_VALUE(type) (type)0, (type)0, (type)0 switch ((OptionsMenuCategory)g_categoryIndex) { case OptionsMenuCategory::System: DrawOption(rowCount++, &Config::Language, !OptionsMenu::s_isPause, cmnReason); DrawOption(rowCount++, &Config::VoiceLanguage, !OptionsMenu::s_isPause, cmnReason); DrawOption(rowCount++, &Config::Subtitles, true); DrawOption(rowCount++, &Config::Hints, true); DrawOption(rowCount++, &Config::ControlTutorial, true); DrawOption(rowCount++, &Config::Autosave, true); DrawOption(rowCount++, &Config::AchievementNotifications, true); break; case OptionsMenuCategory::Input: DrawOption(rowCount++, &Config::HorizontalCamera, true); DrawOption(rowCount++, &Config::VerticalCamera, true); DrawOption(rowCount++, &Config::AllowBackgroundInput, true); DrawOption(rowCount++, &Config::ControllerIcons, !OptionsMenu::s_isPause, cmnReason); DrawOption(rowCount++, &Config::LightDash, true, nullptr, ENUM_VALUE(ELightDash), false, true); DrawOption(rowCount++, &Config::SlidingAttack, true, nullptr, ENUM_VALUE(ESlidingAttack), false, true); break; case OptionsMenuCategory::Audio: DrawOption(rowCount++, &Config::MasterVolume, true); DrawOption(rowCount++, &Config::MusicVolume, true); DrawOption(rowCount++, &Config::EffectsVolume, true); DrawOption(rowCount++, &Config::ChannelConfiguration, !OptionsMenu::s_isPause, cmnReason); DrawOption(rowCount++, &Config::MuteOnFocusLost, true); DrawOption(rowCount++, &Config::MusicAttenuation, AudioPatches::CanAttenuate(), &Localise("Options_Desc_OSNotSupported")); break; case OptionsMenuCategory::Video: { // TODO: implement buffer resize. DrawOption(rowCount++, &Config::WindowSize, false, devReason, 0, 0, 1, false); // DrawOption(rowCount++, &Config::WindowSize, !Config::Fullscreen, // &Localise("Options_Desc_NotAvailableFullscreen"), // 0, 0, (int)GameWindow::GetDisplayModes().size() - 1, false); auto displayCount = GameWindow::GetDisplayCount(); auto canChangeMonitor = Config::Fullscreen && displayCount > 1; auto monitorReason = &Localise("Options_Desc_NotAvailableWindowed"); if (Config::Fullscreen && displayCount <= 1) monitorReason = &Localise("Options_Desc_NotAvailableHardware"); DrawOption(rowCount++, &Config::Monitor, false, devReason, 0, 0, 1, false); // TODO: implement buffer resize. DrawOption(rowCount++, &Config::Monitor, canChangeMonitor, monitorReason, 0, 0, displayCount - 1, false); DrawOption(rowCount++, &Config::AspectRatio, false, devReason); // TODO: implement buffer resize. DrawOption(rowCount++, &Config::AspectRatio, true); DrawOption(rowCount++, &Config::ResolutionScale, false, devReason); // TODO: implement buffer resize. DrawOption(rowCount++, &Config::ResolutionScale, true, nullptr, 0.25f, 1.0f, 2.0f); DrawOption(rowCount++, &Config::Fullscreen, false, devReason); // TODO: implement buffer resize. DrawOption(rowCount++, &Config::Fullscreen, true); DrawOption(rowCount++, &Config::VSync, true); DrawOption(rowCount++, &Config::FPS, true, nullptr, FPS_MIN, 120, FPS_MAX); DrawOption(rowCount++, &Config::Brightness, true); DrawOption(rowCount++, &Config::AntiAliasing, false, devReason); // TODO: implement MSAA. DrawOption(rowCount++, &Config::AntiAliasing, Config::AntiAliasing.InaccessibleValues.size() != 3, &Localise("Options_Desc_NotAvailableHardware")); DrawOption(rowCount++, &Config::TransparencyAntiAliasing, false, devReason); // TODO: implement MSAA. DrawOption(rowCount++, &Config::TransparencyAntiAliasing, Config::AntiAliasing != EAntiAliasing::Off, &Localise("Options_Desc_NotAvailableMSAA")); DrawOption(rowCount++, &Config::ShadowResolution, !OptionsMenu::s_isPause, cmnReason); // TODO: allow changes on demand. DrawOption(rowCount++, &Config::ShadowResolution, true); DrawOption(rowCount++, &Config::ReflectionResolution, !OptionsMenu::s_isPause, cmnReason); // TODO: allow changes on demand. DrawOption(rowCount++, &Config::ReflectionResolution, true); DrawOption(rowCount++, &Config::RadialBlur, true); DrawOption(rowCount++, &Config::CutsceneAspectRatio, true); DrawOption(rowCount++, &Config::UIAlignmentMode, true); break; } case OptionsMenuCategory::Debug: { for (auto def : g_configDefinitions) { if (def->GetSection() != "Codes") continue; def->SetHidden(false); DrawOption(rowCount++, (ConfigDef*)def, true); } break; } } #undef ENUM_VALUE g_optionCount = rowCount; drawList->PopClipRect(); if (g_optionCount > 3 && OptionsMenu::s_flowState == OptionsMenuFlowState::OptionCursor) { ImVec2 scrollArrowsMin = { max.x - BlackBar::s_pillarboxWidth - Scale(84, true), min.y + Scale(12, true) }; ImVec2 scrollArrowsMax = { max.x, max.y - Scale(16, true) }; DrawScrollArrows(scrollArrowsMin, scrollArrowsMax, Scale(20, true), g_scrollArrowsTime, g_optionIndex > 1, g_optionIndex < rowCount - 2); } } static void DrawContainer(ImVec2 min, ImVec2 max) { auto* drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; auto containerUVs = PIXELS_TO_UV_COORDS(1024, 1024, 1, 6, 736, 432); auto containerWidth = Scale(716, true); auto containerHeight = Scale(412, true); auto containerOffsetX = Scale(73, true); auto containerOffsetY = Scale(136, true); ImVec2 containerMin = { max.x - containerWidth - containerOffsetX, min.y + containerOffsetY }; ImVec2 containerMax = { containerMin.x + containerWidth, containerMin.y + containerHeight }; auto containerAlphaMotionTime = ComputeLinearMotion(g_stateTime, 0, 5, OptionsMenu::s_state != OptionsMenuState::Idle); auto containerAlphaMotion = IM_COL32(255, 255, 255, 255 * containerAlphaMotionTime); SetGradient(containerMin, containerMax, containerAlphaMotion, containerAlphaMotion, IM_COL32_WHITE_TRANS, IM_COL32(255, 255, 255, 20 * containerAlphaMotionTime)); drawList->AddImage(g_upTexMainMenu9.get(), containerMin, containerMax, GET_UV_COORDS(containerUVs)); ResetGradient(); auto optionsOffsetX = Scale(170, true); auto optionsOffsetY = Scale(23, true); DrawOptions({ containerMin.x + optionsOffsetX, containerMin.y + optionsOffsetY }, { res.x, containerMax.y - optionsOffsetY }); } void OptionsMenu::Draw() { if (!s_isVisible) { if (g_isClosingButtonWindow) { ButtonWindow::Close(); g_isClosingButtonWindow = false; } return; } auto* drawList = ImGui::GetBackgroundDrawList(); auto& res = ImGui::GetIO().DisplaySize; ImVec2 min = { g_horzCentre + g_aspectRatioNarrowMargin, g_vertCentre }; ImVec2 max = { res.x - min.x, res.y - min.y }; auto alphaMotionTime = s_isPause ? ComputeLinearMotion(g_stateTime, 0, 10, s_state == OptionsMenuState::Closing) : 0.0; auto alpha = s_isPause ? Lerp(0, 175, alphaMotionTime) : 255; auto gradientTop = IM_COL32(0, 103, 255, alpha); auto gradientBottom = IM_COL32(0, 41, 100, alpha); auto drawBackground = [=]() { drawList->AddRectFilledMultiColor({ 0.0f, g_vertCentre }, { res.x, res.y - g_vertCentre }, gradientTop, gradientTop, gradientBottom, gradientBottom); }; if (s_isPause) { drawBackground(); } else { auto horzMargin = Scale(128, true); ImVec2 footerClipMin = { g_horzCentre + horzMargin, res.y - g_vertCentre - Scale(152, true) }; ImVec2 footerClipMax = { res.x - g_horzCentre - horzMargin, res.y - g_vertCentre - Scale(107, true) }; drawList->PushClipRect(footerClipMin, footerClipMax); drawBackground(); drawList->PopClipRect(); } auto upIsHeld = false; auto downIsHeld = false; auto leftIsHeld = false; auto rightIsHeld = false; switch (s_state) { case OptionsMenuState::Opening: s_commonMenu.Open(); s_state = OptionsMenuState::Idle; break; case OptionsMenuState::Idle: { if (s_commonMenu.IsOpen()) { for (auto& spInputManager : App::s_pApp->m_pDoc->m_vspInputManager) { auto& rPadState = spInputManager->m_PadState; if (rPadState.IsDown(Sonicteam::SoX::Input::KeyState_DPadUp) || -rPadState.LeftStickVertical > 0.5f) upIsHeld = true; if (!g_upWasHeld && upIsHeld) g_up = true; if (rPadState.IsDown(Sonicteam::SoX::Input::KeyState_DPadDown) || -rPadState.LeftStickVertical < -0.5f) downIsHeld = true; if (!g_downWasHeld && downIsHeld) g_down = true; if (rPadState.IsDown(Sonicteam::SoX::Input::KeyState_DPadLeft) || -rPadState.LeftStickHorizontal > 0.5f) leftIsHeld = true; if (!g_leftWasHeld && leftIsHeld) g_left = true; if (rPadState.IsDown(Sonicteam::SoX::Input::KeyState_DPadRight) || -rPadState.LeftStickHorizontal < -0.5f) rightIsHeld = true; if (!g_rightWasHeld && rightIsHeld) g_right = true; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_A)) g_isAccepted = true; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_B)) g_isDeclined = true; if (rPadState.IsPressed(Sonicteam::SoX::Input::KeyState_X)) g_isReset = true; } } if (s_isPause) DrawArrows({ 0, 0 }, res, g_chevronTime); auto buttons = s_flowState == OptionsMenuFlowState::OptionCursor && g_optionCanReset ? "Button_ResetSelectBack" : "Button_SelectBack"; ButtonWindow::Open(buttons, false); break; } case OptionsMenuState::Closing: { auto closingTime = s_commonMenu.Close(); static bool s_isProcessedMessages{}; if (closingTime >= 0.5) { if (closingTime >= 1.0) { MainMenuTaskPatches::s_hideButtonWindow = false; g_isClosingButtonWindow = true; s_isProcessedMessages = false; s_pMainMenuTask = nullptr; s_isVisible = false; break; } if (!s_isProcessedMessages && s_pMainMenuTask) { guest_stack_var msgSetCursor ( Sonicteam::HUDMainMenu::HUDMainMenuState_MainCursorIntro, s_pMainMenuTask->m_MainMenuSelectedIndex ); // Play cursor intro animation. s_pMainMenuTask->m_pHUDMainMenu->ProcessMessage(msgSetCursor.get()); guest_stack_var msgTransition ( Sonicteam::HUDMainMenu::HUDMainMenuState_OptionsIntro, 3 ); // Play options -> main menu transition. s_pMainMenuTask->m_pHUDMainMenu->ProcessMessage(msgTransition.get()); s_isProcessedMessages = true; } } break; } case OptionsMenuState::Restarting: { static bool s_restartPromptOpened = false; static bool s_restartFaderBegun = false; static int s_restartMessageResult = -1; auto message = Localise("Options_Message_Restart"); std::array options = { Localise("Common_Yes"), Localise("Common_No") }; if (!s_restartPromptOpened) { // Fade out description. s_commonMenu.SetDescription(" "); for (auto& def : g_configDefinitions) { if (def->RequiresRestart() && def->IsValueChanged()) message += "\n- " + def->GetNameLocalised(Config::Language); } s_restartPromptOpened = true; } if (!s_restartFaderBegun && MessageWindow::Open(message, &s_restartMessageResult, options) == MSG_CLOSED) { if (s_restartMessageResult == 0) { Fader::FadeOut(1, []() { App::Restart({ "--use-cwd --skip-logos" }); }); s_restartFaderBegun = true; } else { s_commonMenu.SetDescription(GetCategoryDescription((OptionsMenuCategory)g_categoryIndex)); s_state = OptionsMenuState::Opening; } s_restartPromptOpened = false; s_restartMessageResult = -1; } break; } } switch (s_flowState) { case OptionsMenuFlowState::CategoryCursor: { auto categoryCount = (int)OptionsMenuCategory::Count; // Remove debug category from cursor select. if (!s_isDebugUnlocked) categoryCount -= 1; MoveCursor(g_categoryIndex, g_flowStateTime, 0, categoryCount, []() { ResetOptionSelection(); g_categoryTime = ImGui::GetTime(); g_cursorArrowsTime = g_categoryTime; s_commonMenu.SetDescription(GetCategoryDescription((OptionsMenuCategory)g_categoryIndex)); }); if (CheckAndDiscard(g_isAccepted)) { Game_PlaySound("main_deside"); SetFlowState(OptionsMenuFlowState::OptionCursor); g_cursorArrowsTime = ImGui::GetTime(); g_scrollArrowsTime = g_cursorArrowsTime; } if (CheckAndDiscard(g_isDeclined)) { Game_PlaySound("window_close"); g_stateTime = ImGui::GetTime(); g_categoryTime = g_stateTime; if (IsRestartRequired()) { s_state = OptionsMenuState::Restarting; } else { Close(); } } break; } case OptionsMenuFlowState::OptionCursor: { MoveCursor(g_optionIndex, g_flowStateTime, 0, g_optionCount, []() { g_cursorArrowsTime = ImGui::GetTime(); g_scrollArrowsTime = g_cursorArrowsTime; }); if (CheckAndDiscard(g_isDeclined)) { Game_PlaySound("window_close"); SetFlowState(OptionsMenuFlowState::CategoryCursor); g_cursorArrowsTime = ImGui::GetTime(); s_commonMenu.SetDescription(GetCategoryDescription((OptionsMenuCategory)g_categoryIndex)); } break; } } DrawCategories(min, max); DrawContainer(min, max); s_commonMenu.Draw(); g_up = false; g_upWasHeld = upIsHeld; g_down = false; g_downWasHeld = downIsHeld; g_left = false; g_leftWasHeld = leftIsHeld; g_right = false; g_rightWasHeld = rightIsHeld; g_isAccepted = false; g_isDeclined = false; g_isReset = false; } void OptionsMenu::Init() { g_upTexMainMenu9 = LOAD_ZSTD_TEXTURE(g_main_menu9); } void OptionsMenu::Open(bool isPause) { s_commonMenu = CommonMenu(Localise("Options_Header_Name"), "", isPause); s_commonMenu.ReduceDraw = !isPause; g_stateTime = ImGui::GetTime(); g_chevronTime = g_stateTime; g_categoryTime = g_stateTime; g_cursorArrowsTime = g_stateTime; s_state = OptionsMenuState::Opening; s_isVisible = true; s_isPause = isPause; // Update stored values for config definitions // to check if their values have changed later. for (auto& def : g_configDefinitions) def->UpdateStore(); ResetSelection(); ButtonWindow::Open("Button_SelectBack", s_isPause); MainMenuTaskPatches::s_hideButtonWindow = true; } void OptionsMenu::Close() { if (s_state == OptionsMenuState::Closing) return; s_state = OptionsMenuState::Closing; g_stateTime = ImGui::GetTime(); g_cursorArrowsTime = g_stateTime; if (s_pBgmCue) { s_pBgmCue->SetPause(true); s_pBgmCue = nullptr; } if (s_isPause) ButtonWindow::Close(); Config::Save(); } bool OptionsMenu::CanClose() { return OptionsMenu::s_flowState == OptionsMenuFlowState::CategoryCursor; } bool OptionsMenu::IsRestartRequired() { if (!s_isVisible) return false; for (auto& def : g_configDefinitions) { if (def->RequiresRestart() && def->IsValueChanged()) return true; } return false; } void OptionsMenu::SetFlowState(OptionsMenuFlowState flowState) { s_flowState = flowState; g_flowStateTime = ImGui::GetTime(); } ================================================ FILE: MarathonRecomp/ui/options_menu.h ================================================ #pragma once #include #include #define MARATHON_RECOMP_OPTIONS_MENU enum class OptionsMenuState { Opening, Idle, Closing, Restarting }; enum class OptionsMenuFlowState { CategoryCursor, OptionCursor, OptionSelected }; enum class OptionsMenuCategory { System, Input, Audio, Video, Debug, Count }; class OptionsMenu { public: static inline CommonMenu s_commonMenu{}; static inline OptionsMenuState s_state{}; static inline OptionsMenuFlowState s_flowState{}; static inline Sonicteam::MainMenuTask* s_pMainMenuTask{}; static inline Sonicteam::SoX::Audio::Cue* s_pBgmCue{}; static inline bool s_isVisible{}; static inline bool s_isPause{}; static inline bool s_isDebugUnlocked{}; static void Init(); static void Draw(); static void Open(bool isPause = false); static void Close(); static bool CanClose(); static bool IsRestartRequired(); static void SetFlowState(OptionsMenuFlowState flowState); }; ================================================ FILE: MarathonRecomp/user/achievement_data.cpp ================================================ #include "achievement_data.h" #define NUM_RECORDS sizeof(Records) / sizeof(AchRecord) bool AchievementData::VerifySignature() const { char sig[4] = ACH_SIGNATURE; return memcmp(Signature, sig, sizeof(Signature)) == 0; } bool AchievementData::VerifyVersion() const { return Version <= ACH_VERSION; } bool AchievementData::VerifyChecksum() { return Checksum == CalculateChecksum(); } uint32_t AchievementData::CalculateChecksum() { auto result = 0; for (int i = 0; i < NUM_RECORDS; i++) { auto& record = Records[i]; for (size_t j = 0; j < sizeof(AchRecord); j++) result ^= ((uint8_t*)(&record))[j]; } return result; } ================================================ FILE: MarathonRecomp/user/achievement_data.h ================================================ #pragma once #include #define ACH_FILENAME "SonicNextAchievementData.bin" #define ACH_SIGNATURE { 'A', 'C', 'H', ' ' } #define ACH_VERSION 1 #define ACH_RECORDS 23 class AchievementData { public: #pragma pack(push, 1) struct AchRecord { uint16_t ID; time_t Timestamp; uint16_t Reserved[3]; }; #pragma pack(pop) char Signature[4] ACH_SIGNATURE; uint32_t Version{ ACH_VERSION }; uint32_t Checksum{}; uint32_t Reserved{}; AchRecord Records[ACH_RECORDS]{}; bool VerifySignature() const; bool VerifyVersion() const; bool VerifyChecksum(); uint32_t CalculateChecksum(); }; ================================================ FILE: MarathonRecomp/user/achievement_manager.cpp ================================================ #include "achievement_manager.h" #include #include #include #define NUM_RECORDS sizeof(AchievementManager::Data.Records) / sizeof(AchievementData::AchRecord) time_t AchievementManager::GetTimestamp(uint16_t id) { for (int i = 0; i < NUM_RECORDS; i++) { if (!Data.Records[i].ID) break; if (Data.Records[i].ID == id) return Data.Records[i].Timestamp; } return 0; } size_t AchievementManager::GetTotalRecords() { auto result = 0; for (int i = 0; i < NUM_RECORDS; i++) { if (!Data.Records[i].ID) break; result++; } return result; } bool AchievementManager::IsUnlocked(uint16_t id) { for (int i = 0; i < NUM_RECORDS; i++) { if (!Data.Records[i].ID) break; if (Data.Records[i].ID == id) return true; } return false; } void AchievementManager::Unlock(uint16_t id) { if (IsUnlocked(id)) return; for (int i = 0; i < NUM_RECORDS; i++) { if (Data.Records[i].ID == 0) { Data.Records[i].ID = id; Data.Records[i].Timestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); break; } } if (Config::AchievementNotifications) AchievementOverlay::Open(id); } void AchievementManager::UnlockAll() { for (uint16_t i = 1; i <= 23; i++) AchievementManager::Unlock(i); } void AchievementManager::Reset() { Data = {}; } bool AchievementManager::LoadBinary() { AchievementManager::Reset(); BinStatus = EAchBinStatus::Success; auto dataPath = GetDataPath(true); if (!std::filesystem::exists(dataPath)) { // Try loading base achievement data as fallback. dataPath = GetDataPath(false); if (!std::filesystem::exists(dataPath)) return true; } std::error_code ec; auto fileSize = std::filesystem::file_size(dataPath, ec); auto dataSize = sizeof(AchievementData); if (fileSize != dataSize) { BinStatus = EAchBinStatus::BadFileSize; return false; } std::ifstream file(dataPath, std::ios::binary); if (!file) { BinStatus = EAchBinStatus::IOError; return false; } AchievementData data{}; file.read((char*)&data.Signature, sizeof(data.Signature)); if (!data.VerifySignature()) { BinStatus = EAchBinStatus::BadSignature; file.close(); return false; } file.read((char*)&data.Version, sizeof(data.Version)); if (!data.VerifyVersion()) { BinStatus = EAchBinStatus::BadVersion; file.close(); return false; } file.seekg(0); file.read((char*)&data, sizeof(data)); if (!data.VerifyChecksum()) { BinStatus = EAchBinStatus::BadChecksum; file.close(); return false; } file.close(); memcpy(&Data, &data, dataSize); return true; } bool AchievementManager::SaveBinary(bool ignoreStatus) { if (!ignoreStatus && BinStatus != EAchBinStatus::Success) { LOGN_WARNING("Achievement data will not be saved in this session!"); return false; } LOGN("Saving achievements..."); std::ofstream file(GetDataPath(true), std::ios::binary); if (!file) { LOGN_ERROR("Failed to write achievement data."); return false; } Data.Checksum = Data.CalculateChecksum(); file.write((const char*)&Data, sizeof(AchievementData)); file.close(); BinStatus = EAchBinStatus::Success; return true; } ================================================ FILE: MarathonRecomp/user/achievement_manager.h ================================================ #pragma once #include enum class EAchBinStatus { Success, IOError, BadFileSize, BadSignature, BadVersion, BadChecksum }; class AchievementManager { public: static inline AchievementData Data{}; static inline EAchBinStatus BinStatus{ EAchBinStatus::Success }; static std::filesystem::path GetDataPath(bool checkForMods) { return GetSavePath(checkForMods) / ACH_FILENAME; } static time_t GetTimestamp(uint16_t id); static size_t GetTotalRecords(); static bool IsUnlocked(uint16_t id); static void Unlock(uint16_t id); static void UnlockAll(); static void Reset(); static bool LoadBinary(); static bool SaveBinary(bool ignoreStatus = false); }; ================================================ FILE: MarathonRecomp/user/config.cpp ================================================ #include "config.h" #include #include #include #include #include #include std::vector g_configDefinitions; #define CONFIG_DEFINE_ENUM_TEMPLATE(type) \ static std::unordered_map g_##type##_template = CONFIG_DEFINE_ENUM_TEMPLATE(ELanguage) { { "English", ELanguage::English }, { "Japanese", ELanguage::Japanese }, { "German", ELanguage::German }, { "French", ELanguage::French }, { "Spanish", ELanguage::Spanish }, { "Italian", ELanguage::Italian } }; CONFIG_DEFINE_ENUM_TEMPLATE(ECameraRotationMode) { { "Normal", ECameraRotationMode::Normal }, { "Reverse", ECameraRotationMode::Reverse } }; CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons) { { "Auto", EControllerIcons::Auto }, { "Xbox", EControllerIcons::Xbox }, { "PlayStation", EControllerIcons::PlayStation } }; CONFIG_DEFINE_ENUM_TEMPLATE(ELightDash) { { "X", ELightDash::X }, { "Y", ELightDash::Y } }; CONFIG_DEFINE_ENUM_TEMPLATE(ESlidingAttack) { { "B", ESlidingAttack::B }, { "X", ESlidingAttack::X } }; CONFIG_DEFINE_ENUM_TEMPLATE(SDL_Scancode) { { "???", SDL_SCANCODE_UNKNOWN }, { "A", SDL_SCANCODE_A }, { "B", SDL_SCANCODE_B }, { "C", SDL_SCANCODE_C }, { "D", SDL_SCANCODE_D }, { "E", SDL_SCANCODE_E }, { "F", SDL_SCANCODE_F }, { "G", SDL_SCANCODE_G }, { "H", SDL_SCANCODE_H }, { "I", SDL_SCANCODE_I }, { "J", SDL_SCANCODE_J }, { "K", SDL_SCANCODE_K }, { "L", SDL_SCANCODE_L }, { "M", SDL_SCANCODE_M }, { "N", SDL_SCANCODE_N }, { "O", SDL_SCANCODE_O }, { "P", SDL_SCANCODE_P }, { "Q", SDL_SCANCODE_Q }, { "R", SDL_SCANCODE_R }, { "S", SDL_SCANCODE_S }, { "T", SDL_SCANCODE_T }, { "U", SDL_SCANCODE_U }, { "V", SDL_SCANCODE_V }, { "W", SDL_SCANCODE_W }, { "X", SDL_SCANCODE_X }, { "Y", SDL_SCANCODE_Y }, { "Z", SDL_SCANCODE_Z }, { "1", SDL_SCANCODE_1 }, { "2", SDL_SCANCODE_2 }, { "3", SDL_SCANCODE_3 }, { "4", SDL_SCANCODE_4 }, { "5", SDL_SCANCODE_5 }, { "6", SDL_SCANCODE_6 }, { "7", SDL_SCANCODE_7 }, { "8", SDL_SCANCODE_8 }, { "9", SDL_SCANCODE_9 }, { "0", SDL_SCANCODE_0 }, { "RETURN", SDL_SCANCODE_RETURN }, { "ESCAPE", SDL_SCANCODE_ESCAPE }, { "BACKSPACE", SDL_SCANCODE_BACKSPACE }, { "TAB", SDL_SCANCODE_TAB }, { "SPACE", SDL_SCANCODE_SPACE }, { "MINUS", SDL_SCANCODE_MINUS }, { "EQUALS", SDL_SCANCODE_EQUALS }, { "LEFT BRACKET", SDL_SCANCODE_LEFTBRACKET }, { "RIGHT BRACKET", SDL_SCANCODE_RIGHTBRACKET }, { "BACKSLASH", SDL_SCANCODE_BACKSLASH }, { "NON-US HASH", SDL_SCANCODE_NONUSHASH }, { "SEMICOLON", SDL_SCANCODE_SEMICOLON }, { "APOSTROPHE", SDL_SCANCODE_APOSTROPHE }, { "GRAVE", SDL_SCANCODE_GRAVE }, { "COMMA", SDL_SCANCODE_COMMA }, { "PERIOD", SDL_SCANCODE_PERIOD }, { "SLASH", SDL_SCANCODE_SLASH }, { "CAPS LOCK", SDL_SCANCODE_CAPSLOCK }, { "F1", SDL_SCANCODE_F1 }, { "F2", SDL_SCANCODE_F2 }, { "F3", SDL_SCANCODE_F3 }, { "F4", SDL_SCANCODE_F4 }, { "F5", SDL_SCANCODE_F5 }, { "F6", SDL_SCANCODE_F6 }, { "F7", SDL_SCANCODE_F7 }, { "F8", SDL_SCANCODE_F8 }, { "F9", SDL_SCANCODE_F9 }, { "F10", SDL_SCANCODE_F10 }, { "F11", SDL_SCANCODE_F11 }, { "F12", SDL_SCANCODE_F12 }, { "PRINT SCREEN", SDL_SCANCODE_PRINTSCREEN }, { "SCROLL LOCK", SDL_SCANCODE_SCROLLLOCK }, { "PAUSE", SDL_SCANCODE_PAUSE }, { "INSERT", SDL_SCANCODE_INSERT }, { "HOME", SDL_SCANCODE_HOME }, { "PAGE UP", SDL_SCANCODE_PAGEUP }, { "DELETE", SDL_SCANCODE_DELETE }, { "END", SDL_SCANCODE_END }, { "PAGE DOWN", SDL_SCANCODE_PAGEDOWN }, { "RIGHT", SDL_SCANCODE_RIGHT }, { "LEFT", SDL_SCANCODE_LEFT }, { "DOWN", SDL_SCANCODE_DOWN }, { "UP", SDL_SCANCODE_UP }, { "NUM LOCK", SDL_SCANCODE_NUMLOCKCLEAR }, { "KP DIVIDE", SDL_SCANCODE_KP_DIVIDE }, { "KP MULTIPLY", SDL_SCANCODE_KP_MULTIPLY }, { "KP MINUS", SDL_SCANCODE_KP_MINUS }, { "KP PLUS", SDL_SCANCODE_KP_PLUS }, { "KP ENTER", SDL_SCANCODE_KP_ENTER }, { "KP 1", SDL_SCANCODE_KP_1 }, { "KP 2", SDL_SCANCODE_KP_2 }, { "KP 3", SDL_SCANCODE_KP_3 }, { "KP 4", SDL_SCANCODE_KP_4 }, { "KP 5", SDL_SCANCODE_KP_5 }, { "KP 6", SDL_SCANCODE_KP_6 }, { "KP 7", SDL_SCANCODE_KP_7 }, { "KP 8", SDL_SCANCODE_KP_8 }, { "KP 9", SDL_SCANCODE_KP_9 }, { "KP 0", SDL_SCANCODE_KP_0 }, { "KP PERIOD", SDL_SCANCODE_KP_PERIOD }, { "NON-US BACKSLASH", SDL_SCANCODE_NONUSBACKSLASH }, { "APPLICATION", SDL_SCANCODE_APPLICATION }, { "POWER", SDL_SCANCODE_POWER }, { "KP EQUALS", SDL_SCANCODE_KP_EQUALS }, { "F13", SDL_SCANCODE_F13 }, { "F14", SDL_SCANCODE_F14 }, { "F15", SDL_SCANCODE_F15 }, { "F16", SDL_SCANCODE_F16 }, { "F17", SDL_SCANCODE_F17 }, { "F18", SDL_SCANCODE_F18 }, { "F19", SDL_SCANCODE_F19 }, { "F20", SDL_SCANCODE_F20 }, { "F21", SDL_SCANCODE_F21 }, { "F22", SDL_SCANCODE_F22 }, { "F23", SDL_SCANCODE_F23 }, { "F24", SDL_SCANCODE_F24 }, { "EXECUTE", SDL_SCANCODE_EXECUTE }, { "HELP", SDL_SCANCODE_HELP }, { "MENU", SDL_SCANCODE_MENU }, { "SELECT", SDL_SCANCODE_SELECT }, { "STOP", SDL_SCANCODE_STOP }, { "AGAIN", SDL_SCANCODE_AGAIN }, { "UNDO", SDL_SCANCODE_UNDO }, { "CUT", SDL_SCANCODE_CUT }, { "COPY", SDL_SCANCODE_COPY }, { "PASTE", SDL_SCANCODE_PASTE }, { "FIND", SDL_SCANCODE_FIND }, { "MUTE", SDL_SCANCODE_MUTE }, { "VOLUME UP", SDL_SCANCODE_VOLUMEUP }, { "VOLUME DOWN", SDL_SCANCODE_VOLUMEDOWN }, { "KP COMMA", SDL_SCANCODE_KP_COMMA }, { "KP EQUALS AS400", SDL_SCANCODE_KP_EQUALSAS400 }, { "INTERNATIONAL 1", SDL_SCANCODE_INTERNATIONAL1 }, { "INTERNATIONAL 2", SDL_SCANCODE_INTERNATIONAL2 }, { "INTERNATIONAL 3", SDL_SCANCODE_INTERNATIONAL3 }, { "INTERNATIONAL 4", SDL_SCANCODE_INTERNATIONAL4 }, { "INTERNATIONAL 5", SDL_SCANCODE_INTERNATIONAL5 }, { "INTERNATIONAL 6", SDL_SCANCODE_INTERNATIONAL6 }, { "INTERNATIONAL 7", SDL_SCANCODE_INTERNATIONAL7 }, { "INTERNATIONAL 8", SDL_SCANCODE_INTERNATIONAL8 }, { "INTERNATIONAL 9", SDL_SCANCODE_INTERNATIONAL9 }, { "LANG 1", SDL_SCANCODE_LANG1 }, { "LANG 2", SDL_SCANCODE_LANG2 }, { "LANG 3", SDL_SCANCODE_LANG3 }, { "LANG 4", SDL_SCANCODE_LANG4 }, { "LANG 5", SDL_SCANCODE_LANG5 }, { "LANG 6", SDL_SCANCODE_LANG6 }, { "LANG 7", SDL_SCANCODE_LANG7 }, { "LANG 8", SDL_SCANCODE_LANG8 }, { "LANG 9", SDL_SCANCODE_LANG9 }, { "ALT ERASE", SDL_SCANCODE_ALTERASE }, { "SYS REQ", SDL_SCANCODE_SYSREQ }, { "CANCEL", SDL_SCANCODE_CANCEL }, { "CLEAR", SDL_SCANCODE_CLEAR }, { "PRIOR", SDL_SCANCODE_PRIOR }, { "RETURN 2", SDL_SCANCODE_RETURN2 }, { "SEPARATOR", SDL_SCANCODE_SEPARATOR }, { "OUT", SDL_SCANCODE_OUT }, { "OPER", SDL_SCANCODE_OPER }, { "CLEAR AGAIN", SDL_SCANCODE_CLEARAGAIN }, { "CR SEL", SDL_SCANCODE_CRSEL }, { "EX SEL", SDL_SCANCODE_EXSEL }, { "KP 00", SDL_SCANCODE_KP_00 }, { "KP 000", SDL_SCANCODE_KP_000 }, { "THOUSANDS SEPARATOR", SDL_SCANCODE_THOUSANDSSEPARATOR }, { "DECIMAL SEPARATOR", SDL_SCANCODE_DECIMALSEPARATOR }, { "CURRENCY UNIT", SDL_SCANCODE_CURRENCYUNIT }, { "CURRENCY SUBUNIT", SDL_SCANCODE_CURRENCYSUBUNIT }, { "KP LEFT PAREN", SDL_SCANCODE_KP_LEFTPAREN }, { "KP RIGHT PAREN", SDL_SCANCODE_KP_RIGHTPAREN }, { "KP LEFT BRACE", SDL_SCANCODE_KP_LEFTBRACE }, { "KP RIGHT BRACE", SDL_SCANCODE_KP_RIGHTBRACE }, { "KP TAB", SDL_SCANCODE_KP_TAB }, { "KP BACKSPACE", SDL_SCANCODE_KP_BACKSPACE }, { "KP A", SDL_SCANCODE_KP_A }, { "KP B", SDL_SCANCODE_KP_B }, { "KP C", SDL_SCANCODE_KP_C }, { "KP D", SDL_SCANCODE_KP_D }, { "KP E", SDL_SCANCODE_KP_E }, { "KP F", SDL_SCANCODE_KP_F }, { "KP XOR", SDL_SCANCODE_KP_XOR }, { "KP POWER", SDL_SCANCODE_KP_POWER }, { "KP PERCENT", SDL_SCANCODE_KP_PERCENT }, { "KP LESS", SDL_SCANCODE_KP_LESS }, { "KP GREATER", SDL_SCANCODE_KP_GREATER }, { "KP AMPERSAND", SDL_SCANCODE_KP_AMPERSAND }, { "KP DBL AMPERSAND", SDL_SCANCODE_KP_DBLAMPERSAND }, { "KP VERTICAL BAR", SDL_SCANCODE_KP_VERTICALBAR }, { "KP DBL VERTICAL BAR", SDL_SCANCODE_KP_DBLVERTICALBAR }, { "KP COLON", SDL_SCANCODE_KP_COLON }, { "KP HASH", SDL_SCANCODE_KP_HASH }, { "KP SPACE", SDL_SCANCODE_KP_SPACE }, { "KP AT", SDL_SCANCODE_KP_AT }, { "KP EXCLAM", SDL_SCANCODE_KP_EXCLAM }, { "KP MEM STORE", SDL_SCANCODE_KP_MEMSTORE }, { "KP MEM RECALL", SDL_SCANCODE_KP_MEMRECALL }, { "KP MEM CLEAR", SDL_SCANCODE_KP_MEMCLEAR }, { "KP MEM ADD", SDL_SCANCODE_KP_MEMADD }, { "KP MEM SUBTRACT", SDL_SCANCODE_KP_MEMSUBTRACT }, { "KP MEM MULTIPLY", SDL_SCANCODE_KP_MEMMULTIPLY }, { "KP MEM DIVIDE", SDL_SCANCODE_KP_MEMDIVIDE }, { "KP PLUS/MINUS", SDL_SCANCODE_KP_PLUSMINUS }, { "KP CLEAR", SDL_SCANCODE_KP_CLEAR }, { "KP CLEAR ENTRY", SDL_SCANCODE_KP_CLEARENTRY }, { "KP BINARY", SDL_SCANCODE_KP_BINARY }, { "KP OCTAL", SDL_SCANCODE_KP_OCTAL }, { "KP DECIMAL", SDL_SCANCODE_KP_DECIMAL }, { "KP HEXADECIMAL", SDL_SCANCODE_KP_HEXADECIMAL }, { "LEFT CTRL", SDL_SCANCODE_LCTRL }, { "LEFT SHIFT", SDL_SCANCODE_LSHIFT }, { "LEFT ALT", SDL_SCANCODE_LALT }, { "LEFT SUPER", SDL_SCANCODE_LGUI }, { "RIGHT CTRL", SDL_SCANCODE_RCTRL }, { "RIGHT SHIFT", SDL_SCANCODE_RSHIFT }, { "RIGHT ALT", SDL_SCANCODE_RALT }, { "RIGHT SUPER", SDL_SCANCODE_RGUI }, { "MODE", SDL_SCANCODE_MODE }, { "AUDIO NEXT", SDL_SCANCODE_AUDIONEXT }, { "AUDIO PREV", SDL_SCANCODE_AUDIOPREV }, { "AUDIO STOP", SDL_SCANCODE_AUDIOSTOP }, { "AUDIO PLAY", SDL_SCANCODE_AUDIOPLAY }, { "AUDIO MUTE", SDL_SCANCODE_AUDIOMUTE }, { "MEDIA SELECT", SDL_SCANCODE_MEDIASELECT }, { "WWW", SDL_SCANCODE_WWW }, { "MAIL", SDL_SCANCODE_MAIL }, { "CALCULATOR", SDL_SCANCODE_CALCULATOR }, { "COMPUTER", SDL_SCANCODE_COMPUTER }, { "AC SEARCH", SDL_SCANCODE_AC_SEARCH }, { "AC HOME", SDL_SCANCODE_AC_HOME }, { "AC BACK", SDL_SCANCODE_AC_BACK }, { "AC FORWARD", SDL_SCANCODE_AC_FORWARD }, { "AC STOP", SDL_SCANCODE_AC_STOP }, { "AC REFRESH", SDL_SCANCODE_AC_REFRESH }, { "AC BOOKMARKS", SDL_SCANCODE_AC_BOOKMARKS }, { "BRIGHTNESS DOWN", SDL_SCANCODE_BRIGHTNESSDOWN }, { "BRIGHTNESS UP", SDL_SCANCODE_BRIGHTNESSUP }, { "DISPLAY SWITCH", SDL_SCANCODE_DISPLAYSWITCH }, { "KBD ILLUM TOGGLE", SDL_SCANCODE_KBDILLUMTOGGLE }, { "KBD ILLUM DOWN", SDL_SCANCODE_KBDILLUMDOWN }, { "KBD ILLUM UP", SDL_SCANCODE_KBDILLUMUP }, { "EJECT", SDL_SCANCODE_EJECT }, { "SLEEP", SDL_SCANCODE_SLEEP }, { "APP 1", SDL_SCANCODE_APP1 }, { "APP 2", SDL_SCANCODE_APP2 }, { "AUDIO REWIND", SDL_SCANCODE_AUDIOREWIND }, { "AUDIO FAST FORWARD", SDL_SCANCODE_AUDIOFASTFORWARD }, { "SOFT LEFT", SDL_SCANCODE_SOFTLEFT }, { "SOFT RIGHT", SDL_SCANCODE_SOFTRIGHT }, { "CALL", SDL_SCANCODE_CALL }, { "END CALL", SDL_SCANCODE_ENDCALL }, }; CONFIG_DEFINE_ENUM_TEMPLATE(EChannelConfiguration) { { "Stereo", EChannelConfiguration::Stereo }, { "Surround", EChannelConfiguration::Surround } }; CONFIG_DEFINE_ENUM_TEMPLATE(EVoiceLanguage) { { "English", EVoiceLanguage::English }, { "Japanese", EVoiceLanguage::Japanese } }; CONFIG_DEFINE_ENUM_TEMPLATE(EGraphicsAPI) { { "Auto", EGraphicsAPI::Auto }, #ifdef MARATHON_RECOMP_D3D12 { "D3D12", EGraphicsAPI::D3D12 }, #endif #ifdef MARATHON_RECOMP_METAL { "Metal", EGraphicsAPI::Metal }, #endif { "Vulkan", EGraphicsAPI::Vulkan } }; CONFIG_DEFINE_ENUM_TEMPLATE(EWindowState) { { "Normal", EWindowState::Normal }, { "Maximised", EWindowState::Maximised }, { "Maximized", EWindowState::Maximised } }; CONFIG_DEFINE_ENUM_TEMPLATE(EAspectRatio) { { "Auto", EAspectRatio::Auto }, { "Original", EAspectRatio::Original } }; CONFIG_DEFINE_ENUM_TEMPLATE(ETripleBuffering) { { "Auto", ETripleBuffering::Auto }, { "On", ETripleBuffering::On }, { "Off", ETripleBuffering::Off } }; CONFIG_DEFINE_ENUM_TEMPLATE(EAntiAliasing) { { "Off", EAntiAliasing::Off }, { "2x MSAA", EAntiAliasing::MSAA2x }, { "4x MSAA", EAntiAliasing::MSAA4x }, { "8x MSAA", EAntiAliasing::MSAA8x } }; CONFIG_DEFINE_ENUM_TEMPLATE(EShadowResolution) { { "512", EShadowResolution::x512 }, { "1024", EShadowResolution::x1024 }, { "2048", EShadowResolution::x2048 }, { "4096", EShadowResolution::x4096 }, { "8192", EShadowResolution::x8192 }, }; CONFIG_DEFINE_ENUM_TEMPLATE(EReflectionResolution) { { "Full", EReflectionResolution::Full }, { "Half", EReflectionResolution::Half }, { "Quarter", EReflectionResolution::Quarter }, { "Eighth", EReflectionResolution::Eighth }, }; CONFIG_DEFINE_ENUM_TEMPLATE(ERadialBlur) { { "Off", ERadialBlur::Off }, { "Original", ERadialBlur::Original }, { "Enhanced", ERadialBlur::Enhanced } }; CONFIG_DEFINE_ENUM_TEMPLATE(ECutsceneAspectRatio) { { "Original", ECutsceneAspectRatio::Original }, { "Unlocked", ECutsceneAspectRatio::Unlocked } }; CONFIG_DEFINE_ENUM_TEMPLATE(EUIAlignmentMode) { { "Edge", EUIAlignmentMode::Edge }, { "Centre", EUIAlignmentMode::Centre }, { "Center", EUIAlignmentMode::Centre } }; #undef CONFIG_DEFINE #define CONFIG_DEFINE(section, type, name, defaultValue, requiresRestart) \ ConfigDef Config::name{section, #name, defaultValue, requiresRestart}; #undef CONFIG_DEFINE_HIDDEN #define CONFIG_DEFINE_HIDDEN(section, type, name, defaultValue, requiresRestart) \ ConfigDef Config::name{section, #name, defaultValue, requiresRestart}; #undef CONFIG_DEFINE_LOCALISED #define CONFIG_DEFINE_LOCALISED(section, type, name, defaultValue, requiresRestart) \ extern CONFIG_LOCALE g_##name##_locale; \ ConfigDef Config::name{section, #name, &g_##name##_locale, defaultValue, requiresRestart}; #undef CONFIG_DEFINE_ENUM #define CONFIG_DEFINE_ENUM(section, type, name, defaultValue, requiresRestart) \ ConfigDef Config::name{section, #name, defaultValue, requiresRestart, &g_##type##_template}; #undef CONFIG_DEFINE_ENUM_LOCALISED #define CONFIG_DEFINE_ENUM_LOCALISED(section, type, name, defaultValue, requiresRestart) \ extern CONFIG_LOCALE g_##name##_locale; \ extern CONFIG_ENUM_LOCALE(type) g_##type##_locale; \ ConfigDef Config::name{section, #name, &g_##name##_locale, defaultValue, requiresRestart, &g_##type##_template, &g_##type##_locale}; #include "config_def.h" // CONFIG_DEFINE template ConfigDef::ConfigDef(std::string section, std::string name, T defaultValue, bool requiresRestart) : Section(section), Name(name), DefaultValue(defaultValue), IsRestartRequired(requiresRestart) { g_configDefinitions.emplace_back(this); } // CONFIG_DEFINE_LOCALISED template ConfigDef::ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue, bool requiresRestart) : Section(section), Name(name), Locale(nameLocale), DefaultValue(defaultValue), IsRestartRequired(requiresRestart) { g_configDefinitions.emplace_back(this); } // CONFIG_DEFINE_ENUM template ConfigDef::ConfigDef(std::string section, std::string name, T defaultValue, bool requiresRestart, std::unordered_map* enumTemplate) : Section(section), Name(name), DefaultValue(defaultValue), IsRestartRequired(requiresRestart), EnumTemplate(enumTemplate) { for (const auto& pair : *EnumTemplate) EnumTemplateReverse[pair.second] = pair.first; g_configDefinitions.emplace_back(this); } // CONFIG_DEFINE_ENUM_LOCALISED template ConfigDef::ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue, bool requiresRestart, std::unordered_map* enumTemplate, CONFIG_ENUM_LOCALE(T)* enumLocale) : Section(section), Name(name), Locale(nameLocale), DefaultValue(defaultValue), IsRestartRequired(requiresRestart), EnumTemplate(enumTemplate), EnumLocale(enumLocale) { for (const auto& pair : *EnumTemplate) EnumTemplateReverse[pair.second] = pair.first; g_configDefinitions.emplace_back(this); } template ConfigDef::~ConfigDef() = default; template bool ConfigDef::IsHidden() { return isHidden && !IsLoadedFromConfig; } template void ConfigDef::SetHidden(bool hidden) { IsLoadedFromConfig = !hidden; } template void ConfigDef::ReadValue(toml::v3::ex::parse_result& toml) { if (auto pSection = toml[Section].as_table()) { const auto& section = *pSection; if constexpr (std::is_same::value) { Value = section[Name].value_or(DefaultValue); } else if constexpr (std::is_enum_v) { auto value = section[Name].value_or(std::string()); auto it = EnumTemplate->find(value); if (it != EnumTemplate->end()) { Value = it->second; } else { Value = DefaultValue; } } else { Value = section[Name].value_or(DefaultValue); } if (Callback) Callback(this); if (pSection->contains(Name)) IsLoadedFromConfig = true; } } template void ConfigDef::MakeDefault() { Value = DefaultValue; if constexpr (std::is_enum_v) SnapToNearestAccessibleValue(false); } template std::string_view ConfigDef::GetSection() const { return Section; } template std::string_view ConfigDef::GetName() const { return Name; } template std::string ConfigDef::GetNameLocalised(ELanguage language) const { if (Locale != nullptr) { auto languageFindResult = Locale->find(language); if (languageFindResult == Locale->end()) languageFindResult = Locale->find(ELanguage::English); if (languageFindResult != Locale->end()) return std::get<0>(languageFindResult->second); } return Name; } template std::string ConfigDef::GetDescription(ELanguage language) const { if (Locale != nullptr) { auto languageFindResult = Locale->find(language); if (languageFindResult == Locale->end()) languageFindResult = Locale->find(ELanguage::English); if (languageFindResult != Locale->end()) return std::get<1>(languageFindResult->second); } return ""; } template bool ConfigDef::IsDefaultValue() const { return Value == DefaultValue; } template const void* ConfigDef::GetValue() const { return &Value; } template std::string ConfigDef::GetValueLocalised(ELanguage language) const { CONFIG_ENUM_LOCALE(T)* locale = nullptr; if constexpr (std::is_enum_v) { locale = EnumLocale; } else if constexpr (std::is_same_v) { return Value ? Localise("Common_On") : Localise("Common_Off"); } if (locale != nullptr) { ELanguage languages[] = { language, ELanguage::English }; for (auto languageToFind : languages) { auto languageFindResult = locale->find(languageToFind); if (languageFindResult != locale->end()) { auto valueFindResult = languageFindResult->second.find(Value); if (valueFindResult != languageFindResult->second.end()) return std::get<0>(valueFindResult->second); } if (languageToFind == ELanguage::English) break; } } return ToString(false); } template std::string ConfigDef::GetValueDescription(ELanguage language) const { CONFIG_ENUM_LOCALE(T)* locale = nullptr; if constexpr (std::is_enum_v) { locale = EnumLocale; } else if constexpr (std::is_same_v) { return ""; } if (locale != nullptr) { ELanguage languages[] = { language, ELanguage::English }; for (auto languageToFind : languages) { auto languageFindResult = locale->find(languageToFind); if (languageFindResult != locale->end()) { auto valueFindResult = languageFindResult->second.find(Value); if (valueFindResult != languageFindResult->second.end()) return std::get<1>(valueFindResult->second); } if (languageToFind == ELanguage::English) break; } } return ""; } template std::string ConfigDef::GetDefinition(bool withSection) const { std::string result; if (withSection) result += "[" + Section + "]\n"; result += Name + " = " + ToString(); return result; } template std::string ConfigDef::ToString(bool strWithQuotes) const { std::string result = "N/A"; if constexpr (std::is_same_v) { result = fmt::format("{}", Value); if (strWithQuotes) result = fmt::format("\"{}\"", result); } else if constexpr (std::is_enum_v) { auto it = EnumTemplateReverse.find(Value); if (it != EnumTemplateReverse.end()) result = fmt::format("{}", it->second); if (strWithQuotes) result = fmt::format("\"{}\"", result); } else { result = fmt::format("{}", Value); } return result; } template void ConfigDef::GetLocaleStrings(std::vector& localeStrings) const { if (Locale != nullptr) { for (auto& [language, nameAndDesc] : *Locale) { localeStrings.push_back(std::get<0>(nameAndDesc)); localeStrings.push_back(std::get<1>(nameAndDesc)); } } if (EnumLocale != nullptr) { for (auto& [language, locale] : *EnumLocale) { for (auto& [value, nameAndDesc] : locale) { localeStrings.push_back(std::get<0>(nameAndDesc)); localeStrings.push_back(std::get<1>(nameAndDesc)); } } } } template void ConfigDef::SnapToNearestAccessibleValue(bool searchUp) { if constexpr (std::is_enum_v) { if (EnumTemplateReverse.empty() || InaccessibleValues.empty()) return; if (EnumTemplateReverse.size() == InaccessibleValues.size()) { assert(false && "All enum values are marked inaccessible and the nearest accessible value cannot be determined."); return; } auto it = EnumTemplateReverse.find(Value); if (it == EnumTemplateReverse.end()) { assert(false && "Enum value does not exist in the template."); return; } // Skip the enum value if it's marked as inaccessible. while (InaccessibleValues.find(it->first) != InaccessibleValues.end()) { if (searchUp) { ++it; if (it == EnumTemplateReverse.end()) it = EnumTemplateReverse.begin(); } else { if (it == EnumTemplateReverse.begin()) it = EnumTemplateReverse.end(); --it; } } Value = it->first; } } template bool ConfigDef::RequiresRestart() { return IsRestartRequired; } template void ConfigDef::UpdateStore() { m_storedValue = Value; } template bool ConfigDef::IsValueChanged() { return m_storedValue != Value; } std::filesystem::path Config::GetConfigPath() { return GetUserPath() / "config.toml"; } void Config::CreateCallbacks() { Config::Language.Callback = [](ConfigDef* def) { if (!App::s_isInit) return; OptionsMenu::s_commonMenu.SetTitle(Localise("Options_Header_Name"), false); }; Config::WindowSize.LockCallback = [](ConfigDef* def) { // Try matching the current window size with a known configuration. if (def->Value < 0) def->Value = GameWindow::FindNearestDisplayMode(); }; Config::WindowSize.ApplyCallback = [](ConfigDef* def) { auto displayModes = GameWindow::GetDisplayModes(); // Use largest supported resolution if overflowed. if (def->Value >= displayModes.size()) def->Value = displayModes.size() - 1; auto& mode = displayModes[def->Value]; auto centre = SDL_WINDOWPOS_CENTERED_DISPLAY(GameWindow::GetDisplay()); GameWindow::SetDimensions(mode.w, mode.h, centre, centre); }; Config::Monitor.Callback = [](ConfigDef* def) { GameWindow::SetDisplay(def->Value); }; Config::Fullscreen.Callback = [](ConfigDef* def) { GameWindow::SetFullscreen(def->Value); GameWindow::SetDisplay(Config::Monitor); }; Config::ResolutionScale.Callback = [](ConfigDef* def) { def->Value = std::clamp(def->Value, 0.25f, 2.0f); }; } void Config::Load() { if (!s_isCallbacksCreated) { CreateCallbacks(); s_isCallbacksCreated = true; } auto configPath = GetConfigPath(); if (!std::filesystem::exists(configPath)) { Config::Save(); return; } try { toml::parse_result toml; std::ifstream tomlStream(configPath); if (tomlStream.is_open()) toml = toml::parse(tomlStream); for (auto def : g_configDefinitions) { def->ReadValue(toml); #if _DEBUG LOGFN_UTILITY("{} (0x{:X})", def->GetDefinition().c_str(), (intptr_t)def->GetValue()); #endif } } catch (toml::parse_error& err) { LOGFN_ERROR("Failed to parse configuration: {}", err.what()); } } void Config::Save() { LOGN("Saving configuration..."); auto userPath = GetUserPath(); if (!std::filesystem::exists(userPath)) std::filesystem::create_directory(userPath); std::string result; std::string section; for (auto def : g_configDefinitions) { if (def->IsHidden()) continue; auto isFirstSection = section.empty(); auto isDefWithSection = section != def->GetSection(); auto tomlDef = def->GetDefinition(isDefWithSection); section = def->GetSection(); // Don't output prefix space for first section. if (!isFirstSection && isDefWithSection) result += '\n'; result += tomlDef + '\n'; } std::ofstream out(GetConfigPath()); if (out.is_open()) { out << result; out.close(); } else { LOGN_ERROR("Failed to write configuration."); } } bool Config::IsControllerIconsPS3() { auto result = Config::ControllerIcons == EControllerIcons::PlayStation; if (Config::ControllerIcons == EControllerIcons::Auto) result = hid::g_inputDeviceController == hid::EInputDevice::PlayStation; return result; } ================================================ FILE: MarathonRecomp/user/config.h ================================================ #pragma once #include class IConfigDef { public: virtual ~IConfigDef() = default; virtual bool IsHidden() = 0; virtual void SetHidden(bool hidden) = 0; virtual void ReadValue(toml::v3::ex::parse_result& toml) = 0; virtual void MakeDefault() = 0; virtual std::string_view GetSection() const = 0; virtual std::string_view GetName() const = 0; virtual std::string GetNameLocalised(ELanguage language) const = 0; virtual std::string GetDescription(ELanguage language) const = 0; virtual bool IsDefaultValue() const = 0; virtual const void* GetValue() const = 0; virtual std::string GetValueLocalised(ELanguage language) const = 0; virtual std::string GetValueDescription(ELanguage language) const = 0; virtual std::string GetDefinition(bool withSection = false) const = 0; virtual std::string ToString(bool strWithQuotes = true) const = 0; virtual void GetLocaleStrings(std::vector& localeStrings) const = 0; virtual void SnapToNearestAccessibleValue(bool searchUp) = 0; virtual bool RequiresRestart() = 0; virtual void UpdateStore() = 0; virtual bool IsValueChanged() = 0; }; #define CONFIG_LOCALE std::unordered_map> #define CONFIG_ENUM_LOCALE(type) std::unordered_map>> #define CONFIG_CALLBACK(name) if (name.Callback) name.Callback(&name) #define CONFIG_LOCK_CALLBACK(name) if (name.LockCallback) name.LockCallback(&name) #define CONFIG_APPLY_CALLBACK(name) if (name.ApplyCallback) name.ApplyCallback(&name) #define WINDOWPOS_CENTRED 0x2FFF0000 extern std::vector g_configDefinitions; enum class EVoiceLanguage : uint32_t { English, Japanese }; enum class ECameraRotationMode : uint32_t { Normal, Reverse }; enum class ELightDash : uint32_t { X, Y }; enum class ESlidingAttack : uint32_t { B, X }; enum class EControllerIcons : uint32_t { Auto, Xbox, PlayStation }; enum class EChannelConfiguration : uint32_t { Stereo, Surround }; enum class EGraphicsAPI : uint32_t { Auto, #ifdef MARATHON_RECOMP_D3D12 D3D12, #endif #ifdef MARATHON_RECOMP_METAL Metal, #endif Vulkan }; enum class EWindowState : uint32_t { Normal, Maximised }; enum class EAspectRatio : uint32_t { Auto, Original }; enum class ETripleBuffering : uint32_t { Auto, On, Off }; static constexpr int32_t FPS_MIN = 15; static constexpr int32_t FPS_MAX = 241; enum class EAntiAliasing : uint32_t { Off = 0, MSAA2x = 2, MSAA4x = 4, MSAA8x = 8 }; enum class EShadowResolution : int32_t { x512 = 512, x1024 = 1024, x2048 = 2048, x4096 = 4096, x8192 = 8192 }; enum class EReflectionResolution : int32_t { Eighth, Quarter, Half, Full }; enum class ERadialBlur : uint32_t { Off, Original, Enhanced }; enum class ECutsceneAspectRatio : uint32_t { Original, Unlocked }; enum class EUIAlignmentMode : uint32_t { Edge, Centre }; enum class EPlayerCharacter : uint32_t { Sonic, Shadow, Silver, Blaze, Amy, Tails, Rouge, Knuckles }; template class ConfigDef final : public IConfigDef { T m_storedValue{}; public: std::string Section{}; std::string Name{}; CONFIG_LOCALE* Locale{}; T DefaultValue{}; T Value{ DefaultValue }; std::set InaccessibleValues{}; std::unordered_map* EnumTemplate{}; std::map EnumTemplateReverse{}; CONFIG_ENUM_LOCALE(T)* EnumLocale{}; std::function*)> Callback; std::function*)> LockCallback; std::function*)> ApplyCallback; bool IsLoadedFromConfig{}; bool IsRestartRequired{}; // CONFIG_DEFINE ConfigDef(std::string section, std::string name, T defaultValue, bool requiresRestart); // CONFIG_DEFINE_LOCALISED ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue, bool requiresRestart); // CONFIG_DEFINE_ENUM ConfigDef(std::string section, std::string name, T defaultValue, bool requiresRestart, std::unordered_map* enumTemplate); // CONFIG_DEFINE_ENUM_LOCALISED ConfigDef(std::string section, std::string name, CONFIG_LOCALE* nameLocale, T defaultValue, bool requiresRestart, std::unordered_map* enumTemplate, CONFIG_ENUM_LOCALE(T)* enumLocale); ConfigDef(const ConfigDef&) = delete; ConfigDef(ConfigDef&&) = delete; ~ConfigDef(); bool IsHidden() override; void SetHidden(bool hidden) override; void ReadValue(toml::v3::ex::parse_result& toml) override; void MakeDefault() override; std::string_view GetSection() const override; std::string_view GetName() const override; std::string GetNameLocalised(ELanguage language) const override; std::string GetDescription(ELanguage language) const override; bool IsDefaultValue() const override; const void* GetValue() const override; std::string GetValueLocalised(ELanguage language) const override; std::string GetValueDescription(ELanguage language) const override; std::string GetDefinition(bool withSection = false) const override; std::string ToString(bool strWithQuotes = true) const override; void GetLocaleStrings(std::vector& localeStrings) const override; void SnapToNearestAccessibleValue(bool searchUp) override; bool RequiresRestart() override; void UpdateStore() override; bool IsValueChanged() override; operator T() const { return Value; } void operator=(const T& other) { Value = other; } }; #define CONFIG_DECLARE(type, name) static ConfigDef name; #define CONFIG_DECLARE_HIDDEN(type, name) static ConfigDef name; #define CONFIG_DEFINE(section, type, name, defaultValue, requiresRestart) CONFIG_DECLARE(type, name) #define CONFIG_DEFINE_HIDDEN(section, type, name, defaultValue, requiresRestart) CONFIG_DECLARE_HIDDEN(type, name) #define CONFIG_DEFINE_LOCALISED(section, type, name, defaultValue, requiresRestart) CONFIG_DECLARE(type, name) #define CONFIG_DEFINE_ENUM(section, type, name, defaultValue, requiresRestart) CONFIG_DECLARE(type, name) #define CONFIG_DEFINE_ENUM_LOCALISED(section, type, name, defaultValue, requiresRestart) CONFIG_DECLARE(type, name) class Config { public: #include "config_def.h" static inline bool s_isCallbacksCreated; static std::filesystem::path GetConfigPath(); static void CreateCallbacks(); static void Load(); static void Save(); static bool IsControllerIconsPS3(); }; ================================================ FILE: MarathonRecomp/user/config_def.h ================================================ // This file gets included in both config.h and config.cpp, with their own macros changing // the preprocessed output. The header is only going to have the declarations this way. CONFIG_DEFINE_ENUM_LOCALISED("System", ELanguage, Language, ELanguage::English, true); CONFIG_DEFINE_ENUM_LOCALISED("System", EVoiceLanguage, VoiceLanguage, EVoiceLanguage::English, false); CONFIG_DEFINE_LOCALISED("System", bool, Subtitles, true, false); CONFIG_DEFINE_LOCALISED("System", bool, Hints, true, false); CONFIG_DEFINE_LOCALISED("System", bool, ControlTutorial, true, false); CONFIG_DEFINE_LOCALISED("System", bool, Autosave, true, false); CONFIG_DEFINE_LOCALISED("System", bool, AchievementNotifications, true, false); CONFIG_DEFINE("System", bool, ShowConsole, false, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", ECameraRotationMode, HorizontalCamera, ECameraRotationMode::Reverse, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", ECameraRotationMode, VerticalCamera, ECameraRotationMode::Normal, false); CONFIG_DEFINE_LOCALISED("Input", bool, AllowBackgroundInput, false, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", EControllerIcons, ControllerIcons, EControllerIcons::Auto, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", ELightDash, LightDash, ELightDash::X, false); CONFIG_DEFINE_ENUM_LOCALISED("Input", ESlidingAttack, SlidingAttack, ESlidingAttack::X, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_A, SDL_SCANCODE_S, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_B, SDL_SCANCODE_D, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_X, SDL_SCANCODE_A, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_Y, SDL_SCANCODE_W, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadUp, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadDown, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadLeft, SDL_SCANCODE_Q, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_DPadRight, SDL_SCANCODE_E, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_Start, SDL_SCANCODE_RETURN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_Back, SDL_SCANCODE_BACKSPACE, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftTrigger, SDL_SCANCODE_1, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightTrigger, SDL_SCANCODE_3, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftBumper, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightBumper, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickUp, SDL_SCANCODE_UP, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickDown, SDL_SCANCODE_DOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickLeft, SDL_SCANCODE_LEFT, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_LeftStickRight, SDL_SCANCODE_RIGHT, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickUp, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickDown, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickLeft, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_ENUM("Bindings", SDL_Scancode, Key_RightStickRight, SDL_SCANCODE_UNKNOWN, false); CONFIG_DEFINE_LOCALISED("Audio", float, MasterVolume, 1.0f, false); CONFIG_DEFINE_LOCALISED("Audio", float, MusicVolume, 0.6f, false); CONFIG_DEFINE_LOCALISED("Audio", float, EffectsVolume, 0.6f, false); CONFIG_DEFINE_ENUM_LOCALISED("Audio", EChannelConfiguration, ChannelConfiguration, EChannelConfiguration::Stereo, true); CONFIG_DEFINE_LOCALISED("Audio", bool, MuteOnFocusLost, true, false); CONFIG_DEFINE_LOCALISED("Audio", bool, MusicAttenuation, false, false); CONFIG_DEFINE("Video", std::string, GraphicsDevice, "", true); CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::Auto, true); CONFIG_DEFINE("Video", int32_t, WindowX, WINDOWPOS_CENTRED, false); CONFIG_DEFINE("Video", int32_t, WindowY, WINDOWPOS_CENTRED, false); CONFIG_DEFINE_LOCALISED("Video", int32_t, WindowSize, -1, false); CONFIG_DEFINE("Video", int32_t, WindowWidth, 1280, false); CONFIG_DEFINE("Video", int32_t, WindowHeight, 720, false); CONFIG_DEFINE_ENUM("Video", EWindowState, WindowState, EWindowState::Normal, false); CONFIG_DEFINE_LOCALISED("Video", int32_t, Monitor, 0, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", EAspectRatio, AspectRatio, EAspectRatio::Auto, false); CONFIG_DEFINE_LOCALISED("Video", float, ResolutionScale, 1.0f, false); CONFIG_DEFINE_LOCALISED("Video", bool, Fullscreen, true, false); CONFIG_DEFINE_LOCALISED("Video", bool, VSync, true, false); CONFIG_DEFINE_ENUM("Video", ETripleBuffering, TripleBuffering, ETripleBuffering::Auto, false); CONFIG_DEFINE_LOCALISED("Video", int32_t, FPS, 60, false); CONFIG_DEFINE("Video", bool, ShowFPS, false, false); CONFIG_DEFINE("Video", uint32_t, MaxFrameLatency, 2, false); CONFIG_DEFINE_LOCALISED("Video", float, Brightness, 0.5f, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", EAntiAliasing, AntiAliasing, EAntiAliasing::MSAA4x, false); CONFIG_DEFINE_LOCALISED("Video", bool, TransparencyAntiAliasing, true, false); CONFIG_DEFINE("Video", uint32_t, AnisotropicFiltering, 16, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", EShadowResolution, ShadowResolution, EShadowResolution::x4096, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", EReflectionResolution, ReflectionResolution, EReflectionResolution::Half, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", ERadialBlur, RadialBlur, ERadialBlur::Original, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", ECutsceneAspectRatio, CutsceneAspectRatio, ECutsceneAspectRatio::Original, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIAlignmentMode, UIAlignmentMode, EUIAlignmentMode::Edge, false); CONFIG_DEFINE_HIDDEN("Codes", bool, AntigravityRetainsMomentum, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, ControllableBoundAttack, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, ControllableSpinkick, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, ControllableTeleportDash, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, DisableDWMRoundedCorners, false, true); CONFIG_DEFINE_HIDDEN("Codes", bool, DisableEdgeGrabLeftover, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, DisableKingdomValleyMist, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, DisableLowResolutionFontOnCustomUI, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, DisablePushState, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, DisableTitleInputDelay, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, EnableDebugMode, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, FixPowerUpJingleDuration, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, HUDToggleKey, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, InfiniteLives, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, MidairControlForMachSpeed, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, MidairControlForSnowboards, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreChainJumpFlips, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreChaosBoostJump, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreChaosSpearFlips, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreContextualHUDColours, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreDemoCameraMode, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, RestoreSonicActionGauge, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, SkipIntroLogos, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, TailsGauge, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, UnlimitedAntigravity, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, UseOfficialAchievementText, false, false); CONFIG_DEFINE_HIDDEN("Codes", bool, UseOfficialTitleOnTitleBar, false, true); CONFIG_DEFINE("Update", time_t, LastChecked, 0, false); ================================================ FILE: MarathonRecomp/user/paths.cpp ================================================ #include "paths.h" #include std::filesystem::path g_executableRoot = os::process::GetExecutableRoot(); std::filesystem::path g_userPath = BuildUserPath(); bool CheckPortable() { return std::filesystem::exists(g_executableRoot / "portable.txt"); } std::filesystem::path BuildUserPath() { if (CheckPortable()) return g_executableRoot; std::filesystem::path userPath; #if defined(_WIN32) PWSTR knownPath = NULL; if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &knownPath) == S_OK) userPath = std::filesystem::path{ knownPath } / USER_DIRECTORY; CoTaskMemFree(knownPath); #elif defined(__linux__) || defined(__APPLE__) const char* homeDir = getenv("HOME"); #if defined(__linux__) if (homeDir == nullptr) { homeDir = getpwuid(getuid())->pw_dir; } #endif if (homeDir != nullptr) { // Prefer to store in the .config directory if it exists. Use the home directory otherwise. std::filesystem::path homePath = homeDir; #if defined(__linux__) std::filesystem::path configPath = homePath / ".config"; #else std::filesystem::path configPath = homePath / "Library" / "Application Support"; #endif if (std::filesystem::exists(configPath)) userPath = configPath / USER_DIRECTORY; else userPath = homePath / ("." USER_DIRECTORY); } #else static_assert(false, "GetUserPath() not implemented for this platform."); #endif return userPath; } const std::filesystem::path& GetUserPath() { return g_userPath; } ================================================ FILE: MarathonRecomp/user/paths.h ================================================ #pragma once #include #define USER_DIRECTORY "MarathonRecomp" #ifndef GAME_INSTALL_DIRECTORY #define GAME_INSTALL_DIRECTORY "." #endif extern std::filesystem::path g_executableRoot; inline std::unordered_map g_pathCache; bool CheckPortable(); std::filesystem::path BuildUserPath(); const std::filesystem::path& GetUserPath(); inline std::filesystem::path GetGamePath() { #ifdef __APPLE__ // On macOS, there is the expectation that the app may be installed to // /Applications/, and the bundle should not be modified. Thus we need // to install game files to the user directory instead of next to the app. return GetUserPath(); #else return GAME_INSTALL_DIRECTORY; #endif } inline std::filesystem::path GetSavePath(bool checkForMods) { if (checkForMods && !ModLoader::s_saveFilePath.empty()) return ModLoader::s_saveFilePath.parent_path(); else return GetUserPath() / "save"; } // Returned file name may not necessarily be // equal to SYS-DATA as mods can assign anything. inline std::filesystem::path GetSaveFilePath(bool checkForMods) { if (checkForMods && !ModLoader::s_saveFilePath.empty()) return ModLoader::s_saveFilePath; else return GetSavePath(false) / "SonicNextSaveData.bin"; } static std::string toLower(std::string str) { std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); }); return str; }; inline void BuildPathCache(const std::string& gamePath) { for (const auto& entry : std::filesystem::recursive_directory_iterator(gamePath)) { std::string fullPath = entry.path().string(); std::string key = toLower(fullPath); g_pathCache[key] = entry.path(); } } inline std::filesystem::path FindInPathCache(const std::string& targetPath) { std::string key = toLower(targetPath); auto it = g_pathCache.find(key); if (it != g_pathCache.end()) { return it->second; } return {}; } ================================================ FILE: MarathonRecomp/user/registry.cpp ================================================ #include "registry.h" #include #include void Registry::Save() { os::registry::WriteValue(STR(ExecutableFilePath), os::process::GetExecutablePath()); } ================================================ FILE: MarathonRecomp/user/registry.h ================================================ #pragma once class Registry { public: static void Save(); }; ================================================ FILE: MarathonRecomp/utils/bit_stream.cpp ================================================ /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "bit_stream.h" BitStream::BitStream(uint8_t* buffer, size_t size_in_bits) : buffer_(buffer), size_bits_(size_in_bits) {} BitStream::~BitStream() = default; void BitStream::SetOffset(size_t offset_bits) { // assert_false(offset_bits > size_bits_); offset_bits_ = std::min(offset_bits, size_bits_); } size_t BitStream::BitsRemaining() { return size_bits_ - offset_bits_; } bool BitStream::IsOffsetValid(size_t num_bits) { size_t offset_bytes = offset_bits_ >> 3; size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); if (rel_offset_bits && int32_t(num_bits - 8 - rel_offset_bits) < 0) { return false; } return true; } uint64_t BitStream::Peek(size_t num_bits) { // FYI: The reason we can't copy more than 57 bits is: // 57 = 7 * 8 + 1 - that can only span a maximum of 8 bytes. // We can't read in 9 bytes (easily), so we limit it. // assert_false(num_bits > 57); // assert_false(offset_bits_ + num_bits > size_bits_); size_t offset_bytes = std::min(offset_bits_ >> 3, (size_bits_ - 64) >> 3); size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); // offset --> // ..[junk]..| target bits |....[junk]............. uint64_t bits = *(uint64_t*)(buffer_ + offset_bytes); // We need the data in little endian. // TODO: Have a flag specifying endianness of data? bits = ByteSwap(bits); // Shift right // .....[junk]........| target bits | bits >>= 64 - (rel_offset_bits + num_bits); // AND with mask // ...................| target bits | bits &= (1ULL << num_bits) - 1; return bits; } uint64_t BitStream::Read(size_t num_bits) { uint64_t val = Peek(num_bits); Advance(num_bits); return val; } // TODO: This is totally not tested! bool BitStream::Write(uint64_t val, size_t num_bits) { // assert_false(num_bits > 57); // assert_false(offset_bits_ + num_bits >= size_bits_); size_t offset_bytes = offset_bits_ >> 3; size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); // Construct a mask uint64_t mask = (1ULL << num_bits) - 1; mask <<= 64 - (rel_offset_bits + num_bits); mask = ~mask; // Shift the value left into position. val <<= 64 - (rel_offset_bits + num_bits); // offset -----> // ....[junk]...| target bits w/ junk |....[junk]...... uint64_t bits = *(uint64_t*)(buffer_ + offset_bytes); // AND with mask // ....[junk]...| target bits (0) |........[junk]...... bits &= mask; // OR with val // ....[junk]...| target bits (val) |......[junk]...... bits |= val; // Store into the bitstream. *(uint64_t*)(buffer_ + offset_bytes) = bits; // Advance the bitstream forward. Advance(num_bits); return true; } size_t BitStream::Copy(uint8_t* dest_buffer, size_t num_bits) { size_t offset_bytes = offset_bits_ >> 3; size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); size_t bits_left = num_bits; size_t out_offset_bytes = 0; // First: Copy the first few bits up to a byte boundary. if (rel_offset_bits) { uint64_t bits = Peek(8 - rel_offset_bits); uint8_t clear_mask = ~((uint8_t(1) << rel_offset_bits) - 1); dest_buffer[out_offset_bytes] &= clear_mask; dest_buffer[out_offset_bytes] |= (uint8_t)bits; bits_left -= 8 - rel_offset_bits; Advance(8 - rel_offset_bits); out_offset_bytes++; } // Second: Use memcpy for the bytes left. if (bits_left >= 8) { std::memcpy(dest_buffer + out_offset_bytes, buffer_ + offset_bytes + out_offset_bytes, bits_left / 8); out_offset_bytes += (bits_left / 8); Advance((bits_left / 8) * 8); bits_left -= (bits_left / 8) * 8; } // Third: Copy the last few bits. if (bits_left) { uint64_t bits = Peek(bits_left); bits <<= 8 - bits_left; uint8_t clear_mask = ((uint8_t(1) << bits_left) - 1); dest_buffer[out_offset_bytes] &= clear_mask; dest_buffer[out_offset_bytes] |= (uint8_t)bits; Advance(bits_left); } // Return the bit offset to the copied bits. return rel_offset_bits; } void BitStream::Advance(size_t num_bits) { SetOffset(offset_bits_ + num_bits); } ================================================ FILE: MarathonRecomp/utils/bit_stream.h ================================================ /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2015 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #pragma once class BitStream { public: BitStream(uint8_t *buffer, size_t size_in_bits); ~BitStream(); const uint8_t *buffer() const { return buffer_; } uint8_t *buffer() { return buffer_; } size_t offset_bits() const { return offset_bits_; } size_t size_bits() const { return size_bits_; } void Advance(size_t num_bits); void SetOffset(size_t offset_bits); size_t BitsRemaining(); bool IsOffsetValid(size_t num_bits); // Note: num_bits MUST be in the range 0-57 (inclusive) uint64_t Peek(size_t num_bits); uint64_t Read(size_t num_bits); bool Write(uint64_t val, size_t num_bits); // TODO(DrChat): Not tested! size_t Copy(uint8_t *dest_buffer, size_t num_bits); private: uint8_t *buffer_ = nullptr; size_t offset_bits_ = 0; size_t size_bits_ = 0; }; ================================================ FILE: MarathonRecomp/utils/ring_buffer.cpp ================================================ /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2015 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "ring_buffer.h" void RingBuffer::AdvanceRead(size_t _count) { ring_size_t count = static_cast(_count); if (read_offset_ + count < capacity_) { read_offset_ += count; } else { ring_size_t left_half = capacity_ - read_offset_; ring_size_t right_half = count - left_half; read_offset_ = right_half; } } void RingBuffer::AdvanceWrite(size_t _count) { ring_size_t count = static_cast(_count); if (write_offset_ + count < capacity_) { write_offset_ += count; } else { ring_size_t left_half = capacity_ - write_offset_; ring_size_t right_half = count - left_half; write_offset_ = right_half; } } RingBuffer::ReadRange RingBuffer::BeginRead(size_t _count) { ring_size_t count = std::min(static_cast(_count), capacity_); if (!!(count)) [[likely]] { if (read_offset_ + count < capacity_) { return {buffer_ + read_offset_, nullptr, count, 0}; } else { ring_size_t left_half = capacity_ - read_offset_; ring_size_t right_half = count - left_half; return {buffer_ + read_offset_, buffer_, left_half, right_half}; } } else { return {0}; } } void RingBuffer::EndRead(ReadRange read_range) { if (read_range.second) { read_offset_ = read_range.second_length; } else { read_offset_ += read_range.first_length; } } size_t RingBuffer::Read(uint8_t *buffer, size_t _count) { ring_size_t count = static_cast(_count); count = std::min(count, capacity_); if (!count) { return 0; } // Sanity check: Make sure we don't read over the write offset. if (read_offset_ < write_offset_) { assert(read_offset_ + count <= write_offset_); } else if (read_offset_ + count >= capacity_) { __attribute__((unused)) ring_size_t left_half = capacity_ - read_offset_; assert(count - left_half <= write_offset_); } if (read_offset_ + count < capacity_) { std::memcpy(buffer, buffer_ + read_offset_, count); read_offset_ += count; } else { ring_size_t left_half = capacity_ - read_offset_; ring_size_t right_half = count - left_half; std::memcpy(buffer, buffer_ + read_offset_, left_half); std::memcpy(buffer + left_half, buffer_, right_half); read_offset_ = right_half; } return count; } size_t RingBuffer::Write(const uint8_t *buffer, size_t _count) { ring_size_t count = static_cast(_count); count = std::min(count, capacity_); if (!count) { return 0; } // Sanity check: Make sure we don't write over the read offset. if (write_offset_ < read_offset_) { assert(write_offset_ + count <= read_offset_); } else if (write_offset_ + count >= capacity_) { __attribute__((unused)) size_t left_half = capacity_ - write_offset_; assert(count - left_half <= read_offset_); } if (write_offset_ + count < capacity_) { std::memcpy(buffer_ + write_offset_, buffer, count); write_offset_ += count; } else { ring_size_t left_half = capacity_ - write_offset_; ring_size_t right_half = count - left_half; std::memcpy(buffer_ + write_offset_, buffer, left_half); std::memcpy(buffer_, buffer + left_half, right_half); write_offset_ = right_half; } return count; } ================================================ FILE: MarathonRecomp/utils/ring_buffer.h ================================================ /** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2015 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #pragma once using ring_size_t = uint32_t; class RingBuffer { public: RingBuffer(uint8_t* buffer, size_t capacity) : buffer_(buffer), capacity_(static_cast(capacity)), read_offset_(0), write_offset_(0) {} uint8_t* buffer() const { return buffer_; } ring_size_t capacity() const { return capacity_; } bool empty() const { return read_offset_ == write_offset_; } ring_size_t read_offset() const { return read_offset_; } uintptr_t read_ptr() const { return uintptr_t(buffer_) + static_cast(read_offset_); } // todo: offset/ capacity_ is probably always 1 when its over, just check and // subtract instead void set_read_offset(size_t offset) { read_offset_ = offset % capacity_; } ring_size_t write_offset() const { return write_offset_; } uintptr_t write_ptr() const { return uintptr_t(buffer_) + write_offset_; } void set_write_offset(size_t offset) { write_offset_ = static_cast(offset) % capacity_; } ring_size_t write_count() const { if (read_offset_ == write_offset_) { return capacity_; } else if (write_offset_ < read_offset_) { return read_offset_ - write_offset_; } else { return (capacity_ - write_offset_) + read_offset_; } } void AdvanceRead(size_t count); void AdvanceWrite(size_t count); struct ReadRange { const uint8_t* first; const uint8_t* second; ring_size_t first_length; ring_size_t second_length; }; ReadRange BeginRead(size_t count); void EndRead(ReadRange read_range); size_t Read(uint8_t* buffer, size_t count); template size_t Read(T* buffer, size_t count) { return Read(reinterpret_cast(buffer), count); } template T Read() { static_assert(std::is_fundamental::value, "Immediate read only supports basic types!"); T imm; size_t read = Read(reinterpret_cast(&imm), sizeof(T)); assert(read == sizeof(T)); return imm; } size_t Write(const uint8_t* buffer, size_t count); template size_t Write(const T* buffer, size_t count) { return Write(reinterpret_cast(buffer), count); } template size_t Write(T& data) { return Write(reinterpret_cast(&data), sizeof(T)); } private: uint8_t* buffer_ = nullptr; ring_size_t capacity_ = 0; ring_size_t read_offset_ = 0; ring_size_t write_offset_ = 0; }; ================================================ FILE: MarathonRecomp/version.cmake ================================================ # version.cmake - written by hyperbx # Generates a compilation unit from template files for version information. # OUTPUT_FILE : the original output file from a previous generation. # TEMPLATE_FILE : the corresponding template file that was used to generate the output file. function(CheckOutputFile OUTPUT_FILE TEMPLATE_FILE) if (NOT OUTPUT_FILE) message(FATAL_ERROR "OUTPUT_FILE not specified.") endif() if (NOT TEMPLATE_FILE) message(FATAL_ERROR "TEMPLATE_FILE not specified.") endif() if (EXISTS "${OUTPUT_FILE}") # Read original output file. file(READ "${OUTPUT_FILE}" ORIGINAL_CONTENT) # Read template file and configure. file(READ "${TEMPLATE_FILE}" TEMPLATE_CONTENT) string(CONFIGURE "${TEMPLATE_CONTENT}" TEMPLATE_FILE_FINAL_CONTENT @ONLY) # Check if configured template matches the original output file and replace it if not. if (NOT ORIGINAL_CONTENT STREQUAL TEMPLATE_FILE_FINAL_CONTENT) message("-- Creating ${OUTPUT_FILE}") file(WRITE "${OUTPUT_FILE}" "${TEMPLATE_FILE_FINAL_CONTENT}") endif() else() message("-- Creating ${OUTPUT_FILE}") configure_file("${TEMPLATE_FILE}" "${OUTPUT_FILE}" @ONLY) endif() endfunction() # VERSION_TXT : the input text file containing the milestone, major, minor and revision variables. function(ParseVersionInfo VERSION_TXT) if (NOT VERSION_TXT) message(FATAL_ERROR "VERSION_TXT not specified.") endif() if (NOT EXISTS "${VERSION_TXT}") message(FATAL_ERROR "No such file: ${VERSION_TXT}") endif() file(READ "${VERSION_TXT}" FILE_CONTENT) string(REGEX REPLACE "\r?\n" ";" FILE_LINES "${FILE_CONTENT}") foreach(LINE ${FILE_LINES}) if (LINE STREQUAL "") continue() endif() # Find key/value pair match. string(REGEX MATCH "([A-Za-z_]+)=\\\"?([^\"]+)\\\"?" MATCH "${LINE}") if (MATCH) # Extract key/value pairs. string(REGEX REPLACE "([A-Za-z_]+)=.*" "\\1" KEY "${MATCH}") string(REGEX REPLACE "[A-Za-z_]+=\\\"?([^\"]+)\\\"?" "\\1" VALUE "${MATCH}") # Set environment variable. set("${KEY}" "${VALUE}" CACHE INTERNAL "${KEY}") endif() endforeach() endfunction() # VERSION_TXT : the input text file containing the milestone, major, minor and revision variables. # OUTPUT_CSV : the output string will be formatted as #,#,# without any alphabetic characters (optional). # BUILD_TYPE : the current build configuration (e.g. "Release", "RelWithDebInfo", "Debug") (optional). # SHOW_GIT_INFO : the Git commit hash and branch name should be appended to the version string (optional). # SHOW_BUILD_TYPE : the build type should be appended to the version string (optional). # OUTPUT_VAR : the output variable to store the version string in. function(CreateVersionString) cmake_parse_arguments(ARGS "" "VERSION_TXT;OUTPUT_CSV;BUILD_TYPE;SHOW_GIT_INFO;SHOW_BUILD_TYPE;OUTPUT_VAR" "" ${ARGN}) if (NOT ARGS_VERSION_TXT) message(FATAL_ERROR "VERSION_TXT not specified.") endif() if (NOT ARGS_OUTPUT_VAR) message(FATAL_ERROR "OUTPUT_VAR not specified.") endif() ParseVersionInfo("${ARGS_VERSION_TXT}") if (ARGS_OUTPUT_CSV) set(VERSION_STRING "${VERSION_MAJOR},${VERSION_MINOR},${VERSION_REVISION}") else() set(VERSION_STRING "v${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}") if (VERSION_MILESTONE) string(PREPEND VERSION_STRING "${VERSION_MILESTONE} ") endif() if (ARGS_SHOW_GIT_INFO) find_package(Git REQUIRED) # Get Git branch name. execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD OUTPUT_VARIABLE BRANCH_NAME OUTPUT_STRIP_TRAILING_WHITESPACE ) set(BRANCH_NAME ${BRANCH_NAME} CACHE INTERNAL "BRANCH_NAME") # Get Git commit hash. execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD OUTPUT_VARIABLE COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ) set(COMMIT_HASH ${COMMIT_HASH} CACHE INTERNAL "COMMIT_HASH") # Get short Git commit hash. execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD OUTPUT_VARIABLE COMMIT_HASH_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE ) set(COMMIT_HASH_SHORT ${COMMIT_HASH_SHORT} CACHE INTERNAL "COMMIT_HASH_SHORT") # Append commit hash and branch. if (COMMIT_HASH_SHORT AND BRANCH_NAME) string(APPEND VERSION_STRING ".${COMMIT_HASH_SHORT}-${BRANCH_NAME}") endif() endif() # Append build configuration. if (ARGS_BUILD_TYPE AND ARGS_SHOW_BUILD_TYPE) string(APPEND VERSION_STRING " (${ARGS_BUILD_TYPE})") endif() endif() if (ARGS_OUTPUT_VAR) set(${ARGS_OUTPUT_VAR} ${VERSION_STRING} PARENT_SCOPE) endif() endfunction() # OUTPUT_DIR : the output directory of the resulting *.h/*.cpp files. # VERSION_TXT : the input text file containing the milestone, major, minor and revision variables. # H_TEMPLATE : the input template for the header. # CXX_TEMPLATE : the input template for the source file. # BUILD_TYPE : the current build configuration (e.g. "Release", "RelWithDebInfo", "Debug") (optional). # SHOW_GIT_INFO : the Git commit hash and branch name should be appended to the version string (optional). # SHOW_BUILD_TYPE : the build type should be appended to the version string (optional). function(GenerateVersionSources) cmake_parse_arguments(ARGS "" "OUTPUT_DIR;VERSION_TXT;H_TEMPLATE;CXX_TEMPLATE;BUILD_TYPE;SHOW_GIT_INFO;SHOW_BUILD_TYPE" "" ${ARGN}) message("-- Generating version information...") if (NOT ARGS_OUTPUT_DIR) message(FATAL_ERROR "OUTPUT_DIR not specified.") endif() if (NOT ARGS_VERSION_TXT) message(FATAL_ERROR "VERSION_TXT not specified.") endif() if (NOT ARGS_H_TEMPLATE) message(FATAL_ERROR "H_TEMPLATE not specified.") endif() if (NOT ARGS_CXX_TEMPLATE) message(FATAL_ERROR "CXX_TEMPLATE not specified.") endif() # Required for *.cpp template. set(BUILD_TYPE ${ARGS_BUILD_TYPE}) CreateVersionString( VERSION_TXT ${ARGS_VERSION_TXT} BUILD_TYPE ${ARGS_BUILD_TYPE} SHOW_GIT_INFO ${ARGS_SHOW_GIT_INFO} SHOW_BUILD_TYPE ${ARGS_SHOW_BUILD_TYPE} OUTPUT_VAR VERSION_STRING ) message("-- Build: ${VERSION_STRING}") get_filename_component(H_TEMPLATE_NAME_WE "${ARGS_H_TEMPLATE}" NAME_WE) get_filename_component(CXX_TEMPLATE_NAME_WE "${ARGS_CXX_TEMPLATE}" NAME_WE) set(H_OUTPUT_FILE "${ARGS_OUTPUT_DIR}/${H_TEMPLATE_NAME_WE}.h") set(CXX_OUTPUT_FILE "${ARGS_OUTPUT_DIR}/${CXX_TEMPLATE_NAME_WE}.cpp") CheckOutputFile("${H_OUTPUT_FILE}" "${ARGS_H_TEMPLATE}") CheckOutputFile("${CXX_OUTPUT_FILE}" "${ARGS_CXX_TEMPLATE}") endfunction() ================================================ FILE: MarathonRecomp/xxHashMap.h ================================================ #pragma once struct xxHash { using is_avalanching = void; uint64_t operator()(XXH64_hash_t const& x) const noexcept { return x; } }; template using xxHashMap = ankerl::unordered_dense::map; inline XXH64_hash_t HashStr(const std::string_view& value) { return XXH3_64bits(value.data(), value.size()); } template inline T FindHash(const xxHashMap& map, const XXH64_hash_t hash) { auto findResult = map.find(hash); if (findResult != map.end()) return findResult->second; return {}; } ================================================ FILE: MarathonRecompLib/CMakeLists.txt ================================================ project("MarathonRecompLib") add_compile_options( -fno-strict-aliasing ) if (WIN32) add_compile_options(/fp:strict) else() add_compile_options(-ffp-model=strict) endif() target_compile_definitions(XenonRecomp PRIVATE XENON_RECOMP_CONFIG_FILE_PATH=\"${CMAKE_CURRENT_SOURCE_DIR}/config/Marathon.toml\" XENON_RECOMP_HEADER_FILE_PATH=\"${MARATHON_RECOMP_TOOLS_ROOT}/XenonRecomp/XenonUtils/ppc_context.h\") set(MARATHON_RECOMP_PPC_RECOMPILED_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ppc/ppc_config.h" "${CMAKE_CURRENT_SOURCE_DIR}/ppc/ppc_context.h" "${CMAKE_CURRENT_SOURCE_DIR}/ppc/ppc_func_mapping.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ppc/ppc_recomp_shared.h" ) foreach(i RANGE 0 145) list(APPEND MARATHON_RECOMP_PPC_RECOMPILED_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ppc/ppc_recomp.${i}.cpp") endforeach() add_custom_command( OUTPUT ${MARATHON_RECOMP_PPC_RECOMPILED_SOURCES} COMMAND $ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/private/default.xex" "${CMAKE_CURRENT_SOURCE_DIR}/config/Marathon.toml" USES_TERMINAL ) add_custom_command( OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/private/shader" COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/private/shader.arc" "${CMAKE_CURRENT_SOURCE_DIR}/private/shader" COMMAND $ "${CMAKE_CURRENT_SOURCE_DIR}/private/shader_lt.arc" "${CMAKE_CURRENT_SOURCE_DIR}/private/shader" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/private/shader.arc" "${CMAKE_CURRENT_SOURCE_DIR}/private/shader_lt.arc" ) set(XENOS_RECOMP_ROOT "${MARATHON_RECOMP_TOOLS_ROOT}/XenosRecomp/XenosRecomp") set(XENOS_RECOMP_INCLUDE "${XENOS_RECOMP_ROOT}/shader_common.h") target_compile_definitions(XenosRecomp PRIVATE XENOS_RECOMP_INPUT=\"${CMAKE_CURRENT_SOURCE_DIR}/private/shader\" XENOS_RECOMP_OUTPUT=\"${CMAKE_CURRENT_SOURCE_DIR}/shader/shader_cache.cpp\" XENOS_RECOMP_INCLUDE_INPUT=\"${XENOS_RECOMP_INCLUDE}\" MARATHON_RECOMP ) file(GLOB XENOS_RECOMP_SOURCES "${XENOS_RECOMP_ROOT}/*.h" "${XENOS_RECOMP_ROOT}/*.cpp" ) add_custom_command( OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/shader/shader_cache.cpp" COMMAND $ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/private/shader" ${XENOS_RECOMP_SOURCES} ${XENOS_RECOMP_INCLUDE} USES_TERMINAL ) add_library(MarathonRecompLib ${MARATHON_RECOMP_PPC_RECOMPILED_SOURCES} "shader/shader_cache.h" "shader/shader_cache.cpp" ) target_include_directories(MarathonRecompLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(MarathonRecompLib PRIVATE "${MARATHON_RECOMP_TOOLS_ROOT}/XenonRecomp/thirdparty/simde") target_precompile_headers(MarathonRecompLib PUBLIC "ppc/ppc_recomp_shared.h") ================================================ FILE: MarathonRecompLib/config/Marathon.toml ================================================ [main] file_path = "../private/default.xex" out_directory_path = "../ppc" switch_table_file_path = "./switch_table.toml" skip_lr = false skip_msr = false ctr_as_local = false xer_as_local = false reserved_as_local = false cr_as_local = false non_argument_as_local = false non_volatile_as_local = false restgprlr_14_address = 0x826de8e0 savegprlr_14_address = 0x826de890 restfpr_14_address = 0x826dfb9c savefpr_14_address = 0x826dfb50 restvmx_14_address = 0x826e1038 savevmx_14_address = 0x826e0da0 restvmx_64_address = 0x826e10cc savevmx_64_address = 0x826e0e34 longjmp_address = 0x826e4f10 setjmp_address = 0x826e5030 functions = [ { address = 0x8264C7C8, size = 0x68 }, { address = 0x82636918, size = 0xD0 }, { address = 0x8273C438, size = 0x4D8 }, { address = 0x824A6180, size = 0x90 }, { address = 0x82A97608, size = 0x64 }, { address = 0x82775010, size = 0xD4 }, { address = 0x8259A648, size = 0x84 }, { address = 0x8273C110, size = 0x11C }, { address = 0x821677D0, size = 0x7C }, { address = 0x825B3A28, size = 0x84 }, { address = 0x8273C368, size = 0xD0 }, { address = 0x82912600, size = 0x11C }, { address = 0x8244B920, size = 0x54 }, { address = 0x82912A18, size = 0x254 }, { address = 0x824C45B8, size = 0x50 }, { address = 0x823D49A8, size = 0x1BC }, { address = 0x8250F448, size = 0x98 }, { address = 0x82763E28, size = 0x228 }, { address = 0x828BFB18, size = 0x48 }, { address = 0x82A97670, size = 0x5C }, { address = 0x8223DF00, size = 0x144 }, { address = 0x828B9CC8, size = 0x80 }, { address = 0x8298D248, size = 0x100 }, { address = 0x82218068, size = 0xA4 }, { address = 0x8293F238, size = 0x114 }, { address = 0x824C4550, size = 0x68 }, { address = 0x82610860, size = 0x7C }, { address = 0x821FDC28, size = 0x90 }, { address = 0x82914568, size = 0x78 }, { address = 0x8223EFE8, size = 0x14C }, { address = 0x826103F0, size = 0x7C }, { address = 0x82A975A0, size = 0x68 }, { address = 0x8253B760, size = 0xEC }, { address = 0x82833088, size = 0xB8 }, { address = 0x82A976D0, size = 0x198 }, { address = 0x829124F8, size = 0x100 }, { address = 0x828A06B0, size = 0x114 }, { address = 0x828C4B00, size = 0x1E4 }, { address = 0x82333F80, size = 0xE8 }, { address = 0x823D4768, size = 0x23C }, { address = 0x821EE1C8, size = 0x5C }, { address = 0x821EEAA8, size = 0xFC }, { address = 0x825B4110, size = 0x5C }, { address = 0x824C4498, size = 0xB4 }, { address = 0x82217FC0, size = 0xA8 }, { address = 0x827D9040, size = 0x380 }, { address = 0x82A4B870, size = 0x78 }, { address = 0x82A97868, size = 0x16C }, { address = 0x82912728, size = 0x128 }, { address = 0x82655978, size = 0x26C }, { address = 0x82547890, size = 0x234 }, { address = 0x8273C9B8, size = 0x364 }, { address = 0x8223F138, size = 0xCC }, { address = 0x82860FD0, size = 0x1A8 }, { address = 0x829DA628, size = 0x70 }, { address = 0x8223DD38, size = 0x1C4 }, { address = 0x824ABA68, size = 0xE0 }, { address = 0x824ABB10, size = 0x38 } ] invalid_instructions = [ { data = 0x00000000, size = 4 }, # Padding { data = 0x826E3E60, size = 8 }, # C++ Frame Handler { data = 0x82AEA1CC, size = 8 } # C Specific Frame Handler # { data = 0x00485645, size = 44 } # End of .text ] [[midasm_hook]] name = "SetMSAALevel" address = 0x82607CD4 registers = ["r4"] [[midasm_hook]] name = "TitleTask_SetDefaultOption" address = 0x82512B10 registers = ["r3", "r4"] [[midasm_hook]] name = "SetLifeBarAnimation" address = 0x824D804C registers = ["r3", "r4", "r5", "r31"] [[midasm_hook]] name = "SetLifeBarAnimation" address = 0x824D80A8 registers = ["r3", "r4", "r5", "r31"] [[midasm_hook]] name = "SetLifeBarAnimation" address = 0x824D8F34 registers = ["r3", "r4", "r5", "r31"] [[midasm_hook]] name = "SetRingBarIndex" address = 0x824DEB40 registers = ["r5", "r31"] [[midasm_hook]] name = "PostureDisableEdgeGrabLeftover" address = 0x82200568 registers = ["r31"] [[midasm_hook]] name = "BeginBlockGetName" address = 0x82607850 registers = ["r3"] [[midasm_hook]] name = "PedestrianAnimationLOD" address = 0x824C2808 registers = ["r29"] after_instruction = true [[midasm_hook]] name = "MidairControlForMachSpeed" address = 0x82210380 jump_address_on_true = 0x82210388 [[midasm_hook]] name = "MidairControlForMachSpeed" address = 0x822103B4 jump_address_on_true = 0x822103BC [[midasm_hook]] name = "MidairControlForSnowboards" address = 0x82214710 jump_address_on_true = 0x82214714 [[midasm_hook]] name = "MidairControlForSnowboards" address = 0x8221487C jump_address_on_true = 0x82214880 [[midasm_hook]] name = "MidairControlForSnowboards" address = 0x822145B8 jump_address_on_true = 0x822145BC [[midasm_hook]] name = "MidairControlForSnowboards" address = 0x8221486C jump_address_on_true = 0x82214870 [[midasm_hook]] name = "GetRenderWorldFBO" address = 0x8260D6EC registers = ["r4"] [[midasm_hook]] name = "FurtherObjectShadows" address = 0x8260D7E0 registers = ["r26"] [[midasm_hook]] name = "PlayerDebugMode_RegisterLuaSetup" address = 0x82195AD0 registers = ["r5", "r27"] [[midasm_hook]] name = "PlayerDebugMode_RemapDebugExitButton" address = 0x82221CA8 registers = ["r30"] jump_address_on_true = 0x82221CAC jump_address_on_false = 0x82221CD8 [[midasm_hook]] name = "DisableRadialBlur" address = 0x82653834 jump_address_on_true = 0x82653840 [[midasm_hook]] name = "DisableKingdomValleyMist" address = 0x82311E50 jump_address_on_true = 0x82311E54 [[midasm_hook]] name = "PostureControl_RotationSpeedFix" address = 0x82201C6C registers = ["f1", "r1"] [[midasm_hook]] name = "CriCueUpdateDeltaTimeFix" address = 0x8260F238 registers = ["f13"] [[midasm_hook]] name = "PowerUpJingleDurationFix" address = 0x8216CC28 registers = ["f31"] # Overwrite [[midasm_hook]] name = "SaveAlertThreeOptionRemoveDeviceSelect" address = 0x8238CCCC registers = ["r5"] # No Save Data [[midasm_hook]] name = "SaveAlertThreeOptionRemoveDeviceSelect" address = 0x8238CE34 registers = ["r5"] [[midasm_hook]] name = "XmvPlayerLang" address = 0x8264D844 registers = ["r11"] [[midasm_hook]] name = "CsbSbkLang" address = 0x826124B0 registers = ["r8"] [[midasm_hook]] name = "CsbSbkLang" address = 0x826121BC registers = ["r8"] [[midasm_hook]] name = "CsbSbkLang" address = 0x821784F8 registers = ["r8"] [[midasm_hook]] name = "MovieVoiceLang" address = 0x8264B4E4 registers = ["r19"] [[midasm_hook]] name = "RenderCsdCastNodeMidAsmHook" address = 0x828C8FBC registers = ["r10", "r24"] [[midasm_hook]] name = "RenderCsdCastMidAsmHook" address = 0x828C8FFC registers = ["r4"] # Redirect tagdisplay_2p to tagdisplay_1p [[midasm_hook]] name = "Load2PDisplayMidAsmHook" address = 0x824DAE90 jump_address = 0x824DAE94 # Redirect battledisplay_2p to battledisplay_1p [[midasm_hook]] name = "Load2PDisplayMidAsmHook" address = 0x824DADE8 jump_address = 0x824DADEC [[midasm_hook]] name = "TitleTask_RedirectStateTransitionToOutroAnim" address = 0x82512C90 registers = ["r31"] jump_address_on_true = 0x82512C9C [[midasm_hook]] name = "ControllableTeleportDash" address = 0x8221C49C jump_address_on_true = 0x8221C4A0 [[midasm_hook]] name = "ControllableBoundAttack" address = 0x82217EE0 jump_address_on_true = 0x82217EE4 [[midasm_hook]] name = "ControllableBoundAttack" address = 0x82217F1C jump_address_on_true = 0x82217F20 [[midasm_hook]] name = "ControllableBoundAttack2" address = 0x82217DC8 registers = ["cr6"] jump_address_on_true = 0x82217DD8 jump_address_on_false = 0x82217DCC [[midasm_hook]] name = "ControllableSpinkick" address = 0x821A2984 jump_address_on_true = 0x821A2988 [[midasm_hook]] name = "ControllableSpinkick" address = 0x821A7A04 jump_address_on_true = 0x821A7A08 # Skip SPAABBTree initialisation for point light search [[midasm_hook]] name = "NOP" address = 0x8258DBC4 jump_address = 0x8258DBE0 # Skip SPAABBTree initialisation for point light search [[midasm_hook]] name = "NOP" address = 0x8258DF60 jump_address = 0x8258DF7C # Skip SPAABBTree initialisation for point light search [[midasm_hook]] name = "NOP" address = 0x8258E29C jump_address = 0x8258E2B8 # Skip SPAABBTree initialisation for point light search [[midasm_hook]] name = "NOP" address = 0x8258E598 jump_address = 0x8258E5B4 # Skip point light update optimisation [[midasm_hook]] name = "NOP" address = 0x8258DC28 jump_address = 0x8258DC2C # Skip point light update optimisation [[midasm_hook]] name = "NOP" address = 0x8258DFC4 jump_address = 0x8258DFC8 # Skip point light update optimisation [[midasm_hook]] name = "NOP" address = 0x8258E304 jump_address = 0x8258E308 # Skip point light update optimisation [[midasm_hook]] name = "NOP" address = 0x8258E600 jump_address = 0x8258E604 [[midasm_hook]] name = "TextEntityAlloc" address = 0x825EDB70 registers = ["r3"] [[midasm_hook]] name = "TextEntityAlloc" address = 0x825EDCEC registers = ["r3"] [[midasm_hook]] name = "CameraImp_SetFOV" address = 0x82590980 registers = ["f1"] [[midasm_hook]] name = "CameraImp_SetFOV" address = 0x82590AB4 registers = ["f1"] [[midasm_hook]] name = "SonicCamera_InvertAzDriveK" address = 0x82191D40 registers = ["f12"] [[midasm_hook]] name = "SonicCamera_InvertAltDriveK" address = 0x82191E00 registers = ["f12"] [[midasm_hook]] name = "SonicCamera_RotationSpeedFix" address = 0x82191DA0 registers = ["f0", "f31", "f13"] jump_address = 0x82191DA4 [[midasm_hook]] name = "SonicCamera_RotationSpeedFix" address = 0x82191E28 registers = ["f0", "f31", "f13"] jump_address = 0x82191E2C [[midasm_hook]] name = "RestoreChaosSpearFlips" address = 0x821A6730 jump_address_on_true = 0x821A6734 [[midasm_hook]] name = "RestoreChaosBoostJump" address = 0x821A6988 registers = ["r10", "r11"] [[midasm_hook]] name = "RestoreChainJumpFlips" address = 0x82199654 registers = ["r31", "r30", "r11", "f1", "f2", "f3"] [[midasm_hook]] name = "DisablePushState" address = 0x821A03F0 jump_address_on_true = 0x821A03FC [[midasm_hook]] name = "ObjTarzan_VolatileBranch" address = 0x8232D3E0 registers = ["r30"] jump_address_on_true = 0x8232D3E4 [[midasm_hook]] name = "ObjTarzan_PatchStaticDeltaTime" address = 0x8232D3E4 registers = ["f0", "r30"] after_instruction = true [[midasm_hook]] name = "ObjTarzan_PatchStaticDeltaTime" address = 0x8232D49C registers = ["f1", "f3"] after_instruction = true [[midasm_hook]] name = "ObjTarzan_PatchStaticDeltaTime" address = 0x8232D530 registers = ["f1", "f3"] after_instruction = true [[midasm_hook]] name = "ObjTarzan_PatchStaticDeltaTime" address = 0x8232D57C registers = ["f0", "f3"] after_instruction = true [[midasm_hook]] name = "ObjTarzan_PatchDeltaTimeArgument" address = 0x8232D818 registers = ["f3", "r4", "r1"] [[midasm_hook]] name = "ObjEspSwing_DecayRateFix" address = 0x8232A4D0 registers = ["f0", "f13", "f28"] jump_address = 0x8232A4D4 [[midasm_hook]] name = "ObjectInputWarp_ExtendMsgSuckPlayer" address = 0x82389D70 registers = ["r3", "r4", "f25"] jump_address = 0x82389D74 [[midasm_hook]] name = "ObjectInputWarp_ExtendMsgSuckPlayer" address = 0x82389E48 registers = ["r3", "r4", "f25"] jump_address = 0x82389E4C [[midasm_hook]] name = "PlayerObject_ProcessMsgSuckPlayer_FixForce" address = 0x8219B64C registers = ["r28", "f1"] [[midasm_hook]] name = "PlayerObject_ProcessMsgSuckPlayer_FixDeltaTime" address = 0x8219B660 registers = ["r28", "f31"] # Sonicteam::MyPE::CManageParticle::ManageParticleTask [[midasm_hook]] name = "NOP" address = 0x82645518 jump_address = 0x8264551C # Sonicteam::MyPE::MyEmitter / Sonicteam::GE1PE::Emitter [[midasm_hook]] name = "Spanverse_GE1PE_DeltaTimeFix" address = 0x82670728 registers = ["f1"] # Sonicteam::Spanverse::SpangleParticle [[midasm_hook]] name = "Spanverse_GE1PE_DeltaTimeFix" address = 0x82674030 registers = ["f13"] # Sonicteam::Spanverse::SpangleLight [[midasm_hook]] name = "Spanverse_GE1PE_DeltaTimeFix" address = 0x82674800 registers = ["f13"] [[midasm_hook]] name = "HUDLoadingAlloc" address = 0x824FAE14 registers = ["r3"] [[midasm_hook]] name = "HUDLoadingAlloc" address = 0x824FAF60 registers = ["r3"] [[midasm_hook]] name = "HUDLoadingAlloc" address = 0x824FB008 registers = ["r3"] [[midasm_hook]] name = "HUDLoading_DeltaTimeFix" address = 0x824D7510 registers = ["r31", "f1"] [[midasm_hook]] name = "HUDWindow_PreserveDeltaTime" address = 0x824FA03C registers = ["f31", "f1"] [[midasm_hook]] name = "HUDWindow_Callback" address = 0x824FA088 registers = ["f1", "f31"] [[midasm_hook]] name = "ObjectVehicleBike_EnemyShot_DisableVehicleCollisionLayer" address = 0x822ADBC4 registers = ["r31"] jump_address_on_true = 0x822ADBC8 [[midasm_hook]] name = "SonicGauge_FixFlags" address = 0x822196EC registers = ["r3", "r31"] [[midasm_hook]] name = "SonicGauge_FixGemSprite" address = 0x82185768 registers = ["r30"] [[midasm_hook]] name = "SonicGauge_FixGemSprite" address = 0x821855A4 registers = ["r29"] [[midasm_hook]] name = "SonicGauge_FixGemSprite" address = 0x82177DFC registers = ["r31"] [[midasm_hook]] name = "GameImp_PauseMenu_AddQuitPrefix" address = 0x8217DF60 registers = ["r1", "r30"] # Sonicteam::Player::State::SonicFall [[midasm_hook]] name = "RemapLightDash" address = 0x82216B4C registers = ["r3", "r11"] # Sonicteam::Player::State::SonicJump [[midasm_hook]] name = "RemapLightDash" address = 0x82216CC8 registers = ["r3", "r11"] # Sonicteam::Player::State::SonicWideSpring [[midasm_hook]] name = "RemapLightDash" address = 0x82216F14 registers = ["r3", "r11"] # Sonicteam::Player::State::SonicHomingAfter [[midasm_hook]] name = "RemapLightDash" address = 0x8221745C registers = ["r3", "r11"] # Sonicteam::Player::State::SonicBound [[midasm_hook]] name = "RemapLightDash" address = 0x82217CD4 registers = ["r3", "r11"] # Sonicteam::Player::State::SonicWait [[midasm_hook]] name = "RemapLightDash" address = 0x82218B24 registers = ["r3", "r11"] # Sonicteam::Player::State::SonicWalk [[midasm_hook]] name = "RemapLightDash" address = 0x82218DF8 registers = ["r3", "r11"] # Sonicteam::Player::State::SonicRun [[midasm_hook]] name = "RemapLightDash" address = 0x822190D8 registers = ["r3", "r11"] # Sonicteam::Player::State::FastRun [[midasm_hook]] name = "RemapLightDash" address = 0x82211660 registers = ["r3", "r11"] # Sonicteam::Player::State::ShadowWait [[midasm_hook]] name = "RemapLightDash" address = 0x821A5504 registers = ["r3", "r11"] # Sonicteam::Player::State::ShadowWalk [[midasm_hook]] name = "RemapLightDash" address = 0x821A57C8 registers = ["r3", "r11"] # Sonicteam::Player::State::ShadowRun [[midasm_hook]] name = "RemapLightDash" address = 0x821A5A90 registers = ["r3", "r11"] # Sonicteam::Player::State::ShadowFall [[midasm_hook]] name = "RemapLightDash" address = 0x821A5D58 registers = ["r3", "r11"] # Sonicteam::Player::State::ShadowJump [[midasm_hook]] name = "RemapLightDash" address = 0x821A5F60 registers = ["r3", "r11"] # Sonicteam::Player::State::ShadowWideSpring [[midasm_hook]] name = "RemapLightDash" address = 0x821A6270 registers = ["r3", "r11"] # Sonicteam::Player::State::ShadowHomingAfter / Sonicteam::Player::State::ShadowChaosSpearAfter [[midasm_hook]] name = "RemapLightDash" address = 0x821A6794 registers = ["r3", "r11"] # Sonicteam::Player::State::SonicWalk [[midasm_hook]] name = "RemapAntigravityEnter" address = 0x82218E5C registers = ["r11", "r28"] # Sonicteam::Player::State::SonicRun [[midasm_hook]] name = "RemapAntigravityEnter" address = 0x8221913C registers = ["r11", "r28"] # Sonicteam::Player::State::SonicSliding [[midasm_hook]] name = "RemapAntigravityExit" address = 0x82217AB0 registers = ["r11", "r30"] [[midasm_hook]] name = "AntigravityRetainsMomentum" address = 0x82217968 jump_address_on_true = 0x8221796C [[midasm_hook]] name = "AntigravityRetainsMomentum" address = 0x82217970 jump_address_on_true = 0x82217974 [[midasm_hook]] name = "UnlimitedAntigravity" address = 0x82217A94 jump_address_on_true = 0x82217A98 [[midasm_hook]] name = "DemoGMCamera_InvertHorizontal" address = 0x8218CC64 registers = ["f0"] [[midasm_hook]] name = "DemoGMCamera_InvertVertical" address = 0x8218CC6C registers = ["f0"] # Sonicteam::Player::Sound::SuperSonic [[midasm_hook]] name = "Super3_DisableChangeRequestHint" address = 0x8226B8EC jump_address_on_true = 0x8226B910 # Sonicteam::Player::Sound::SuperShadow / Sonicteam::Player::Sound::SuperSilver [[midasm_hook]] name = "Super3_DisableChangeRequestHint" address = 0x8226AB4C jump_address_on_true = 0x8226AB70 [[midasm_hook]] name = "InfiniteLives" address = 0x821857B0 jump_address_on_true = 0x821857B4 [[midasm_hook]] name = "UnlockAchievement" address = 0x825B0960 registers = ["r29"] [[midasm_hook]] name = "HUDGoldMedal_ShouldDestroyTable" address = 0x824D6720 jump_address_on_false = 0x824D6724 [[midasm_hook]] name = "MainMenuTask_GoldMedalResults_SkipOutro" address = 0x82502E14 jump_address_on_true = 0x82502E90 ================================================ FILE: MarathonRecompLib/config/switch_table.toml ================================================ # Generated by XenonAnalyse # ---- MANUAL JUMPTABLE ---- [[switch]] base = 0x826DC2EC r = 10 default = 0x826dc314 labels = [ 0x826dc314, #0 0x826dc48c, #1 0x826dc528, #2 0x826dc614, #3 0x826dc638, #4 0x826dc6f0, #5 0x826dc81c, #6 0x826dc8d0, #7 0x826dc988, #8 0x826dc9c8, #9 0x826dc9f4, #10 0x826dc9f4, #11 0x826dca00, #12 0x826dcac8, #13 0x826dcb30, #14 0x826dcbac, #15 0x826dcc74, #16 0x826dcf44, #17 0x826dd01c, #18 0x826dd1d8, #19 0x826dd240, #20 0x826dd388, #21 0x826dd410, #22 0x826dd4bc, #23 0x826dd4d8, #24 0x826dd5b4, #25 0x826dd654, #26 0x826dd65c, #27 0x826dd7b0, #28 ] # ---- ABSOLUTE JUMPTABLE ---- [[switch]] base = 0x821677E0 r = 4 default = 0x82167844 labels = [ 0x82167844, 0x82167814, 0x8216782C, 0x8216781C, 0x82167824, 0x8216783C, 0x82167834, ] [[switch]] base = 0x82169220 r = 11 default = 0x82169288 labels = [ 0x82169248, 0x82169254, 0x82169260, 0x8216926C, ] [[switch]] base = 0x82170F48 r = 11 default = 0x82170F24 labels = [ 0x82170F74, 0x82170F98, 0x8217113C, 0x82170F98, 0x82171170, ] [[switch]] base = 0x82171620 r = 29 default = 0x82171778 labels = [ 0x821716BC, 0x821716BC, 0x821716BC, 0x821716BC, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x82171778, 0x821716BC, 0x821716BC, ] [[switch]] base = 0x8217BE28 r = 10 default = 0x8217C5D0 labels = [ 0x8217BE60, 0x8217BE78, 0x8217BE90, 0x8217BEA8, 0x8217BEC0, 0x8217BF08, 0x8217BED8, 0x8217BEF0, ] [[switch]] base = 0x8217BF4C r = 10 default = 0x8217C5D0 labels = [ 0x8217C148, 0x8217C5D0, 0x8217C12C, 0x8217BFDC, 0x8217C5D0, 0x8217BFF4, 0x8217C0B4, 0x8217C0CC, 0x8217C024, 0x8217C03C, 0x8217C00C, 0x8217C5D0, 0x8217C5D0, 0x8217C5D0, 0x8217C5D0, 0x8217C084, 0x8217C09C, 0x8217C0E4, 0x8217C0FC, 0x8217C114, 0x8217C054, 0x8217C06C, 0x8217BFC0, ] [[switch]] base = 0x8217C19C r = 10 default = 0x8217C5D0 labels = [ 0x8217C274, 0x8217C28C, 0x8217C2A4, 0x8217C2BC, 0x8217C2D4, 0x8217C308, 0x8217C344, 0x8217C35C, 0x8217C5D0, 0x8217C374, 0x8217C38C, 0x8217BBF4, 0x8217C5D0, 0x8217C5D0, 0x8217C234, 0x8217C24C, 0x8217C3BC, 0x8217C3A4, 0x8217C47C, 0x8217C5D0, 0x8217C494, 0x8217C4AC, 0x8217C4C4, 0x8217C3D4, 0x8217C3FC, 0x8217C424, 0x8217C44C, 0x8217C464, 0x8217C5D0, 0x8217C5D0, 0x8217C4DC, 0x8217C4F4, ] [[switch]] base = 0x82184C68 r = 11 default = 0x82184D10 labels = [ 0x82184C90, 0x82184CB0, 0x82184CD0, 0x82184CD0, ] [[switch]] base = 0x82184F54 r = 11 default = 0x821850B4 labels = [ 0x82184F88, 0x82185078, 0x8218506C, 0x82184FEC, 0x82185028, 0x82185084, 0x82185090, ] [[switch]] base = 0x82184F94 r = 11 default = 0x82184FE0 labels = [ 0x82184FC8, 0x82184FC8, 0x821850B4, 0x82184FE0, 0x82184FC8, 0x82184FC8, 0x821850B4, ] [[switch]] base = 0x82184FF8 r = 11 default = 0x82184FE0 labels = [ 0x82184FC8, 0x82184FC8, 0x82184FE0, 0x82184FE0, 0x82184FC8, 0x82184FC8, ] [[switch]] base = 0x821850C0 r = 11 default = 0x821851F8 labels = [ 0x821850FC, 0x82185154, 0x82185164, 0x82185174, 0x8218519C, 0x821851AC, 0x821851BC, 0x821851CC, 0x821851DC, ] [[switch]] base = 0x82185104 r = 28 default = 0x82185144 labels = [ 0x8218513C, 0x82185144, 0x8218513C, 0x8218513C, 0x82185144, 0x8218513C, 0x8218513C, 0x8218513C, ] [[switch]] base = 0x82185E00 r = 11 default = 0x82185EB4 labels = [ 0x82185E38, 0x82185E78, 0x82185E48, 0x82185E58, 0x82185E68, 0x82185E88, 0x82185E98, 0x82185EA8, ] [[switch]] base = 0x82186550 r = 5 default = 0x82186640 labels = [ 0x82186594, 0x821865BC, 0x821865E8, 0x82186610, 0x8218662C, 0x8218663C, 0x8218665C, 0x82186678, 0x82186640, 0x82186690, 0x8218663C, ] [[switch]] base = 0x8218761C r = 5 default = 0x821877D8 labels = [ 0x82187660, 0x82187680, 0x82187698, 0x821876B0, 0x821876C8, 0x8218771C, 0x82187744, 0x82187760, 0x821877D8, 0x8218777C, 0x821877A8, ] [[switch]] base = 0x82188684 r = 11 default = 0x82188898 labels = [ 0x821886AC, 0x82188728, 0x821887A4, 0x82188820, ] [[switch]] base = 0x82189900 r = 11 default = 0x82189964 labels = [ 0x82189934, 0x8218981C, 0x8218981C, 0x82189944, 0x8218981C, 0x82189964, 0x82189954, ] [[switch]] base = 0x821975D4 r = 9 default = 0x82198310 labels = [ 0x82197690, 0x82198310, 0x82197658, 0x82197620, 0x8219763C, 0x82198310, 0x82198310, 0x82198310, 0x821976C8, 0x821976AC, 0x82198310, 0x82198310, 0x82197674, ] [[switch]] base = 0x82197708 r = 9 default = 0x82198310 labels = [ 0x82197744, 0x82197760, 0x82198310, 0x82198310, 0x8219777C, 0x82197798, 0x82198310, 0x82198310, 0x821977B4, ] [[switch]] base = 0x82197810 r = 9 default = 0x82198310 labels = [ 0x82197C24, 0x821979EC, 0x82197AB0, 0x82197ACC, 0x82197B3C, 0x82197B5C, 0x82197B20, 0x82197CD8, 0x82197CA0, 0x82198310, 0x82197974, 0x82197990, 0x82197D2C, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82197A08, 0x82197A5C, 0x82197A78, 0x82197A94, 0x82197A40, 0x82197A78, 0x82197A24, 0x82198310, 0x82197CBC, 0x82198310, 0x821979AC, 0x821979AC, 0x821979CC, 0x821979CC, 0x82198310, 0x82198310, 0x82197B98, 0x82198310, 0x82197BB4, 0x82198310, 0x82198310, 0x82197C44, 0x82197C68, 0x82197C84, 0x82197C68, 0x82198310, 0x82198310, 0x82197BD0, 0x82197BEC, 0x82197C08, 0x82197B7C, 0x82198310, 0x82198310, 0x82197D88, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82197D48, 0x82197D6C, 0x82198310, 0x82198310, 0x82198310, 0x82198310, 0x82197CF4, 0x82198310, 0x82198310, 0x82197D10, 0x82197AE8, 0x82197B04, ] [[switch]] base = 0x82197DE4 r = 9 default = 0x82198310 labels = [ 0x82197E84, 0x82197EA0, 0x82197EBC, 0x82197ED8, 0x82197EF4, 0x82197F34, 0x82197F18, 0x82197F50, 0x82197F70, 0x82197F90, 0x82197FAC, 0x82197FC8, 0x82198008, 0x82197FE8, 0x82198024, 0x82198040, 0x8219809C, 0x821980B8, 0x821980D4, 0x821980F8, 0x82198124, 0x82198140, 0x8219815C, 0x82198178, 0x821981A8, 0x821981C4, 0x82198228, 0x8219820C, 0x82198244, 0x82198260, 0x8219827C, 0x8219805C, 0x82198080, 0x82198310, ] [[switch]] base = 0x82198890 r = 9 default = 0x82198B18 labels = [ 0x821988C8, 0x82198968, 0x82198A08, 0x82198B18, 0x82198B00, 0x82198AA8, 0x82198B08, 0x82198B10, ] [[switch]] base = 0x8219BC40 r = 11 default = 0x8219BDB4 labels = [ 0x8219BC70, 0x8219BCAC, 0x8219BCF4, 0x8219BDB4, 0x8219BD3C, 0x8219BD84, ] [[switch]] base = 0x8219BE40 r = 11 default = 0x8219C45C labels = [ 0x8219BE70, 0x8219BF6C, 0x8219C068, 0x8219C16C, 0x8219C268, 0x8219C364, ] [[switch]] base = 0x8219C4BC r = 11 default = 0x8219C7EC labels = [ 0x8219C4E8, 0x8219C5B4, 0x8219C64C, 0x8219C7EC, 0x8219C718, ] [[switch]] base = 0x8219D72C r = 11 default = 0x8219DD28 labels = [ 0x8219D780, 0x8219D7E0, 0x8219D840, 0x8219D8A0, 0x8219D900, 0x8219D960, 0x8219D9C0, 0x8219DA20, 0x8219DA80, 0x8219DAE0, 0x8219DB40, 0x8219DBA0, 0x8219DC00, 0x8219DC60, 0x8219DCC0, ] [[switch]] base = 0x8219DEF4 r = 11 default = 0x8219E3D4 labels = [ 0x8219DF54, 0x8219DF94, 0x8219DFD4, 0x8219E014, 0x8219E054, 0x8219E094, 0x8219E0D4, 0x8219E114, 0x8219E154, 0x8219E194, 0x8219E1D4, 0x8219E214, 0x8219E254, 0x8219E298, 0x8219E2DC, 0x8219E320, 0x8219E360, 0x8219E3A0, ] [[switch]] base = 0x8219E6C8 r = 11 default = 0x8219E9B0 labels = [ 0x8219E718, 0x8219E74C, 0x8219E780, 0x8219E7B4, 0x8219E7E8, 0x8219E81C, 0x8219E850, 0x8219E884, 0x8219E8B8, 0x8219E8EC, 0x8219E920, 0x8219E9B0, 0x8219E954, 0x8219E988, ] [[switch]] base = 0x8219EA50 r = 11 default = 0x8219EF7C labels = [ 0x8219EA90, 0x8219EB14, 0x8219EB98, 0x8219EC1C, 0x8219EC94, 0x8219EDA4, 0x8219EDF8, 0x8219ED1C, 0x8219EE70, 0x8219EEF0, ] [[switch]] base = 0x8219F008 r = 11 default = 0x8219F1D8 labels = [ 0x8219F038, 0x8219F06C, 0x8219F0A0, 0x8219F0D4, 0x8219F108, 0x8219F16C, ] [[switch]] base = 0x8219F4C0 r = 11 default = 0x8219F68C labels = [ 0x8219F4E8, 0x8219F544, 0x8219F5A0, 0x8219F5FC, ] [[switch]] base = 0x821A3C74 r = 10 default = 0x821A3E20 labels = [ 0x821A3E08, 0x821A3E34, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E20, 0x821A3E4C, 0x821A3E70, ] [[switch]] base = 0x821A43B0 r = 11 default = 0x821A4434 labels = [ 0x821A43DC, 0x821A43DC, 0x821A4434, 0x821A4400, 0x821A441C, ] [[switch]] base = 0x821A6F60 r = 11 default = 0x821A6FA0 labels = [ 0x821A6F88, 0x821A6FB8, 0x821A6FE4, 0x821A6FFC, ] [[switch]] base = 0x821B1894 r = 11 default = 0x821B195C labels = [ 0x821B18BC, 0x821B18BC, 0x821B18C4, 0x821B18DC, ] [[switch]] base = 0x821B1C10 r = 11 default = 0x821B1CCC labels = [ 0x821B1C38, 0x821B1C50, 0x821B1CBC, 0x821B1C50, ] [[switch]] base = 0x821B2848 r = 11 default = 0x821B2998 labels = [ 0x821B2A78, 0x821B2954, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B2998, 0x821B29B4, 0x821B2998, 0x821B2A58, ] [[switch]] base = 0x821B2D78 r = 11 default = 0x821B3004 labels = [ 0x821B3088, 0x821B2FCC, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3004, 0x821B3068, 0x821B3004, 0x821B301C, 0x821B3034, ] [[switch]] base = 0x821B39CC r = 11 default = 0x821B3A14 labels = [ 0x821B3A8C, 0x821B3AA4, 0x821B3AB0, 0x821B39FC, 0x821B3A2C, 0x821B3A40, ] [[switch]] base = 0x821B4100 r = 11 default = 0x821B4144 labels = [ 0x821B412C, 0x821B415C, 0x821B4180, 0x821B4144, 0x821B41A8, ] [[switch]] base = 0x821B42F0 r = 11 default = 0x821B4380 labels = [ 0x821B4368, 0x821B4398, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B4380, 0x821B43BC, 0x821B4380, 0x821B4380, 0x821B43E4, ] [[switch]] base = 0x821B4C6C r = 11 default = 0x821B4CB4 labels = [ 0x821B4D2C, 0x821B4D44, 0x821B4D94, 0x821B4C9C, 0x821B4CCC, 0x821B4CE0, ] [[switch]] base = 0x821B4F34 r = 10 default = 0x821B501C labels = [ 0x821B4FCC, 0x821B4FF8, 0x821B501C, 0x821B501C, 0x821B501C, 0x821B501C, 0x821B501C, 0x821B501C, 0x821B4F74, 0x821B4FA0, ] [[switch]] base = 0x821B53EC r = 11 default = 0x821B5538 labels = [ 0x821B566C, 0x821B54F8, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B5538, 0x821B55A8, 0x821B5558, 0x821B5650, ] [[switch]] base = 0x821E7160 r = 3 default = 0x821E71AC labels = [ 0x821E71AC, 0x821E718C, 0x821E7194, 0x821E719C, 0x821E71A4, ] [[switch]] base = 0x821E81C0 r = 11 default = 0x821E8290 labels = [ 0x821E8290, 0x821E81EC, 0x821E81F8, 0x821E8204, 0x821E827C, ] [[switch]] base = 0x821EA7D0 r = 3 default = 0x821EACE8 labels = [ 0x821EA800, 0x821EA85C, 0x821EA900, 0x821EA980, 0x821EAAFC, 0x821EAB7C, ] [[switch]] base = 0x821EE1D8 r = 11 default = 0x821EE21C labels = [ 0x821EE204, 0x821EE204, 0x821EE204, 0x821EE20C, 0x821EE204, ] [[switch]] base = 0x821EE798 r = 11 default = 0x821EE8DC labels = [ 0x821EE7C8, 0x821EE7E8, 0x821EE7F8, 0x821EE88C, 0x821EE864, 0x821EE89C, ] [[switch]] base = 0x821EEAC0 r = 11 default = 0x0 labels = [ 0x821EEAEC, 0x821EEAFC, 0x821EEAEC, 0x821EEB3C, 0x821EEB74, ] [[switch]] base = 0x821F3C88 r = 11 default = 0x821F3D24 labels = [ 0x821F3CB0, 0x821F3CB0, 0x821F3CD4, 0x821F3CD4, ] [[switch]] base = 0x821F465C r = 11 default = 0x821F49AC labels = [ 0x821F4690, 0x821F46E4, 0x821F47C8, 0x821F47C8, 0x821F4878, 0x821F4718, 0x821F48FC, ] [[switch]] base = 0x821F8424 r = 11 default = 0x821F8478 labels = [ 0x821F844C, 0x821F8484, 0x821F8530, 0x821F85B8, ] [[switch]] base = 0x821F86B8 r = 11 default = 0x821F87A0 labels = [ 0x821F86E4, 0x821F8708, 0x821F8750, 0x821F8750, 0x821F8750, ] [[switch]] base = 0x821F9B30 r = 11 default = 0x821F9BD0 labels = [ 0x821F9B5C, 0x821F9B5C, 0x821F9B80, 0x821F9B80, 0x821F9B5C, ] [[switch]] base = 0x821FDC34 r = 11 default = 0x821FDCB0 labels = [ 0x821FDC70, 0x821FDC78, 0x821FDC80, 0x821FDC88, 0x821FDC90, 0x821FDC98, 0x821FDCB0, 0x821FDCA0, 0x821FDCA8, ] [[switch]] base = 0x82200700 r = 25 default = 0x82200890 labels = [ 0x82200734, 0x82200854, 0x82200854, 0x82200854, 0x822007CC, 0x822007EC, 0x82200810, ] [[switch]] base = 0x82200910 r = 25 default = 0x82200E70 labels = [ 0x82200950, 0x82200C58, 0x82200C58, 0x82200CE4, 0x82200C58, 0x82200D84, 0x82200E70, 0x82200E70, 0x82200E5C, 0x82200E5C, ] [[switch]] base = 0x822093A8 r = 10 default = 0x82209490 labels = [ 0x82209430, 0x82209490, 0x82209454, 0x82209490, 0x82209490, 0x82209490, 0x82209490, 0x82209490, 0x822093E8, 0x8220940C, ] [[switch]] base = 0x82209A28 r = 11 default = 0x82209B28 labels = [ 0x82209A50, 0x82209A90, 0x82209AD4, 0x82209B14, ] [[switch]] base = 0x8220B41C r = 10 default = 0x8220B46C labels = [ 0x8220B4FC, 0x8220B51C, 0x8220B528, 0x8220B44C, 0x8220B498, 0x8220B4AC, ] [[switch]] base = 0x82211C64 r = 11 default = 0x82211CDC labels = [ 0x82211C90, 0x82211CA4, 0x82211D1C, 0x82211CDC, 0x82211D40, ] [[switch]] base = 0x82217FE8 r = 11 default = 0x82218060 labels = [ 0x82218020, 0x82218028, 0x82218030, 0x82218038, 0x82218040, 0x82218048, 0x82218050, 0x82218058, ] [[switch]] base = 0x82218090 r = 11 default = 0x0 labels = [ 0x822180C8, 0x822180D0, 0x822180D8, 0x822180E0, 0x822180E8, 0x822180F0, 0x822180F8, 0x82218100, ] [[switch]] base = 0x8221AB68 r = 11 default = 0x8221ABD0 labels = [ 0x8221ABD8, 0x8221ABD0, 0x8221ABD0, 0x8221ABD0, 0x8221ABD8, 0x8221ABD8, 0x8221ABD0, 0x8221ABD8, 0x8221ABD0, 0x8221ABD8, 0x8221ABD0, 0x8221ABD8, 0x8221ABD8, 0x8221ABD0, 0x8221ABD0, 0x8221ABD0, 0x8221ABD0, 0x8221ABD0, 0x8221ABD0, 0x8221ABD8, ] [[switch]] base = 0x8221AF08 r = 11 default = 0x8221B008 labels = [ 0x8221AF38, 0x8221AF58, 0x8221AF78, 0x8221AFA4, 0x8221AFC4, 0x8221AFF0, ] [[switch]] base = 0x8221B0A8 r = 11 default = 0x8221B188 labels = [ 0x8221B0D8, 0x8221B0D8, 0x8221B0F8, 0x8221B124, 0x8221B144, 0x8221B170, ] [[switch]] base = 0x8221B6B8 r = 11 default = 0x8221B738 labels = [ 0x8221B720, 0x8221B730, 0x8221B730, 0x8221B738, 0x8221B720, 0x8221B720, 0x8221B738, 0x8221B720, 0x8221B738, 0x8221B720, 0x8221B738, 0x8221B720, 0x8221B720, 0x8221B738, 0x8221B738, 0x8221B738, 0x8221B738, 0x8221B738, 0x8221B738, 0x8221B720, ] [[switch]] base = 0x8221C90C r = 10 default = 0x8221C984 labels = [ 0x8221C938, 0x8221C978, 0x8221C940, 0x8221C978, 0x8221C95C, ] [[switch]] base = 0x8221C994 r = 11 default = 0x8221CA4C labels = [ 0x8221C9C0, 0x8221CA6C, 0x8221CACC, 0x8221CAF8, 0x8221CB18, ] [[switch]] base = 0x8221CBB0 r = 11 default = 0x8221CCA8 labels = [ 0x8221CBD8, 0x8221CC3C, 0x8221CC74, 0x8221CC8C, ] [[switch]] base = 0x8221D03C r = 11 default = 0x8221D178 labels = [ 0x8221D06C, 0x8221D088, 0x8221D0D8, 0x8221D114, 0x8221D15C, 0x8221D0F4, ] [[switch]] base = 0x82223640 r = 11 default = 0x822238B4 labels = [ 0x82223810, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x822238B4, 0x8222384C, 0x82223888, 0x822238B4, 0x82223890, ] [[switch]] base = 0x82223E88 r = 11 default = 0x82223F6C labels = [ 0x82223F38, 0x82223F40, 0x82223F48, 0x82223EC0, 0x82223F30, 0x82223F6C, 0x82223F6C, 0x82223EC0, ] [[switch]] base = 0x82224318 r = 11 default = 0x82224744 labels = [ 0x8222469C, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x822246D8, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x822246D8, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x82224744, 0x822246D8, 0x822246D8, ] [[switch]] base = 0x82224750 r = 11 default = 0x82224834 labels = [ 0x822247A0, 0x822247A0, 0x822247A0, 0x82224834, 0x82224834, 0x82224834, 0x82224834, 0x82224834, 0x822247A0, 0x822247A0, 0x822247A0, 0x822247A0, 0x822247A0, 0x822247A0, ] [[switch]] base = 0x82226858 r = 11 default = 0x82226DF0 labels = [ 0x82226B00, 0x82226B4C, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226D2C, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DCC, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226DF0, 0x82226B98, 0x82226B98, 0x82226BE4, 0x82226C30, 0x82226C7C, 0x82226CC8, 0x82226D14, 0x82226D1C, 0x82226D24, ] [[switch]] base = 0x82226E64 r = 30 default = 0x82226F9C labels = [ 0x82226E8C, 0x82226EA4, 0x82226EE8, 0x82226F2C, ] [[switch]] base = 0x82227F10 r = 11 default = 0x82228218 labels = [ 0x82228098, 0x82227FA8, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x822280B8, 0x82228218, 0x82228134, 0x82228184, 0x82228048, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x82228218, 0x822280A8, ] [[switch]] base = 0x82228A58 r = 11 default = 0x82228DE4 labels = [ 0x82228C08, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DA0, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228CFC, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228DE4, 0x82228D64, 0x82228DE4, 0x82228DE4, 0x82228D28, ] [[switch]] base = 0x822292D4 r = 11 default = 0x82229444 labels = [ 0x82229390, 0x822293CC, 0x82229318, 0x82229354, 0x82229444, 0x82229444, 0x82229444, 0x82229444, 0x82229444, 0x82229444, 0x82229408, ] [[switch]] base = 0x82229754 r = 11 default = 0x8222988C labels = [ 0x82229804, 0x82229848, 0x8222977C, 0x822297C0, ] [[switch]] base = 0x82229C5C r = 11 default = 0x82229DF4 labels = [ 0x82229D7C, 0x82229DB8, 0x82229D04, 0x82229D40, 0x82229C8C, 0x82229CC8, ] [[switch]] base = 0x8222A118 r = 11 default = 0x8222A508 labels = [ 0x8222A3B4, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A44C, 0x8222A508, 0x8222A508, 0x8222A508, 0x8222A498, 0x8222A4E4, ] [[switch]] base = 0x8222A798 r = 11 default = 0x8222A7F0 labels = [ 0x8222A7C4, 0x8222A7C4, 0x8222A7C4, 0x8222A7CC, 0x8222A7CC, ] [[switch]] base = 0x8222A7FC r = 11 default = 0x8222A89C labels = [ 0x8222A860, 0x8222A860, 0x8222A89C, 0x8222A89C, 0x8222A89C, 0x8222A89C, 0x8222A89C, 0x8222A89C, 0x8222A89C, 0x8222A89C, 0x8222A868, 0x8222A870, 0x8222A89C, 0x8222A878, 0x8222A89C, 0x8222A868, 0x8222A89C, 0x8222A870, 0x8222A878, ] [[switch]] base = 0x8222A8AC r = 11 default = 0x8222A978 labels = [ 0x8222A930, 0x8222A930, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A978, 0x8222A930, 0x8222A930, 0x8222A930, 0x8222A930, 0x8222A930, 0x8222A930, 0x8222A930, ] [[switch]] base = 0x82234F80 r = 4 default = 0x822352F0 labels = [ 0x82234FB0, 0x82235068, 0x82235228, 0x822351B0, 0x822352A0, 0x822352CC, ] [[switch]] base = 0x822387E0 r = 4 default = 0x82238B7C labels = [ 0x82238814, 0x822388B4, 0x82238A58, 0x822389E4, 0x82238B1C, 0x82238B30, 0x82238B44, ] [[switch]] base = 0x8223DD44 r = 11 default = 0x8223DEF4 labels = [ 0x8223DD88, 0x8223DDA8, 0x8223DDC8, 0x8223DDE8, 0x8223DE08, 0x8223DE28, 0x8223DE4C, 0x8223DE6C, 0x8223DE8C, 0x8223DEB0, 0x8223DED0, ] [[switch]] base = 0x8223DF0C r = 11 default = 0x0 labels = [ 0x8223DF50, 0x8223DF64, 0x8223DF78, 0x8223DF8C, 0x8223DFA0, 0x8223DFB4, 0x8223DFC8, 0x8223DFDC, 0x8223DFF0, 0x8223E004, 0x8223E030, ] [[switch]] base = 0x8223EFF4 r = 11 default = 0x8223F12C labels = [ 0x8223F02C, 0x8223F04C, 0x8223F06C, 0x8223F08C, 0x8223F0AC, 0x8223F0CC, 0x8223F0EC, 0x8223F10C, ] [[switch]] base = 0x8223F144 r = 11 default = 0x0 labels = [ 0x8223F17C, 0x8223F184, 0x8223F1A4, 0x8223F1AC, 0x8223F1B4, 0x8223F1BC, 0x8223F1C4, 0x8223F1E4, ] [[switch]] base = 0x82265C2C r = 11 default = 0x82265FD0 labels = [ 0x82265F44, 0x82265FD0, 0x82265F5C, 0x82265D60, 0x82265FD0, 0x82265FD0, 0x82265EAC, 0x82265FD0, 0x82265F2C, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265F14, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265F74, 0x82265FD0, 0x82265FD0, 0x82265FA4, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265FD0, 0x82265F8C, ] [[switch]] base = 0x82265D70 r = 11 default = 0x82265E94 labels = [ 0x82265DEC, 0x82265E4C, 0x82265E04, 0x82265DD4, 0x82265DBC, 0x82265E94, 0x82265E1C, 0x82265E34, 0x82265E7C, 0x82265E94, 0x82265E94, 0x82265E94, 0x82265E64, ] [[switch]] base = 0x822662A0 r = 11 default = 0x822663E0 labels = [ 0x822663C8, 0x82266320, 0x82266380, 0x82266338, 0x82266308, 0x822662F0, 0x822663E0, 0x82266350, 0x82266368, 0x822663B0, 0x822663E0, 0x822663E0, 0x822663E0, 0x82266398, ] [[switch]] base = 0x82266430 r = 11 default = 0x82266530 labels = [ 0x822664A4, 0x822664F4, 0x822664B8, 0x82266490, 0x8226647C, 0x82266530, 0x822664CC, 0x822664E0, 0x8226651C, 0x82266530, 0x82266530, 0x82266530, 0x82266508, ] [[switch]] base = 0x82266990 r = 11 default = 0x82266C90 labels = [ 0x82266BC8, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C90, 0x82266C04, 0x82266C4C, 0x82266C90, 0x82266B60, ] [[switch]] base = 0x82267054 r = 11 default = 0x82267300 labels = [ 0x82267298, 0x82267300, 0x82267300, 0x82267298, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x82267300, 0x822672B0, 0x82267300, 0x822672E0, 0x82267300, 0x822672C8, ] [[switch]] base = 0x822675DC r = 11 default = 0x822677D4 labels = [ 0x8226776C, 0x822677D4, 0x822677D4, 0x8226776C, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x8226779C, 0x82267784, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677D4, 0x822677B4, ] [[switch]] base = 0x82267C44 r = 11 default = 0x82267E3C labels = [ 0x82267DEC, 0x82267E3C, 0x82267D8C, 0x82267E3C, 0x82267E3C, 0x82267E04, 0x82267D8C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267DC8, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E3C, 0x82267E1C, ] [[switch]] base = 0x822681A0 r = 11 default = 0x8226838C labels = [ 0x822681E0, 0x8226838C, 0x82268204, 0x82268240, 0x8226827C, 0x82268330, 0x82268348, 0x822682B8, 0x8226838C, 0x822682F4, ] [[switch]] base = 0x82268684 r = 11 default = 0x82268A4C labels = [ 0x82268A08, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A08, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A08, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A4C, 0x82268A08, 0x82268A08, ] [[switch]] base = 0x82268C58 r = 11 default = 0x82268E10 labels = [ 0x82268D0C, 0x82268E10, 0x82268E10, 0x82268D0C, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268E10, 0x82268D0C, 0x82268D48, 0x82268D90, 0x82268DCC, 0x82268E10, 0x82268CE8, ] [[switch]] base = 0x822691BC r = 11 default = 0x82269564 labels = [ 0x8226937C, 0x822694F0, 0x82269564, 0x822694D8, 0x822694D8, 0x822694D8, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269544, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x8226952C, 0x82269564, 0x822693D0, 0x82269564, 0x82269364, 0x82269564, 0x822693B8, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269564, 0x82269460, 0x82269460, 0x82269460, 0x82269460, 0x8226949C, 0x822693E8, 0x822693E8, 0x82269424, ] [[switch]] base = 0x82269768 r = 11 default = 0x82269810 labels = [ 0x8226979C, 0x82269810, 0x8226979C, 0x82269810, 0x82269810, 0x822697D8, 0x822697F0, ] [[switch]] base = 0x82269818 r = 29 default = 0x82269888 labels = [ 0x82269868, 0x82269870, 0x82269870, 0x82269878, 0x82269880, 0x82269880, 0x82269880, 0x82269880, 0x82269888, 0x82269888, 0x82269888, 0x82269888, 0x82269888, 0x82269868, ] [[switch]] base = 0x822698A4 r = 11 default = 0x82269984 labels = [ 0x822698D0, 0x822698EC, 0x82269910, 0x82269934, 0x82269958, ] [[switch]] base = 0x82269DD0 r = 11 default = 0x8226A194 labels = [ 0x8226A048, 0x8226A00C, 0x8226A194, 0x8226A108, 0x8226A108, 0x8226A108, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A084, 0x8226A194, 0x8226A0CC, 0x8226A0F0, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A120, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A194, 0x8226A144, 0x8226A168, ] [[switch]] base = 0x8226A614 r = 11 default = 0x8226AAB4 labels = [ 0x8226AAA8, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226A9D8, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226A9A0, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226AAB4, 0x8226A958, 0x8226A97C, ] [[switch]] base = 0x8226A9EC r = 11 default = 0x8226AAB4 labels = [ 0x8226AA18, 0x8226AA18, 0x8226AA3C, 0x8226AA60, 0x8226AA84, ] [[switch]] base = 0x8226AD3C r = 11 default = 0x8226B1EC labels = [ 0x8226B1E0, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B110, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B0D8, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B1EC, 0x8226B090, 0x8226B1EC, 0x8226B0B4, ] [[switch]] base = 0x8226B124 r = 11 default = 0x8226B1EC labels = [ 0x8226B150, 0x8226B150, 0x8226B174, 0x8226B198, 0x8226B1BC, ] [[switch]] base = 0x8226B358 r = 11 default = 0x8226B864 labels = [ 0x8226B858, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B778, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B73C, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B864, 0x8226B68C, 0x8226B6E4, ] [[switch]] base = 0x8226B78C r = 11 default = 0x8226B864 labels = [ 0x8226B7B8, 0x8226B7B8, 0x8226B7E0, 0x8226B808, 0x8226B830, ] [[switch]] base = 0x8226BB48 r = 11 default = 0x8226BF44 labels = [ 0x8226BE58, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BF44, 0x8226BE7C, 0x8226BEC4, 0x8226BF00, 0x8226BE04, 0x8226BF44, 0x8226BE1C, ] [[switch]] base = 0x8226C264 r = 11 default = 0x8226C2EC labels = [ 0x8226C290, 0x8226C2B4, 0x8226C290, 0x8226C2CC, 0x8226C2CC, ] [[switch]] base = 0x8226C2F8 r = 11 default = 0x8226C4B4 labels = [ 0x8226C464, 0x8226C4B4, 0x8226C4B4, 0x8226C47C, 0x8226C494, 0x8226C4B4, 0x8226C4B4, 0x8226C3A4, 0x8226C3A4, 0x8226C428, 0x8226C4B4, 0x8226C4B4, 0x8226C3A4, 0x8226C4B4, 0x8226C4B4, 0x8226C3A4, 0x8226C428, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C4B4, 0x8226C3A4, 0x8226C3A4, 0x8226C3EC, 0x8226C3EC, 0x8226C3EC, 0x8226C3EC, ] [[switch]] base = 0x8227E66C r = 3 default = 0x8227E804 labels = [ 0x8227E6BC, 0x8227E6D8, 0x8227E6F4, 0x8227E710, 0x8227E6D8, 0x8227E72C, 0x8227E748, 0x8227E764, 0x8227E780, 0x8227E79C, 0x8227E7B8, 0x8227E7D4, 0x8227E804, 0x8227E7F0, ] [[switch]] base = 0x8227EFE8 r = 11 default = 0x8227F184 labels = [ 0x8227F168, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F184, 0x8227F114, 0x8227F130, 0x8227F14C, ] [[switch]] base = 0x822862F8 r = 11 default = 0x82286494 labels = [ 0x82286478, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286494, 0x82286424, 0x82286440, 0x8228645C, ] [[switch]] base = 0x82287C50 r = 10 default = 0x82287D30 labels = [ 0x82287CBC, 0x82287CAC, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287CF4, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287D30, 0x82287CCC, ] [[switch]] base = 0x822898B0 r = 11 default = 0x82289934 labels = [ 0x822898F8, 0x822898E4, 0x82289934, 0x82289934, 0x82289934, 0x8228990C, 0x82289920, ] [[switch]] base = 0x8228CB08 r = 11 default = 0x8228CB58 labels = [ 0x8228CB34, 0x8228CB3C, 0x8228CB3C, 0x8228CB44, 0x8228CB4C, ] [[switch]] base = 0x82292E80 r = 11 default = 0x82292F80 labels = [ 0x82292F68, 0x82292F80, 0x82292F80, 0x82292F80, 0x82292F80, 0x82292F80, 0x82292F80, 0x82292ED0, 0x82292F3C, 0x82292F80, 0x82292EE4, 0x82292F24, 0x82292F80, 0x82292F54, ] [[switch]] base = 0x822930F0 r = 11 default = 0x822931A0 labels = [ 0x8229311C, 0x82293188, 0x822931A0, 0x82293130, 0x82293170, ] [[switch]] base = 0x822A5CF4 r = 11 default = 0x822A5F24 labels = [ 0x822A5D1C, 0x822A5D80, 0x822A5DE4, 0x822A5E48, ] [[switch]] base = 0x822A6228 r = 10 default = 0x822A74F8 labels = [ 0x822A6250, 0x822A6744, 0x822A6C38, 0x822A7178, ] [[switch]] base = 0x822A753C r = 11 default = 0x822A783C labels = [ 0x822A7564, 0x822A7654, 0x822A7704, 0x822A7794, ] [[switch]] base = 0x822A7570 r = 11 default = 0x822A783C labels = [ 0x822A75AC, 0x822A75AC, 0x822A75AC, 0x822A75F8, 0x822A783C, 0x822A783C, 0x822A783C, 0x822A7824, 0x822A7644, ] [[switch]] base = 0x822A7660 r = 11 default = 0x822A783C labels = [ 0x822A76AC, 0x822A76AC, 0x822A76AC, 0x822A76D0, 0x822A783C, 0x822A783C, 0x822A783C, 0x822A7824, 0x822A7644, 0x822A783C, 0x822A783C, 0x822A783C, 0x822A76F4, ] [[switch]] base = 0x822A7710 r = 11 default = 0x822A783C labels = [ 0x822A76AC, 0x822A76AC, 0x822A76AC, 0x822A7784, 0x822A783C, 0x822A783C, 0x822A783C, 0x822A7824, 0x822A7644, 0x822A783C, 0x822A783C, 0x822A783C, 0x822A783C, 0x822A7774, 0x822A7764, ] [[switch]] base = 0x822A77A0 r = 11 default = 0x822A783C labels = [ 0x822A77DC, 0x822A783C, 0x822A783C, 0x822A783C, 0x822A7800, 0x822A783C, 0x822A783C, 0x822A7824, 0x822A7644, ] [[switch]] base = 0x822A8720 r = 10 default = 0x822AB160 labels = [ 0x822A8748, 0x822A9214, 0x822A9DC0, 0x822AA828, ] [[switch]] base = 0x822A8768 r = 11 default = 0x822A91F8 labels = [ 0x822A87AC, 0x822A87AC, 0x822A87AC, 0x822A87AC, 0x822A8BC8, 0x822A8CC4, 0x822A91F8, 0x822A8E8C, 0x822A8FC4, 0x822A91F8, 0x822A90FC, ] [[switch]] base = 0x822A9234 r = 11 default = 0x822A91F8 labels = [ 0x822A9280, 0x822A9280, 0x822A9280, 0x822A94A8, 0x822A9688, 0x822A9784, 0x822A91F8, 0x822A994C, 0x822A9A84, 0x822A91F8, 0x822A9BBC, 0x822A91F8, 0x822A9CB8, ] [[switch]] base = 0x822A9DE0 r = 11 default = 0x822A91F8 labels = [ 0x822A9F3C, 0x822A9F3C, 0x822A9F3C, 0x822AA1B0, 0x822AA2B8, 0x822A91F8, 0x822A91F8, 0x822AA3B4, 0x822AA4EC, 0x822A91F8, 0x822AA72C, 0x822A91F8, 0x822A91F8, 0x822AA624, 0x822A9E34, ] [[switch]] base = 0x822AA848 r = 11 default = 0x822A91F8 labels = [ 0x822AA884, 0x822A91F8, 0x822A91F8, 0x822A91F8, 0x822AAAB0, 0x822AACDC, 0x822A91F8, 0x822AAEF0, 0x822AB028, ] [[switch]] base = 0x822AE744 r = 11 default = 0x822AE81C labels = [ 0x822AE774, 0x822AE790, 0x822AE7AC, 0x822AE5D0, 0x822AE800, 0x822AE7C8, ] [[switch]] base = 0x822B27B4 r = 11 default = 0x822B28AC labels = [ 0x822B27E8, 0x822B2804, 0x822B2820, 0x822B283C, 0x822B2640, 0x822B2890, 0x822B2858, ] [[switch]] base = 0x822B6DA0 r = 11 default = 0x822B6E84 labels = [ 0x822B6DD0, 0x822B6DEC, 0x822B6E08, 0x822B6E84, 0x822B6E5C, 0x822B6E24, ] [[switch]] base = 0x822BC034 r = 11 default = 0x822BC10C labels = [ 0x822BC064, 0x822BC080, 0x822BC09C, 0x822BBEC0, 0x822BC0F0, 0x822BC0B8, ] [[switch]] base = 0x822C27E8 r = 11 default = 0x822C30AC labels = [ 0x822C2814, 0x822C28AC, 0x822C30AC, 0x822C29D8, 0x822C2EC4, ] [[switch]] base = 0x822C3668 r = 11 default = 0x822C36D4 labels = [ 0x822C3690, 0x822C36A0, 0x822C36B0, 0x822C36C0, ] [[switch]] base = 0x822C8D44 r = 11 default = 0x822C8E84 labels = [ 0x822C8D6C, 0x822C8DBC, 0x822C8DD8, 0x822C8E7C, ] [[switch]] base = 0x822CD01C r = 11 default = 0x822CD274 labels = [ 0x822CD048, 0x822CD07C, 0x822CD0B0, 0x822CD134, 0x822CD1F0, ] [[switch]] base = 0x822D6220 r = 10 default = 0x822D6268 labels = [ 0x822D6268, 0x822D6248, 0x822D6254, 0x822D6260, ] [[switch]] base = 0x822D6488 r = 11 default = 0x822D6664 labels = [ 0x822D64B0, 0x822D6550, 0x822D64B0, 0x822D65F0, ] [[switch]] base = 0x822D70BC r = 11 default = 0x822D7140 labels = [ 0x822D70E4, 0x822D710C, 0x822D70E4, 0x822D710C, ] [[switch]] base = 0x822D7644 r = 11 default = 0x822D7728 labels = [ 0x822D766C, 0x822D76A8, 0x822D766C, 0x822D76E4, ] [[switch]] base = 0x822D800C r = 11 default = 0x822D84E4 labels = [ 0x822D8034, 0x822D8130, 0x822D82AC, 0x822D840C, ] [[switch]] base = 0x822DAB10 r = 11 default = 0x822DAE58 labels = [ 0x822DAB38, 0x822DAE58, 0x822DAE58, 0x822DAE50, ] [[switch]] base = 0x822DABD8 r = 11 default = 0x822DAE58 labels = [ 0x822DAE58, 0x822DAC00, 0x822DAC08, 0x822DAE58, ] [[switch]] base = 0x822DAC98 r = 11 default = 0x822DAE58 labels = [ 0x822DACC0, 0x822DAE58, 0x822DAE58, 0x822DAE50, ] [[switch]] base = 0x822DADA4 r = 11 default = 0x822DAE58 labels = [ 0x822DADCC, 0x822DAE58, 0x822DAE58, 0x822DAE50, ] [[switch]] base = 0x822DAFE0 r = 11 default = 0x822DB024 labels = [ 0x822DB024, 0x822DB008, 0x822DB024, 0x822DB024, ] [[switch]] base = 0x822DD5E0 r = 11 default = 0x822DD6B4 labels = [ 0x822DD64C, 0x822DD6B4, 0x822DD6B4, 0x822DD6B4, 0x822DD6B4, 0x822DD6B4, 0x822DD6B4, 0x822DD6B4, 0x822DD6A4, 0x822DD6B4, 0x822DD6B4, 0x822DD62C, 0x822DD638, ] [[switch]] base = 0x822E24C8 r = 11 default = 0x822E2640 labels = [ 0x822E24F4, 0x822E2530, 0x822E257C, 0x822E25C0, 0x822E25FC, ] [[switch]] base = 0x822FB668 r = 11 default = 0x822FB724 labels = [ 0x822FB690, 0x822FB6AC, 0x822FB6C8, 0x822FB714, ] [[switch]] base = 0x82300174 r = 11 default = 0x8230023C labels = [ 0x823001B0, 0x823001C0, 0x823001D0, 0x823001E0, 0x823001F0, 0x82300200, 0x82300210, 0x82300220, 0x82300230, ] [[switch]] base = 0x8230F0B4 r = 11 default = 0x8230F338 labels = [ 0x8230F0DC, 0x8230F0F0, 0x8230F1C4, 0x8230F298, ] [[switch]] base = 0x82315E2C r = 11 default = 0x82316304 labels = [ 0x82315E58, 0x82315EA4, 0x8231606C, 0x823160F0, 0x82316210, ] [[switch]] base = 0x82317DA4 r = 10 default = 0x82317DE8 labels = [ 0x82317DCC, 0x82317DD4, 0x82317DDC, 0x82317DE4, ] [[switch]] base = 0x8231846C r = 11 default = 0x8231853C labels = [ 0x82318494, 0x823184BC, 0x823184E4, 0x8231850C, ] [[switch]] base = 0x8232685C r = 11 default = 0x823269F4 labels = [ 0x82326884, 0x82326884, 0x82326898, 0x823269D0, ] [[switch]] base = 0x8232893C r = 11 default = 0x82328BB8 labels = [ 0x82328A98, 0x82328B78, 0x82328964, 0x82328A44, ] [[switch]] base = 0x82328C10 r = 11 default = 0x82328E0C labels = [ 0x82328D28, 0x82328D48, 0x82328C38, 0x82328C58, ] [[switch]] base = 0x82328CC8 r = 10 default = 0x82328E0C labels = [ 0x82328E0C, 0x82328E0C, 0x82328E0C, 0x82328CF0, ] [[switch]] base = 0x82328DB8 r = 10 default = 0x82328E0C labels = [ 0x82328E0C, 0x82328DE0, 0x82328E0C, 0x82328E0C, ] [[switch]] base = 0x82333F98 r = 10 default = 0x82334060 labels = [ 0x82333FFC, 0x82334060, 0x82334028, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334060, 0x82334038, 0x82334050, ] [[switch]] base = 0x82336A80 r = 11 default = 0x82336C24 labels = [ 0x82336B64, 0x82336AE8, 0x82336B64, 0x82336C24, 0x82336AAC, ] [[switch]] base = 0x82336C98 r = 11 default = 0x82337338 labels = [ 0x82336CD0, 0x82336D68, 0x82336D50, 0x8233715C, 0x823370F0, 0x82337264, 0x82337304, 0x82336FD0, ] [[switch]] base = 0x8233BA38 r = 11 default = 0x8233BBD4 labels = [ 0x8233BA60, 0x8233BABC, 0x8233BAE4, 0x8233BB0C, ] [[switch]] base = 0x8233C0B0 r = 11 default = 0x8233C6D8 labels = [ 0x8233C0D8, 0x8233C0EC, 0x8233C134, 0x8233C35C, ] [[switch]] base = 0x8234DE14 r = 11 default = 0x8234DFF0 labels = [ 0x8234DE68, 0x8234DF00, 0x8234DF70, 0x8234DFF0, 0x8234DFF0, 0x8234DFF0, 0x8234DF90, 0x8234DF78, 0x8234DEB4, 0x8234DF9C, 0x8234DF1C, 0x8234DF38, 0x8234DF54, 0x8234DFC8, 0x8234DFE4, ] [[switch]] base = 0x82350448 r = 10 default = 0x82350BB0 labels = [ 0x82350470, 0x823507BC, 0x82350960, 0x82350A88, ] [[switch]] base = 0x82351450 r = 11 default = 0x82351664 labels = [ 0x82351494, 0x823514B4, 0x823514D4, 0x82351664, 0x82351664, 0x823516A4, 0x82351664, 0x82351664, 0x82351664, 0x82351664, 0x823514F4, ] [[switch]] base = 0x8235E42C r = 11 default = 0x8235E680 labels = [ 0x8235E458, 0x8235E4D8, 0x8235E5A4, 0x8235E5B0, 0x8235E5C8, ] [[switch]] base = 0x823622FC r = 11 default = 0x82362444 labels = [ 0x82362324, 0x82362444, 0x82362444, 0x82362394, ] [[switch]] base = 0x823623B0 r = 11 default = 0x82362444 labels = [ 0x82362444, 0x823623D8, 0x823623E4, 0x82362444, ] [[switch]] base = 0x82362584 r = 11 default = 0x823625C8 labels = [ 0x823625C8, 0x823625AC, 0x823625C8, 0x823625C8, ] [[switch]] base = 0x82371AEC r = 11 default = 0x82371B64 labels = [ 0x82371B18, 0x82371B28, 0x82371B38, 0x82371B48, 0x82371B58, ] [[switch]] base = 0x823742B8 r = 28 default = 0x82374390 labels = [ 0x82374390, 0x823742E0, 0x823742E8, 0x823742F0, ] [[switch]] base = 0x8237530C r = 11 default = 0x823753DC labels = [ 0x823753DC, 0x82375334, 0x8237533C, 0x82375344, ] [[switch]] base = 0x82379A58 r = 11 default = 0x82379C80 labels = [ 0x82379A80, 0x82379AD4, 0x82379B9C, 0x82379BDC, ] [[switch]] base = 0x82382D2C r = 11 default = 0x82383080 labels = [ 0x82382D54, 0x82382DE8, 0x82382E68, 0x82382FDC, ] [[switch]] base = 0x8238361C r = 11 default = 0x823836D4 labels = [ 0x82383AB8, 0x82383648, 0x823836F0, 0x82383AD0, 0x82383B40, ] [[switch]] base = 0x823870D8 r = 11 default = 0x82387864 labels = [ 0x82387100, 0x82387268, 0x82387358, 0x82387780, ] [[switch]] base = 0x8238969C r = 11 default = 0x8238A08C labels = [ 0x82389FE4, 0x823896C4, 0x8238985C, 0x82389EA8, ] [[switch]] base = 0x8238C9D0 r = 11 default = 0x8238CAFC labels = [ 0x8238C9F8, 0x8238CA1C, 0x8238CA68, 0x8238CAB0, ] [[switch]] base = 0x8238CB60 r = 31 default = 0x8238D324 labels = [ 0x8238CBA0, 0x8238CD08, 0x8238CE70, 0x8238CFA8, 0x8238CFD8, 0x8238CFD8, 0x8238CFE8, 0x8238D120, 0x8238D258, 0x8238D2A4, ] [[switch]] base = 0x8238D608 r = 11 default = 0x8238D778 labels = [ 0x8238D640, 0x8238D640, 0x8238D6B8, 0x8238D6E4, 0x8238D6FC, 0x8238D73C, 0x8238D6B8, 0x8238D6B8, ] [[switch]] base = 0x8238EAB0 r = 11 default = 0x8238EB44 labels = [ 0x8238EAF4, 0x8238EB08, 0x8238EB44, 0x8238EB44, 0x8238EB44, 0x8238EB30, 0x8238EB44, 0x8238EB44, 0x8238EB44, 0x8238EB44, 0x8238EB1C, ] [[switch]] base = 0x82394EC8 r = 11 default = 0x82394F64 labels = [ 0x82394EF8, 0x82394F5C, 0x82394F64, 0x82394F00, 0x82394F08, 0x82394F5C, ] [[switch]] base = 0x82394FB4 r = 11 default = 0x82395310 labels = [ 0x82394FE0, 0x8239510C, 0x82395310, 0x8239522C, 0x823952E8, ] [[switch]] base = 0x8239DC84 r = 11 default = 0x8239DF50 labels = [ 0x8239DDA8, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DDB8, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DDC8, 0x8239DF50, 0x8239DDD8, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DF50, 0x8239DDE8, ] [[switch]] base = 0x823A1708 r = 11 default = 0x823A1928 labels = [ 0x823A1738, 0x823A178C, 0x823A17D8, 0x823A1824, 0x823A1880, 0x823A18DC, ] [[switch]] base = 0x823A3268 r = 11 default = 0x823A32CC labels = [ 0x823A3290, 0x823A3330, 0x823A32A0, 0x823A32A0, ] [[switch]] base = 0x823A3368 r = 11 default = 0x823A33CC labels = [ 0x823A3390, 0x823A33A0, 0x823A3434, 0x823A3434, ] [[switch]] base = 0x823A3480 r = 11 default = 0x823A34E4 labels = [ 0x823A34A8, 0x823A34B8, 0x823A354C, 0x823A354C, ] [[switch]] base = 0x823A4C04 r = 11 default = 0x823A4CCC labels = [ 0x823A4C34, 0x823A4D04, 0x823A4DB0, 0x823A4E18, 0x823A4F00, 0x823A4F88, ] [[switch]] base = 0x823A5BA0 r = 11 default = 0x823A604C labels = [ 0x823A5BD4, 0x823A5C50, 0x823A5D1C, 0x823A5DD8, 0x823A5E94, 0x823A5FD4, 0x823A6044, ] [[switch]] base = 0x823A9338 r = 11 default = 0x823A98FC labels = [ 0x823A9364, 0x823A93A8, 0x823A945C, 0x823A952C, 0x823A95F8, ] [[switch]] base = 0x823A98A0 r = 11 default = 0x823A98FC labels = [ 0x823A98D8, 0x823A98FC, 0x823A98D8, 0x823A98D8, 0x823A98D8, 0x823A98D8, 0x823A98FC, 0x823A98D8, ] [[switch]] base = 0x823AA500 r = 11 default = 0x823AA82C labels = [ 0x823AA52C, 0x823AA6E8, 0x823AA7A0, 0x823AA7B4, 0x823AA7E4, ] [[switch]] base = 0x823AAB64 r = 11 default = 0x823AAE7C labels = [ 0x823AAB90, 0x823AAC70, 0x823AAD68, 0x823AAE28, 0x823AAE50, ] [[switch]] base = 0x823AB9A8 r = 11 default = 0x823ABB68 labels = [ 0x823AB9D0, 0x823ABA7C, 0x823ABAD4, 0x823ABB3C, ] [[switch]] base = 0x823AC1CC r = 11 default = 0x823AC4B8 labels = [ 0x823AC1F8, 0x823AC4B8, 0x823AC290, 0x823AC43C, 0x823AC48C, ] [[switch]] base = 0x823ACFDC r = 11 default = 0x823AD2D8 labels = [ 0x823AD004, 0x823AD09C, 0x823AD280, 0x823AD2D0, ] [[switch]] base = 0x823ADE58 r = 11 default = 0x823AE100 labels = [ 0x823ADE80, 0x823ADEDC, 0x823ADF1C, 0x823AE0BC, ] [[switch]] base = 0x823AFFFC r = 11 default = 0x823B05C4 labels = [ 0x823B0030, 0x823B02D8, 0x823B0080, 0x823B0194, 0x823B0370, 0x823B0530, 0x823B05BC, ] [[switch]] base = 0x823B0C78 r = 11 default = 0x823B1214 labels = [ 0x823B0CA8, 0x823B0CAC, 0x823B0DA4, 0x823B1074, 0x823B0E40, 0x823B0EB8, ] [[switch]] base = 0x823B1634 r = 11 default = 0x823B1BC8 labels = [ 0x823B1668, 0x823B1734, 0x823B1848, 0x823B1990, 0x823B1A4C, 0x823B1938, 0x823B17E4, ] [[switch]] base = 0x823B3350 r = 11 default = 0x823B3398 labels = [ 0x823B3378, 0x823B33D8, 0x823B3418, 0x823B3438, ] [[switch]] base = 0x823B4AB0 r = 11 default = 0x823B4CD0 labels = [ 0x823B4AD8, 0x823B4AF4, 0x823B4BD0, 0x823B4CC8, ] [[switch]] base = 0x823B4D9C r = 11 default = 0x823B4E58 labels = [ 0x823B4DC4, 0x823B4DE4, 0x823B4E1C, 0x823B4E3C, ] [[switch]] base = 0x823B6044 r = 5 default = 0x823B6194 labels = [ 0x823B6070, 0x823B6128, 0x823B6134, 0x823B6140, 0x823B614C, ] [[switch]] base = 0x823B6B88 r = 11 default = 0x823B6DD8 labels = [ 0x823B6BB0, 0x823B6BEC, 0x823B6C18, 0x823B6D54, ] [[switch]] base = 0x823C15D8 r = 11 default = 0x823C1814 labels = [ 0x823C1608, 0x823C1668, 0x823C16C8, 0x823C171C, 0x823C17D4, 0x823C1814, ] [[switch]] base = 0x823C316C r = 11 default = 0x823C31A4 labels = [ 0x823C3194, 0x823C31B4, 0x823C31D4, 0x823C31F4, ] [[switch]] base = 0x823C3D60 r = 11 default = 0x823C3FF4 labels = [ 0x823C3D90, 0x823C3DD8, 0x823C3E30, 0x823C3F90, 0x823C3FA4, 0x823C3FD4, ] [[switch]] base = 0x823C482C r = 11 default = 0x823C498C labels = [ 0x823C4854, 0x823C4890, 0x823C48B0, 0x823C4928, ] [[switch]] base = 0x823C4E6C r = 11 default = 0x823C55F4 labels = [ 0x823C4EA0, 0x823C4F50, 0x823C4F64, 0x823C5094, 0x823C52E0, 0x823C5558, 0x823C55D4, ] [[switch]] base = 0x823C772C r = 11 default = 0x823C7F2C labels = [ 0x823C7764, 0x823C77DC, 0x823C795C, 0x823C7BE4, 0x823C7C80, 0x823C7D94, 0x823C7DE8, 0x823C7EA8, ] [[switch]] base = 0x823C8CC0 r = 11 default = 0x823C8E34 labels = [ 0x823C8CE8, 0x823C8D30, 0x823C8E2C, 0x823C8DA4, ] [[switch]] base = 0x823CAD08 r = 11 default = 0x823CAE04 labels = [ 0x823CAD30, 0x823CADA0, 0x823CADBC, 0x823CADEC, ] [[switch]] base = 0x823D2FF8 r = 11 default = 0x823D3234 labels = [ 0x823D3024, 0x823D3204, 0x823D3210, 0x823D321C, 0x823D3228, ] [[switch]] base = 0x823D33D4 r = 11 default = 0x823D3234 labels = [ 0x823D3408, 0x823D3400, 0x823D3408, 0x823D3408, 0x823D3408, ] [[switch]] base = 0x823D4774 r = 11 default = 0x823D499C labels = [ 0x823D47A4, 0x823D47F8, 0x823D484C, 0x823D48A0, 0x823D4948, 0x823D48F4, ] [[switch]] base = 0x823D49B4 r = 11 default = 0x823D4B5C labels = [ 0x823D49E4, 0x823D4A28, 0x823D4A6C, 0x823D4AB0, 0x823D4B30, 0x823D4AF4, ] [[switch]] base = 0x823DC7A8 r = 11 default = 0x823DC994 labels = [ 0x823DC7D4, 0x823DC994, 0x823DC7EC, 0x823DC81C, 0x823DC804, ] [[switch]] base = 0x823DC85C r = 11 default = 0x823DC994 labels = [ 0x823DC8D4, 0x823DC8EC, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC91C, 0x823DC994, 0x823DC994, 0x823DC744, 0x823DC994, 0x823DC904, 0x823DC97C, 0x823DC934, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC994, 0x823DC94C, 0x823DC964, ] [[switch]] base = 0x823DE214 r = 11 default = 0x823DE45C labels = [ 0x823DE23C, 0x823DE258, 0x823DE274, 0x823DE294, ] [[switch]] base = 0x823DE2DC r = 11 default = 0x823DE45C labels = [ 0x823DE354, 0x823DE45C, 0x823DE370, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE424, 0x823DE45C, 0x823DE440, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE45C, 0x823DE38C, 0x823DE3A8, 0x823DE3C4, 0x823DE3E4, 0x823DE400, ] [[switch]] base = 0x823E02E0 r = 11 default = 0x823E0568 labels = [ 0x823E030C, 0x823E0568, 0x823E032C, 0x823E036C, 0x823E034C, ] [[switch]] base = 0x823E03BC r = 11 default = 0x823E0568 labels = [ 0x823E04A8, 0x823E046C, 0x823E0568, 0x823E0568, 0x823E04C8, 0x823E0568, 0x823E0298, 0x823E0298, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E04E8, 0x823E0548, 0x823E0528, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0568, 0x823E0508, ] [[switch]] base = 0x823E1484 r = 11 default = 0x823E15E4 labels = [ 0x823E1528, 0x823E15E4, 0x823E14E8, 0x823E1508, 0x823E15E4, 0x823E15E4, 0x823E15E4, 0x823E15A4, 0x823E15E4, 0x823E15E4, 0x823E15C4, 0x823E15E4, 0x823E15E4, 0x823E15E4, 0x823E15E4, 0x823E15E4, 0x823E15E4, 0x823E1564, 0x823E1584, ] [[switch]] base = 0x823E2ED0 r = 11 default = 0x823E30B4 labels = [ 0x823E2F04, 0x823E30B4, 0x823E2F1C, 0x823E30B4, 0x823E2F34, 0x823E30B4, 0x823E2F4C, ] [[switch]] base = 0x823E2F8C r = 11 default = 0x823E30B4 labels = [ 0x823E2FDC, 0x823E30B4, 0x823E2FF4, 0x823E30B4, 0x823E303C, 0x823E306C, 0x823E3054, 0x823E30B4, 0x823E30B4, 0x823E300C, 0x823E3024, 0x823E30B4, 0x823E3084, 0x823E309C, ] [[switch]] base = 0x823E5820 r = 11 default = 0x823E5A98 labels = [ 0x823E5858, 0x823E5A98, 0x823E5870, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5888, 0x823E58A0, ] [[switch]] base = 0x823E58E0 r = 11 default = 0x823E5A98 labels = [ 0x823E59D8, 0x823E5A98, 0x823E59F0, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E57A4, 0x823E5A08, 0x823E5A20, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A38, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A98, 0x823E5A50, 0x823E5A68, 0x823E5A80, ] [[switch]] base = 0x823E7CE8 r = 28 default = 0x823E7F44 labels = [ 0x823E7D18, 0x823E7D60, 0x823E7DA8, 0x823E7E70, 0x823E7EF8, 0x823E7F40, ] [[switch]] base = 0x823EB570 r = 11 default = 0x823EB6AC labels = [ 0x823EB4D4, 0x823EB4D4, 0x823EB4D4, 0x823EB4D4, ] [[switch]] base = 0x823EB5A8 r = 11 default = 0x823EB6AC labels = [ 0x823EB4D4, 0x823EB6AC, 0x823EB4D4, 0x823EB4D4, 0x823EB4D4, 0x823EB6AC, 0x823EB4D4, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB4D4, 0x823EB668, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB6AC, 0x823EB680, 0x823EB650, ] [[switch]] base = 0x823ECD7C r = 11 default = 0x823ECDFC labels = [ 0x823ECDA4, 0x823ECDA4, 0x823ECDDC, 0x823ECDC0, ] [[switch]] base = 0x823EFEBC r = 11 default = 0x823EFF98 labels = [ 0x823EFF04, 0x823EFF24, 0x823EFF98, 0x823EFF40, 0x823EFF98, 0x823EFF98, 0x823EFF98, 0x823EFF98, 0x823EFF7C, 0x823EFF98, 0x823EFDFC, 0x823EFF54, ] [[switch]] base = 0x823F32AC r = 11 default = 0x823F3478 labels = [ 0x823F32E8, 0x823F3478, 0x823F3478, 0x823F3478, 0x823F3478, 0x823F3478, 0x823F32FC, 0x823F3314, 0x823F3324, ] [[switch]] base = 0x823F335C r = 11 default = 0x823F3478 labels = [ 0x823F3414, 0x823F3428, 0x823F3478, 0x823F3478, 0x823F3478, 0x823F33B8, 0x823F33CC, 0x823F33E0, 0x823F3478, 0x823F3478, 0x823F3478, 0x823F3400, 0x823F3450, 0x823F3478, 0x823F343C, 0x823F3478, 0x823F3464, ] [[switch]] base = 0x823F4484 r = 11 default = 0x823F4578 labels = [ 0x823F44D0, 0x823F44E8, 0x823F4500, 0x823F4578, 0x823F4518, 0x823F4578, 0x823F4548, 0x823F4560, 0x823F4578, 0x823F4578, 0x823F4578, 0x823F4578, 0x823F4530, ] [[switch]] base = 0x823F6C40 r = 11 default = 0x823F6DCC labels = [ 0x823F6D3C, 0x823F6D54, 0x823F6DCC, 0x823F6D6C, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6D84, 0x823F6D9C, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DCC, 0x823F6DB4, ] [[switch]] base = 0x823F7D74 r = 11 default = 0x823F7DD0 labels = [ 0x823F7D9C, 0x823F7DA8, 0x823F7DB4, 0x823F7DC0, ] [[switch]] base = 0x823F8C38 r = 11 default = 0x823F8D00 labels = [ 0x823F8BAC, 0x823F8D00, 0x823F8C88, 0x823F8D00, 0x823F8CB8, 0x823F8D00, 0x823F8D00, 0x823F8D00, 0x823F8D00, 0x823F8D00, 0x823F8CA0, 0x823F8D00, 0x823F8CD0, 0x823F8CE8, ] [[switch]] base = 0x823F9FC0 r = 11 default = 0x823FA0F8 labels = [ 0x823FA010, 0x823FA0F8, 0x823FA038, 0x823FA0F8, 0x823FA098, 0x823FA0F8, 0x823FA0F8, 0x823FA0F8, 0x823FA058, 0x823F9E68, 0x823FA078, 0x823FA0F8, 0x823FA0B8, 0x823FA0D8, ] [[switch]] base = 0x823FB48C r = 11 default = 0x823FB680 labels = [ 0x823FB60C, 0x823FB680, 0x823FB620, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB3B4, 0x823FB634, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB680, 0x823FB644, 0x823FB66C, ] [[switch]] base = 0x823FE0EC r = 11 default = 0x823FE42C labels = [ 0x823FE18C, 0x823FE42C, 0x823FE178, 0x823FE42C, 0x823FE1A0, 0x823FE1B4, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE1C8, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE1F0, ] [[switch]] base = 0x823FE228 r = 11 default = 0x823FE42C labels = [ 0x823FE344, 0x823FE42C, 0x823FE42C, 0x823FE36C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE358, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE42C, 0x823FE404, 0x823FE418, 0x823FE3A0, 0x823FE42C, 0x823FE3B4, 0x823FE38C, 0x823FE3C8, 0x823FE3DC, 0x823FE3F0, 0x823FE308, 0x823FE318, ] [[switch]] base = 0x8240083C r = 11 default = 0x82400940 labels = [ 0x824008E0, 0x824008F4, 0x82400908, 0x8240092C, 0x824008CC, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x82400940, 0x824008B4, ] [[switch]] base = 0x8240284C r = 11 default = 0x82402970 labels = [ 0x8240291C, 0x82402970, 0x82402970, 0x824028F4, 0x82402908, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402970, 0x82402948, 0x8240295C, ] [[switch]] base = 0x8240BFFC r = 11 default = 0x8240C02C labels = [ 0x8240C068, 0x8240C068, 0x8240C02C, 0x8240C02C, 0x8240C068, 0x8240C068, ] [[switch]] base = 0x8240CBB8 r = 11 default = 0x8240CCA8 labels = [ 0x8240CCC4, 0x8240CBE8, 0x8240CCA8, 0x8240CCA8, 0x8240CC28, 0x8240CC68, ] [[switch]] base = 0x8240F69C r = 11 default = 0x8240F8AC labels = [ 0x8240F888, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F830, 0x8240F8AC, 0x8240F8AC, 0x8240F844, 0x8240F858, 0x8240F874, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F8AC, 0x8240F89C, ] [[switch]] base = 0x82410C5C r = 11 default = 0x82410CB8 labels = [ 0x82410C9C, 0x82410C9C, 0x82410C90, 0x82410C84, ] [[switch]] base = 0x82412FF0 r = 11 default = 0x824130B4 labels = [ 0x82413018, 0x82413044, 0x82413068, 0x82413094, ] [[switch]] base = 0x824131A8 r = 11 default = 0x824132B8 labels = [ 0x8241312C, 0x824132B8, 0x824132B8, 0x824132B8, 0x82413288, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x824132B8, 0x82413298, 0x824132A8, ] [[switch]] base = 0x82416778 r = 11 default = 0x82416834 labels = [ 0x824167AC, 0x824167BC, 0x824167D0, 0x824167E4, 0x824167F8, 0x82416820, 0x8241680C, ] [[switch]] base = 0x82422BF4 r = 11 default = 0x82422D58 labels = [ 0x82422D38, 0x82422CD8, 0x82422CF8, 0x82422D18, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422D58, 0x82422CB4, ] [[switch]] base = 0x824244A8 r = 11 default = 0x824245E0 labels = [ 0x8242456C, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x82424554, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x824245E0, 0x82424584, 0x8242459C, 0x824245B4, ] [[switch]] base = 0x824256F4 r = 11 default = 0x82425934 labels = [ 0x8242586C, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x824256AC, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x82425934, 0x824258E0, 0x8242588C, ] [[switch]] base = 0x82426B70 r = 11 default = 0x82426C20 labels = [ 0x82426B98, 0x82426BB0, 0x82426BC8, 0x82426BE0, ] [[switch]] base = 0x82428D48 r = 11 default = 0x82428DB8 labels = [ 0x82428D70, 0x82428D84, 0x82428D98, 0x82428DAC, ] [[switch]] base = 0x82434738 r = 11 default = 0x8243488C labels = [ 0x824347AC, 0x824347C8, 0x824347E4, 0x82434800, 0x8243488C, 0x8243481C, 0x8243488C, 0x82434838, 0x8243488C, 0x82434854, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x8243488C, 0x82434870, ] [[switch]] base = 0x82439CC8 r = 11 default = 0x8243A538 labels = [ 0x82439D00, 0x8243A108, 0x8243A530, 0x8243A518, 0x8243A538, 0x82439FCC, 0x82439FF0, 0x8243A530, ] [[switch]] base = 0x82442F38 r = 5 default = 0x824430F0 labels = [ 0x82442F60, 0x82442FB8, 0x82443014, 0x8244306C, ] [[switch]] base = 0x82449CF0 r = 11 default = 0x82449D60 labels = [ 0x82449D4C, 0x82449D4C, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D60, 0x82449D4C, 0x82449D4C, ] [[switch]] base = 0x8244AC4C r = 11 default = 0x8244ACA0 labels = [ 0x8244AC98, 0x8244ACA0, 0x8244ACA0, 0x8244ACA0, 0x8244ACA0, 0x8244ACA0, 0x8244AC98, 0x8244ACA0, 0x8244ACA0, 0x8244AC98, 0x8244ACA0, 0x8244ACA0, 0x8244AC98, ] [[switch]] base = 0x8244B938 r = 4 default = 0x0 labels = [ 0x8244B960, 0x8244B964, 0x8244B968, 0x8244B96C, ] [[switch]] base = 0x8245AF2C r = 11 default = 0x8245AFD8 labels = [ 0x8245AF60, 0x8245AF60, 0x8245AF60, 0x8245AF94, 0x8245AF60, 0x8245AF94, 0x8245AF60, ] [[switch]] base = 0x824A02D8 r = 11 default = 0x824A03B4 labels = [ 0x824A0308, 0x824A03B4, 0x824A0320, 0x824A03B4, 0x824A0354, 0x824A0388, ] [[switch]] base = 0x824A08A0 r = 11 default = 0x824A0998 labels = [ 0x824A08D4, 0x824A08E0, 0x824A08EC, 0x824A090C, 0x824A0934, 0x824A0974, 0x824A0994, ] [[switch]] base = 0x824A6194 r = 10 default = 0x824A6200 labels = [ 0x824A61D0, 0x824A61E0, 0x824A6200, 0x824A6200, 0x824A61F0, 0x824A6200, 0x824A6200, 0x824A6200, 0x824A61E0, ] [[switch]] base = 0x824A6434 r = 11 default = 0x824A6660 labels = [ 0x824A6648, 0x824A6650, 0x824A6660, 0x824A647C, 0x824A6508, 0x824A6640, 0x824A6660, 0x824A6658, 0x824A6660, 0x824A6640, 0x824A6660, 0x824A65F8, ] [[switch]] base = 0x824A6FB0 r = 11 default = 0x824A758C labels = [ 0x824A6FF8, 0x824A7074, 0x824A70F0, 0x824A716C, 0x824A71CC, 0x824A729C, 0x824A72EC, 0x824A7334, 0x824A7468, 0x824A7560, 0x824A74F8, 0x824A7234, ] [[switch]] base = 0x824A9F6C r = 11 default = 0x824AA090 labels = [ 0x824A9FA0, 0x824AA090, 0x824A9FA0, 0x824AA090, 0x824A9FE8, 0x824AA090, 0x824AA03C, ] [[switch]] base = 0x824ABAB0 r = 10 default = 0x824ABAF0 labels = [ 0x824ABAD8, 0x824ABAE0, 0x824ABAE0, 0x824ABAE8, ] [[switch]] base = 0x824AEBA4 r = 10 default = 0x824AEC14 labels = [ 0x824AEBD0, 0x824AEBE0, 0x824AEBF0, 0x824AEBF8, 0x824AEC08, ] [[switch]] base = 0x824B1C48 r = 11 default = 0x824B1EF0 labels = [ 0x824B1D54, 0x824B1D78, 0x824B1D84, 0x824B1D90, 0x824B1E0C, 0x824B1CEC, 0x824B1D1C, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1DA0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1E30, 0x824B1E4C, 0x824B1E60, 0x824B1E74, 0x824B1E88, 0x824B1E9C, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1EB0, 0x824B1EF0, 0x824B1EF0, 0x824B1EF0, 0x824B1DE0, ] [[switch]] base = 0x824B2AEC r = 9 default = 0x824B3080 labels = [ 0x824B2B90, 0x824B3080, 0x824B2B58, 0x824B2B24, 0x824B2B74, 0x824B3080, 0x824B2AB4, 0x824B2AAC, ] [[switch]] base = 0x824B2E44 r = 9 default = 0x824B3080 labels = [ 0x824B2E8C, 0x824B2EA8, 0x824B2ECC, 0x824B2EE8, 0x824B2F6C, 0x824B2F88, 0x824B303C, 0x824B2F14, 0x824B2F30, 0x824B2FF4, 0x824B2FAC, 0x824B2AB4, ] [[switch]] base = 0x824B6328 r = 11 default = 0x824B67EC labels = [ 0x824B636C, 0x824B63D4, 0x824B6438, 0x824B6494, 0x824B6504, 0x824B6568, 0x824B65CC, 0x824B6630, 0x824B6694, 0x824B66F0, 0x824B674C, ] [[switch]] base = 0x824BA110 r = 11 default = 0x824BA1C4 labels = [ 0x824BA1C4, 0x824BA138, 0x824BA174, 0x824BA1A0, ] [[switch]] base = 0x824C281C r = 29 default = 0x824C2860 labels = [ 0x824C2844, 0x824C284C, 0x824C2858, 0x824C2858, ] [[switch]] base = 0x824C44A8 r = 3 default = 0x824C4540 labels = [ 0x824C44D0, 0x824C44EC, 0x824C4508, 0x824C4524, ] [[switch]] base = 0x824C4558 r = 3 default = 0x824C45B0 labels = [ 0x824C4580, 0x824C458C, 0x824C4598, 0x824C45A4, ] [[switch]] base = 0x824C45C0 r = 3 default = 0x824C4600 labels = [ 0x824C45E8, 0x824C45F0, 0x824C45F0, 0x824C45F8, ] [[switch]] base = 0x824C480C r = 4 default = 0x824C4864 labels = [ 0x824C4834, 0x824C4840, 0x824C484C, 0x824C4858, ] [[switch]] base = 0x824C4878 r = 4 default = 0x824C48B8 labels = [ 0x824C48A0, 0x824C48A8, 0x824C48A8, 0x824C48B0, ] [[switch]] base = 0x824C494C r = 4 default = 0x824C4A44 labels = [ 0x824C4974, 0x824C49AC, 0x824C49E4, 0x824C4A1C, ] [[switch]] base = 0x824C4F00 r = 31 default = 0x824C4F84 labels = [ 0x824C4F28, 0x824C4F74, 0x824C4F7C, 0x824C4F34, ] [[switch]] base = 0x824C6DC4 r = 11 default = 0x824C6FF4 labels = [ 0x824C6DEC, 0x824C6E3C, 0x824C6EC0, 0x824C6F54, ] [[switch]] base = 0x824C8AA8 r = 11 default = 0x824C8FE8 labels = [ 0x824C8AD0, 0x824C8B28, 0x824C8E24, 0x824C8FB4, ] [[switch]] base = 0x824C9680 r = 11 default = 0x824C985C labels = [ 0x824C96AC, 0x824C96F4, 0x824C973C, 0x824C976C, 0x824C97F4, ] [[switch]] base = 0x824CC374 r = 11 default = 0x824CC728 labels = [ 0x824CC3B4, 0x824CC3F0, 0x824CC410, 0x824CC474, 0x824CC4BC, 0x824CC5AC, 0x824CC5CC, 0x824CC648, 0x824CC690, 0x824CC6F0, ] [[switch]] base = 0x824D1334 r = 11 default = 0x824D15F4 labels = [ 0x824D1374, 0x824D15F4, 0x824D15F4, 0x824D15F4, 0x824D15F4, 0x824D15F4, 0x824D14B4, 0x824D15CC, 0x824D15F4, 0x824D1670, ] [[switch]] base = 0x824D2DC4 r = 11 default = 0x824D2FE0 labels = [ 0x824D2E00, 0x824D2EA0, 0x824D2EC4, 0x824D2F50, 0x824D2F8C, 0x824D2FA4, 0x824D2FBC, 0x824D2E88, 0x824D2FD4, ] [[switch]] base = 0x824D3320 r = 11 default = 0x824D35E8 labels = [ 0x824D3374, 0x824D33A0, 0x824D35E8, 0x824D33D4, 0x824D3408, 0x824D342C, 0x824D3460, 0x824D3484, 0x824D34B8, 0x824D34DC, 0x824D3510, 0x824D3534, 0x824D3568, 0x824D358C, 0x824D35C0, ] [[switch]] base = 0x824D3B4C r = 4 default = 0x824D423C labels = [ 0x824D3B80, 0x824D3BC8, 0x824D3C10, 0x824D40F0, 0x824D4144, 0x824D4198, 0x824D41EC, ] [[switch]] base = 0x824D5580 r = 3 default = 0x824D5748 labels = [ 0x824D55A8, 0x824D55D8, 0x824D5608, 0x824D5638, ] [[switch]] base = 0x824D5670 r = 3 default = 0x824D5748 labels = [ 0x824D5698, 0x824D56C4, 0x824D56F0, 0x824D571C, ] [[switch]] base = 0x824D5798 r = 3 default = 0x824D5858 labels = [ 0x824D57C0, 0x824D57E4, 0x824D5808, 0x824D582C, ] [[switch]] base = 0x824D68D8 r = 11 default = 0x824D6BD0 labels = [ 0x824D6934, 0x824D6964, 0x824D6BD0, 0x824D69F4, 0x824D6A58, 0x824D6ABC, 0x824D6AE8, 0x824D6B0C, 0x824D6BD0, 0x824D6B30, 0x824D6B30, 0x824D6B84, 0x824D6B9C, 0x824D6BD0, 0x824D6B8C, 0x824D6BA8, 0x824D6BA8, ] [[switch]] base = 0x824DEE80 r = 11 default = 0x824DF020 labels = [ 0x824DEEF8, 0x824DEF0C, 0x824DEF20, 0x824DEF34, 0x824DEF5C, 0x824DEF48, 0x824DEF70, 0x824DEF84, 0x824DEF98, 0x824DEFAC, 0x824DF020, 0x824DF020, 0x824DF020, 0x824DF020, 0x824DEFC0, 0x824DEFD4, 0x824DEFE8, 0x824DEFFC, 0x824DF034, 0x824DF058, 0x824DF06C, 0x824DF080, 0x824DF094, 0x824DF0A8, ] [[switch]] base = 0x824DF0E8 r = 9 default = 0x824DF520 labels = [ 0x824DF128, 0x824DF148, 0x824DF198, 0x824DF218, 0x824DF298, 0x824DF2F4, 0x824DF360, 0x824DF3B4, 0x824DF440, 0x824DF48C, ] [[switch]] base = 0x824E825C r = 10 default = 0x824E9844 labels = [ 0x824E8684, 0x824E8734, 0x824E8880, 0x824E8928, 0x824E8928, 0x824E8950, 0x824E89B4, 0x824E9844, 0x824E8A64, 0x824E8B44, 0x824E89D0, 0x824E8B98, 0x824E8B98, 0x824E8BB4, 0x824E8C0C, 0x824E8C60, 0x824E8C60, 0x824E8C7C, 0x824E8CD4, 0x824E8D28, 0x824E8D28, 0x824E8D44, 0x824E8D9C, 0x824E8DF0, 0x824E8DF0, 0x824E8E84, 0x824E8E98, 0x824E8EF8, 0x824E8F14, 0x824E8F14, 0x824E8F30, 0x824E8FCC, 0x824E9024, 0x824E9024, 0x824E9044, 0x824E9058, 0x824E90AC, 0x824E9114, 0x824E9158, 0x824E9174, 0x824E9174, 0x824E9190, 0x824E9224, 0x824E9280, 0x824E9280, 0x824E92B8, 0x824E92CC, 0x824E9350, 0x824E9350, 0x824E92B8, 0x824E9380, 0x824E9444, 0x824E9444, 0x824E9458, 0x824E94A8, 0x824E94C8, 0x824E94C8, 0x824E9844, 0x824E94E4, 0x824E9538, 0x824E9538, 0x824E9538, 0x824E954C, 0x824E95A0, 0x824E95A0, 0x824E95A0, 0x824E9844, 0x824E95B4, 0x824E95B4, 0x824E95B4, 0x824E9844, 0x824E95C8, 0x824E95C8, 0x824E95C8, 0x824E9844, 0x824E95DC, 0x824E95DC, 0x824E95DC, 0x824E95F0, 0x824E9644, 0x824E9644, 0x824E9644, 0x824E9658, 0x824E9658, 0x824E9844, 0x824E9844, 0x824E967C, 0x824E96D0, 0x824E9724, 0x824E9778, 0x824E97CC, 0x824E9844, 0x824E845C, 0x824E84A4, 0x824E84EC, 0x824E8534, 0x824E857C, 0x824E85C4, 0x824E8748, 0x824E8760, 0x824E8778, 0x824E8790, 0x824E87A8, 0x824E87C0, 0x824E87D8, 0x824E87F0, 0x824E8808, 0x824E8820, 0x824E8838, 0x824E9844, 0x824E9844, 0x824E9844, 0x824E9368, 0x824E8868, 0x824E9844, 0x824E860C, 0x824E8624, 0x824E863C, 0x824E9844, 0x824E9844, 0x824E8654, 0x824E866C, ] [[switch]] base = 0x824E9FF8 r = 10 default = 0x824EA1E4 labels = [ 0x824EA070, 0x824EA070, 0x824EA11C, 0x824EA028, 0x824EA1F0, 0x824EA264, ] [[switch]] base = 0x824EB47C r = 11 default = 0x824EB74C labels = [ 0x824EB4D0, 0x824EB74C, 0x824EB74C, 0x824EB554, 0x824EB574, 0x824EB594, 0x824EB574, 0x824EB5E0, 0x824EB69C, 0x824EB6C0, 0x824EB6E4, 0x824EB6FC, 0x824EB714, 0x824EB730, 0x824EB744, ] [[switch]] base = 0x824EE648 r = 11 default = 0x824EE8E4 labels = [ 0x824EE730, 0x824EE900, 0x824EE6F0, 0x824EEA34, 0x824EE8E4, 0x824EEAB0, 0x824EEAB0, 0x824EE8E4, 0x824EE8D0, 0x824EEC18, 0x824EEC18, 0x824EEC28, 0x824EEC28, 0x824EE8E4, 0x824EE8D0, 0x824EECCC, 0x824EECCC, 0x824EECE4, 0x824EE8E4, 0x824EE8D0, 0x824EECF8, 0x824EECF8, 0x824EED10, 0x824EE8E4, 0x824EE8D0, 0x824EECA0, 0x824EECA0, 0x824EECB8, 0x824EE8E4, 0x824EE8D0, 0x824EED24, 0x824EED24, 0x824EED34, 0x824EED34, 0x824EE8E4, 0x824EE8D0, ] [[switch]] base = 0x824EF894 r = 10 default = 0x824F059C labels = [ 0x824EF910, 0x824F00F8, 0x824F0488, 0x824F0394, 0x824EF8C0, ] [[switch]] base = 0x824F2240 r = 11 default = 0x824F2AB8 labels = [ 0x824F22B4, 0x824F2358, 0x824F2AB8, 0x824F23C8, 0x824F2AB8, 0x824F2464, 0x824F2464, 0x824F24E0, 0x824F2340, 0x824F257C, 0x824F257C, 0x824F263C, 0x824F26D8, 0x824F2774, 0x824F2774, 0x824F2A14, 0x824F2828, 0x824F28C4, 0x824F28C4, 0x824F2AB8, 0x824F2970, 0x824F2A0C, 0x824F2A0C, ] [[switch]] base = 0x824F5344 r = 11 default = 0x824F5A34 labels = [ 0x824F5380, 0x824F544C, 0x824F555C, 0x824F5628, 0x824F56F4, 0x824F57C0, 0x824F588C, 0x824F5898, 0x824F5960, ] [[switch]] base = 0x824F5ABC r = 11 default = 0x824F7460 labels = [ 0x824F5B10, 0x824F5C4C, 0x824F5F54, 0x824F61F0, 0x824F6420, 0x824F65EC, 0x824F6804, 0x824F69D0, 0x824F6D60, 0x824F6F90, 0x824F7260, 0x824F72C4, 0x824F7328, 0x824F738C, 0x824F73F0, ] [[switch]] base = 0x824F9DD4 r = 11 default = 0x824F9FB8 labels = [ 0x824F9E08, 0x824F9ED8, 0x824F9EFC, 0x824F9F88, 0x824F9EA8, 0x824F9E90, 0x824F9EC0, ] [[switch]] base = 0x824FA918 r = 10 default = 0x824FAABC labels = [ 0x824FA948, 0x824FA9A0, 0x824FAAD8, 0x824FAB00, 0x824FAC3C, 0x824FAC64, ] [[switch]] base = 0x824FB400 r = 10 default = 0x824FB6D0 labels = [ 0x824FB46C, 0x824FB488, 0x824FB4A4, 0x824FB4C0, 0x824FB518, 0x824FB4DC, 0x824FB534, 0x824FB550, 0x824FB56C, 0x824FB588, 0x824FB6D0, 0x824FB5A4, 0x824FB5C0, 0x824FB5DC, 0x824FB5F8, 0x824FB614, 0x824FB630, 0x824FB64C, 0x824FB668, 0x824FB684, 0x824FB6A0, ] [[switch]] base = 0x824FC4A4 r = 11 default = 0x824FC600 labels = [ 0x824FC4D0, 0x824FC4DC, 0x824FC4E8, 0x824FC4F4, 0x824FC500, ] [[switch]] base = 0x824FC53C r = 11 default = 0x824FC600 labels = [ 0x824FC578, 0x824FC584, 0x824FC590, 0x824FC59C, 0x824FC5A8, 0x824FC5B4, 0x824FC5C0, 0x824FC5CC, 0x824FC5D8, ] [[switch]] base = 0x824FC644 r = 10 default = 0x824FC7A4 labels = [ 0x824FC674, 0x824FC6A0, 0x824FC6D8, 0x824FC704, 0x824FC730, 0x824FC768, ] [[switch]] base = 0x824FE078 r = 11 default = 0x824FE0EC labels = [ 0x824FE0A4, 0x824FE0BC, 0x824FE0D4, 0x824FE0EC, 0x824FE0E0, ] [[switch]] base = 0x824FE128 r = 30 default = 0x824FE1D4 labels = [ 0x824FE150, 0x824FE168, 0x824FE180, 0x824FE198, ] [[switch]] base = 0x824FE230 r = 30 default = 0x824FE2DC labels = [ 0x824FE258, 0x824FE270, 0x824FE288, 0x824FE2A0, ] [[switch]] base = 0x824FE570 r = 11 default = 0x824FEC90 labels = [ 0x824FE5A0, 0x824FE690, 0x824FE800, 0x824FE940, 0x824FEA7C, 0x824FEBC0, ] [[switch]] base = 0x824FECC0 r = 11 default = 0x824FF2F4 labels = [ 0x824FECEC, 0x824FEE08, 0x824FEE90, 0x824FF060, 0x824FF290, ] [[switch]] base = 0x824FFD28 r = 11 default = 0x8250502C labels = [ 0x824FFE40, 0x824FFE94, 0x824FFF40, 0x82500390, 0x825004E0, 0x8250065C, 0x825006DC, 0x825005DC, 0x825014AC, 0x82501548, 0x82501784, 0x825017FC, 0x82501850, 0x825018D0, 0x82501AD4, 0x82501B34, 0x82501DE0, 0x82501E48, 0x82502044, 0x825020B8, 0x82502248, 0x825022C4, 0x82500AB0, 0x82500B30, 0x82500DB4, 0x82500E34, 0x82501078, 0x82501190, 0x82501210, 0x825024CC, 0x82502564, 0x82502848, 0x82502464, 0x825024CC, 0x82502564, 0x82502848, 0x82502984, 0x825029DC, 0x82502C68, 0x82502D84, 0x82503068, 0x82503094, 0x82503290, 0x82503444, 0x825035F8, 0x82503798, 0x825038A8, 0x8250398C, 0x82503A28, 0x82503B18, 0x82503C0C, 0x82503C48, 0x82503E9C, 0x825041D8, 0x82504354, 0x825040C8, 0x82504474, 0x82504610, 0x825046A4, 0x825047A4, 0x82504EAC, 0x82504F24, 0x82504F68, 0x82504FE0, ] [[switch]] base = 0x824FFF5C r = 11 default = 0x8250502C labels = [ 0x824FFF84, 0x82500024, 0x825000C8, 0x825001B4, ] [[switch]] base = 0x82504938 r = 28 default = 0x82504990 labels = [ 0x82504960, 0x82504968, 0x82504970, 0x82504978, ] [[switch]] base = 0x8250499C r = 28 default = 0x82504A74 labels = [ 0x825049C4, 0x825049F0, 0x82504A1C, 0x82504A48, ] [[switch]] base = 0x8250760C r = 11 default = 0x825076D4 labels = [ 0x82507634, 0x82507644, 0x825076FC, 0x82507650, ] [[switch]] base = 0x82507EAC r = 11 default = 0x82508158 labels = [ 0x82507EF0, 0x82508158, 0x82508158, 0x82508158, 0x82508158, 0x82508158, 0x8250803C, 0x82508114, 0x825080AC, 0x82508158, 0x8250813C, ] [[switch]] base = 0x825098AC r = 11 default = 0x82509F98 labels = [ 0x825098F4, 0x82509B30, 0x82509B98, 0x82509D8C, 0x82509DE0, 0x82509E30, 0x82509E3C, 0x82509F44, 0x82509F60, 0x82509F6C, 0x82509F6C, 0x82509F6C, ] [[switch]] base = 0x8250A69C r = 11 default = 0x8250A7FC labels = [ 0x8250A6C4, 0x8250A6D4, 0x8250A824, 0x8250A6E0, ] [[switch]] base = 0x8250B1FC r = 11 default = 0x8250BBE8 labels = [ 0x8250B23C, 0x8250B534, 0x8250B6AC, 0x8250B8B0, 0x8250BAE8, 0x8250BB7C, 0x8250BBE8, 0x8250BBE8, 0x8250BBE8, 0x8250BC64, ] [[switch]] base = 0x8250C43C r = 11 default = 0x8250D3EC labels = [ 0x8250C4BC, 0x8250C56C, 0x8250C6A0, 0x8250C6E8, 0x8250C7D0, 0x8250C8BC, 0x8250C99C, 0x8250CA14, 0x8250CA60, 0x8250CB68, 0x8250CC64, 0x8250CCDC, 0x8250CD28, 0x8250D3EC, 0x8250CE24, 0x8250CE9C, 0x8250CEE8, 0x8250CFD8, 0x8250D050, 0x8250D0CC, 0x8250D1C8, 0x8250D28C, 0x8250D388, 0x8250D388, 0x8250D3EC, 0x8250D3E4, ] [[switch]] base = 0x8250D6E4 r = 11 default = 0x8250D808 labels = [ 0x8250D70C, 0x8250D71C, 0x8250D830, 0x8250D728, ] [[switch]] base = 0x8250DFE0 r = 11 default = 0x8250E898 labels = [ 0x8250E020, 0x8250E218, 0x8250E454, 0x8250E5D4, 0x8250E768, 0x8250E814, 0x8250E898, 0x8250E898, 0x8250E898, 0x8250E908, ] [[switch]] base = 0x8250F13C r = 11 default = 0x8250F35C labels = [ 0x8250F17C, 0x8250F288, 0x8250F35C, 0x8250F35C, 0x8250F35C, 0x8250F300, 0x8250F35C, 0x8250F35C, 0x8250F35C, 0x8250F3D8, ] [[switch]] base = 0x8250F454 r = 11 default = 0x0 labels = [ 0x8250F488, 0x8250F4A0, 0x8250F4DC, 0x8250F4DC, 0x8250F4DC, 0x8250F4C8, 0x8250F4D4, ] [[switch]] base = 0x82510ECC r = 11 default = 0x825114BC labels = [ 0x82510EF4, 0x82510F3C, 0x82511280, 0x82511404, ] [[switch]] base = 0x82511CC4 r = 4 default = 0x82512240 labels = [ 0x82511D18, 0x82511D38, 0x82511D5C, 0x82511D80, 0x82511DA4, 0x82511E14, 0x82511E38, 0x82511E64, 0x82511D80, 0x82511EBC, 0x82511F48, 0x825120A8, 0x82511F9C, 0x825120E0, 0x82512140, ] [[switch]] base = 0x825126EC r = 11 default = 0x82512E48 labels = [ 0x82512740, 0x82512770, 0x82512830, 0x825129F8, 0x82512A80, 0x82512AE4, 0x82512B24, 0x82512C7C, 0x82512C7C, 0x82512E48, 0x82512E48, 0x82512D2C, 0x82512CA0, 0x82512D94, 0x82512DF4, ] [[switch]] base = 0x8251ADE8 r = 11 default = 0x8251B1C8 labels = [ 0x8251AE14, 0x8251AE5C, 0x8251AF04, 0x8251AE14, 0x8251AE14, ] [[switch]] base = 0x8252C6A4 r = 10 default = 0x8252C78C labels = [ 0x8252C6D4, 0x8252C7A8, 0x8252C954, 0x8252C9D0, 0x8252CAC0, 0x8252CAE4, ] [[switch]] base = 0x82568A08 r = 4 default = 0x82568AF4 labels = [ 0x82568A54, 0x82568A68, 0x82568A74, 0x82568A80, 0x82568A8C, 0x82568A98, 0x82568AA4, 0x82568AB0, 0x82568ABC, 0x82568AC8, 0x82568AD4, 0x82568AE0, 0x82568AEC, ] [[switch]] base = 0x8256E4B8 r = 10 default = 0x8256E4F4 labels = [ 0x8256E4E8, 0x8256E4E8, 0x8256E4F4, 0x8256E4E8, 0x8256E4F4, 0x8256E4E8, ] [[switch]] base = 0x82587288 r = 11 default = 0x82587310 labels = [ 0x825872C4, 0x825872CC, 0x825872D4, 0x825872DC, 0x825872E4, 0x825872EC, 0x825872F4, 0x825872FC, 0x82587304, ] [[switch]] base = 0x82587C58 r = 11 default = 0x82588A00 labels = [ 0x82587CC4, 0x8258800C, 0x82588144, 0x82587D8C, 0x82587ED8, 0x825885B0, 0x82588A00, 0x82588A00, 0x82588394, 0x82588A00, 0x82588408, 0x82588A00, 0x82588A00, 0x82588A00, 0x82588A00, 0x82588A00, 0x82588A00, 0x82588A00, 0x8258834C, 0x82588A00, 0x8258849C, ] [[switch]] base = 0x8259A654 r = 11 default = 0x8259A6C8 labels = [ 0x8259A680, 0x8259A68C, 0x8259A6A0, 0x8259A6B8, 0x8259A6B8, ] [[switch]] base = 0x825B0BF4 r = 11 default = 0x825B0CB4 labels = [ 0x825B0C24, 0x825B0C3C, 0x825B0C54, 0x825B0C6C, 0x825B0C84, 0x825B0C9C, ] [[switch]] base = 0x825B0D00 r = 11 default = 0x825B1160 labels = [ 0x825B117C, 0x825B1160, 0x825B1110, 0x825B1110, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1160, 0x825B1110, ] [[switch]] base = 0x825B2420 r = 4 default = 0x825B2640 labels = [ 0x825B2474, 0x825B2488, 0x825B249C, 0x825B24B8, 0x825B24C8, 0x825B2540, 0x825B2560, 0x825B24D8, 0x825B24F0, 0x825B2508, 0x825B2578, 0x825B2590, 0x825B25C4, 0x825B2604, 0x825B2614, ] [[switch]] base = 0x825B3A34 r = 11 default = 0x825B3AA4 labels = [ 0x825B3AA4, 0x825B3A6C, 0x825B3A74, 0x825B3A7C, 0x825B3A84, 0x825B3A8C, 0x825B3A94, 0x825B3A9C, ] [[switch]] base = 0x825B4120 r = 11 default = 0x0 labels = [ 0x825B4154, 0x825B415C, 0x825B4154, 0x825B4164, 0x825B4164, 0x825B4164, 0x825B4164, ] [[switch]] base = 0x825B4500 r = 3 default = 0x825B47A0 labels = [ 0x825B4538, 0x825B467C, 0x825B467C, 0x825B4628, 0x825B46D0, 0x825B4734, 0x825B47A0, 0x825B45A0, ] [[switch]] base = 0x825C396C r = 11 default = 0x825C3A6C labels = [ 0x825C39A0, 0x825C39B0, 0x825C39C0, 0x825C39D0, 0x825C39E8, 0x825C39FC, 0x825C3A34, ] [[switch]] base = 0x825C6678 r = 10 default = 0x825C69B0 labels = [ 0x825C66A0, 0x825C67D0, 0x825C688C, 0x825C692C, ] [[switch]] base = 0x825E923C r = 4 default = 0x825E92B8 labels = [ 0x825E9270, 0x825E9284, 0x825E9270, 0x825E9284, 0x825E9284, 0x825E9284, 0x825E9284, ] [[switch]] base = 0x8260F808 r = 3 default = 0x8260F8A8 labels = [ 0x8260F880, 0x8260F830, 0x8260F858, 0x8260F880, ] [[switch]] base = 0x8261042C r = 11 default = 0x82610464 labels = [ 0x82610454, 0x8261045C, 0x826103FC, 0x826103FC, ] [[switch]] base = 0x8261089C r = 11 default = 0x826108D4 labels = [ 0x826108C4, 0x826108CC, 0x8261086C, 0x8261086C, ] [[switch]] base = 0x826160B4 r = 11 default = 0x826162F4 labels = [ 0x826160E0, 0x82616240, 0x826162A0, 0x826162F4, 0x82616360, ] [[switch]] base = 0x82635CE0 r = 11 default = 0x82635EDC labels = [ 0x82635D1C, 0x82635D54, 0x82635DC0, 0x82635DF8, 0x82635E64, 0x82635E7C, 0x82635E94, 0x82635EAC, 0x82635EC4, ] [[switch]] base = 0x82636934 r = 11 default = 0x826369E0 labels = [ 0x82636988, 0x826369E0, 0x826369A0, 0x826369E0, 0x82636994, 0x826369E0, 0x826369AC, 0x826369E0, 0x826369B8, 0x826369E0, 0x826369C4, 0x826369E0, 0x826369D0, 0x826369E0, 0x826369DC, ] [[switch]] base = 0x82645724 r = 30 default = 0x82645790 labels = [ 0x82645764, 0x8264577C, 0x82645784, 0x8264574C, ] [[switch]] base = 0x8264C7D8 r = 11 default = 0x8264C828 labels = [ 0x8264C810, 0x8264C818, 0x8264C820, 0x8264C820, 0x8264C820, 0x8264C810, 0x8264C828, 0x8264C810, ] [[switch]] base = 0x826555C8 r = 6 default = 0x8265584C labels = [ 0x82655600, 0x82655634, 0x826556D8, 0x82655720, 0x82655768, 0x826557B0, 0x826557D0, 0x82655818, ] [[switch]] base = 0x826559B0 r = 10 default = 0x82655BD4 labels = [ 0x82655B14, 0x82655BD4, 0x82655B2C, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BB4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BCC, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655BD4, 0x82655B44, ] [[switch]] base = 0x8265D3C8 r = 10 default = 0x8265D510 labels = [ 0x8265D3FC, 0x8265D46C, 0x8265D510, 0x8265D488, 0x8265D430, 0x8265D430, 0x8265D4EC, ] [[switch]] base = 0x8266953C r = 4 default = 0x826695E4 labels = [ 0x82669564, 0x82669570, 0x8266957C, 0x82669588, ] [[switch]] base = 0x8266FF6C r = 3 default = 0x82670158 labels = [ 0x8266FFA0, 0x8266FFE0, 0x82670014, 0x8267004C, 0x82670084, 0x826700C8, 0x8267010C, ] [[switch]] base = 0x82671B3C r = 11 default = 0x82671E64 labels = [ 0x82671BAC, 0x82671BC0, 0x82671E64, 0x82671BCC, 0x82671BFC, 0x82671C7C, 0x82671C58, 0x82671E64, 0x82671E64, 0x82671C30, 0x82671C0C, 0x82671C94, 0x82671CB0, 0x82671DB0, 0x82671DD8, 0x82671E00, 0x82671E28, 0x82671D20, 0x82671D44, 0x82671D68, 0x82671D8C, 0x82671E50, ] [[switch]] base = 0x82676764 r = 11 default = 0x82676838 labels = [ 0x82676828, 0x82676838, 0x826767F8, 0x826767E8, 0x82676838, 0x82676818, 0x82676838, 0x82676838, 0x82676838, 0x826767D8, 0x82676838, 0x826767C8, 0x82676838, 0x82676838, 0x82676838, 0x82676838, 0x82676838, 0x82676838, 0x82676808, ] [[switch]] base = 0x82677708 r = 11 default = 0x82677898 labels = [ 0x82677884, 0x8267773C, 0x8267774C, 0x826777A8, 0x82677778, 0x826777B8, 0x826777F8, ] [[switch]] base = 0x8271695C r = 11 default = 0x82716A24 labels = [ 0x827169A0, 0x827169D0, 0x82716A04, 0x82716A0C, 0x82716A0C, 0x82716A0C, 0x82716A0C, 0x82716A0C, 0x82716A0C, 0x82716A24, 0x82716A0C, ] [[switch]] base = 0x82718A74 r = 10 default = 0x82719010 labels = [ 0x82718FDC, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82718EA0, 0x82718EA0, 0x82718EC0, 0x82718F20, 0x82718F44, 0x82718F20, 0x82718F20, 0x82718F20, 0x82718F20, 0x82718EE0, 0x82718F00, 0x82718F20, 0x82718F2C, 0x82718F20, 0x82718F20, 0x82718F20, 0x82718FDC, 0x82718FDC, 0x82718FDC, 0x82718FDC, 0x82718FDC, 0x82718FDC, 0x82718FDC, 0x82718FDC, 0x82718E84, 0x82719060, 0x82719040, 0x82718F5C, 0x82718F2C, 0x82718F74, 0x82719010, 0x82719010, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82718F8C, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82719010, 0x82718FB8, ] [[switch]] base = 0x82720BBC r = 11 default = 0x82720C00 labels = [ 0x82720BE8, 0x82720BF0, 0x82720BF0, 0x82720BF8, 0x82720BF8, ] [[switch]] base = 0x82727FA0 r = 11 default = 0x82728084 labels = [ 0x82727FD8, 0x82727FE8, 0x82728084, 0x82727FF8, 0x82728084, 0x82728084, 0x82728084, 0x82728008, ] [[switch]] base = 0x82728014 r = 11 default = 0x82728078 labels = [ 0x82728048, 0x82728058, 0x82728068, 0x82728048, 0x82728058, 0x82728048, 0x82728058, ] [[switch]] base = 0x82729BC4 r = 11 default = 0x82729C20 labels = [ 0x82729BF0, 0x82729C00, 0x82729C00, 0x82729C10, 0x82729C10, ] [[switch]] base = 0x82733BB8 r = 11 default = 0x82733F8C labels = [ 0x82733F8C, 0x82733BE0, 0x82733DE0, 0x82733E80, ] [[switch]] base = 0x82735084 r = 11 default = 0x82735150 labels = [ 0x82735100, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x8273510C, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735118, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735150, 0x82735118, ] [[switch]] base = 0x8273BF84 r = 11 default = 0x8273C0D8 labels = [ 0x8273C060, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C088, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0A0, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0D8, 0x8273C0B8, ] [[switch]] base = 0x8273C124 r = 11 default = 0x8273C220 labels = [ 0x8273C1AC, 0x8273C1BC, 0x8273C1CC, 0x8273C1DC, 0x8273C1E8, 0x8273C1F4, 0x8273C208, 0x8273C214, 0x8273C16C, 0x8273C194, 0x8273C200, 0x8273C1A0, ] [[switch]] base = 0x8273C370 r = 3 default = 0x8273C430 labels = [ 0x8273C3C0, 0x8273C3C8, 0x8273C3D0, 0x8273C3D8, 0x8273C3E0, 0x8273C3E8, 0x8273C3F0, 0x8273C3F8, 0x8273C400, 0x8273C408, 0x8273C410, 0x8273C418, 0x8273C420, 0x8273C428, ] [[switch]] base = 0x8273C500 r = 11 default = 0x8273C8C0 labels = [ 0x8273C7CC, 0x8273C7D4, 0x8273C7D4, 0x8273C7D4, 0x8273C7D4, 0x8273C7D4, 0x8273C7D4, 0x8273C7D4, 0x8273C7D4, 0x8273C8C0, 0x8273C7DC, 0x8273C7DC, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C7E4, 0x8273C7E4, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C8C0, 0x8273C7C4, ] [[switch]] base = 0x8273C814 r = 11 default = 0x8273C8C0 labels = [ 0x8273C84C, 0x8273C8C0, 0x8273C8C0, 0x8273C854, 0x8273C85C, 0x8273C864, 0x8273C86C, 0x8273C874, ] [[switch]] base = 0x8273C9DC r = 10 default = 0x8273CD10 labels = [ 0x8273CD18, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CCB8, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CCC4, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CCCC, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CCD4, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD18, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD18, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CCDC, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CCF0, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD18, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD18, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD10, 0x8273CD18, ] [[switch]] base = 0x8273D448 r = 11 default = 0x8273D5DC labels = [ 0x8273D470, 0x8273D480, 0x8273D470, 0x8273D4A0, ] [[switch]] base = 0x8273D524 r = 11 default = 0x8273D5DC labels = [ 0x8273D54C, 0x8273D470, 0x8273D56C, 0x8273D470, ] [[switch]] base = 0x8273EA04 r = 11 default = 0x8273EB80 labels = [ 0x8273EA34, 0x8273EA90, 0x8273EAF4, 0x8273EB80, 0x8273EB48, 0x8273EB5C, ] [[switch]] base = 0x8273FFB0 r = 11 default = 0x827401C8 labels = [ 0x8273FFFC, 0x8274009C, 0x827400BC, 0x82740154, ] [[switch]] base = 0x82740400 r = 11 default = 0x8274051C labels = [ 0x8274051C, 0x82740428, 0x827404D8, 0x8274051C, ] [[switch]] base = 0x82741200 r = 11 default = 0x8274124C labels = [ 0x82741234, 0x82741240, 0x8274124C, 0x82741228, ] [[switch]] base = 0x82748850 r = 9 default = 0x82748C60 labels = [ 0x82748B68, 0x827489E4, 0x82748878, 0x82748878, ] [[switch]] base = 0x82749868 r = 10 default = 0x82749778 labels = [ 0x82749890, 0x82749890, 0x827498B8, 0x827498EC, ] [[switch]] base = 0x82749D80 r = 11 default = 0x82749DD0 labels = [ 0x82749DA8, 0x82749DB4, 0x82749DC0, 0x82749DA8, ] [[switch]] base = 0x82749F3C r = 11 default = 0x82749FD4 labels = [ 0x82749F64, 0x82749F7C, 0x82749FA4, 0x82749F64, ] [[switch]] base = 0x8274CD9C r = 11 default = 0x8274CD90 labels = [ 0x8274D108, 0x8274CD90, 0x8274D128, 0x8274D1EC, 0x8274CDDC, 0x8274CEA0, 0x8274CF54, 0x8274CF94, 0x8274CFD8, 0x8274D098, ] [[switch]] base = 0x8274EC8C r = 11 default = 0x8274F35C labels = [ 0x8274ECBC, 0x8274ED4C, 0x8274ED74, 0x8274F35C, 0x8274EE20, 0x8274EEDC, ] [[switch]] base = 0x8274FE04 r = 11 default = 0x82750694 labels = [ 0x8274FE34, 0x8274FF9C, 0x82750178, 0x82750694, 0x8275067C, 0x827502E4, ] [[switch]] base = 0x82752E90 r = 11 default = 0x8275327C labels = [ 0x82752EC8, 0x82752F3C, 0x82752F44, 0x82752F4C, 0x8275327C, 0x82752F58, 0x82752F68, 0x82752F78, ] [[switch]] base = 0x8275A4B8 r = 10 default = 0x8275A584 labels = [ 0x8275A4F0, 0x8275A4F0, 0x8275A4E0, 0x8275A51C, ] [[switch]] base = 0x8275A770 r = 10 default = 0x8275A8D0 labels = [ 0x8275A798, 0x8275A7AC, 0x8275A798, 0x8275A7C8, ] [[switch]] base = 0x8275A848 r = 10 default = 0x8275A8D0 labels = [ 0x8275A870, 0x8275A798, 0x8275A894, 0x8275A798, ] [[switch]] base = 0x8275AB88 r = 11 default = 0x8275AD40 labels = [ 0x8275ABB0, 0x8275ABB0, 0x8275ABDC, 0x8275AC2C, ] [[switch]] base = 0x8275BE78 r = 11 default = 0x8275BE68 labels = [ 0x8275BEB4, 0x8275C088, 0x8275C0E0, 0x8275C138, 0x8275C16C, 0x8275C214, 0x8275C3EC, 0x8275C9B8, 0x8275CE7C, ] [[switch]] base = 0x8275CFA8 r = 11 default = 0x8275CF9C labels = [ 0x8275D00C, 0x8275D00C, 0x8275D00C, 0x8275D00C, 0x8275D024, 0x8275D06C, 0x8275D06C, 0x8275D06C, 0x8275D06C, 0x8275D06C, 0x8275D06C, 0x8275D06C, 0x8275D06C, 0x8275D06C, 0x8275D054, 0x8275D054, 0x8275D054, 0x8275D0B0, 0x8275D0EC, ] [[switch]] base = 0x82763E30 r = 5 default = 0x82764034 labels = [ 0x82764018, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82763FFC, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82763FE0, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82764034, 0x82763FC4, ] [[switch]] base = 0x8276554C r = 27 default = 0x827656E4 labels = [ 0x827657F8, 0x82765580, 0x82765590, 0x827655B4, 0x827655E8, 0x8276562C, 0x82765680, ] [[switch]] base = 0x82769E4C r = 11 default = 0x8276A384 labels = [ 0x8276A050, 0x82769E90, 0x8276A0A4, 0x8276A0D0, 0x8276A0E8, 0x8276A120, 0x8276A16C, 0x8276A1A8, 0x8276A1F8, 0x8276A22C, 0x8276A27C, ] [[switch]] base = 0x8276A44C r = 11 default = 0x8276BB9C labels = [ 0x8276A538, 0x8276BB9C, 0x8276BB9C, 0x8276AFF0, 0x8276B5B4, 0x8276B658, 0x8276B680, 0x8276B680, 0x8276B6A4, 0x8276B874, 0x8276BB9C, 0x8276A8C4, 0x8276AA0C, 0x8276A994, 0x8276AA68, 0x8276AAE8, 0x8276BB9C, 0x8276BB9C, 0x8276AD84, 0x8276B060, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276B0D0, 0x8276B4A0, 0x8276B4E8, 0x8276AB04, 0x8276B148, 0x8276BB9C, 0x8276BB9C, 0x8276ACAC, 0x8276AE40, 0x8276AE08, 0x8276AC84, 0x8276ABE4, 0x8276AC2C, 0x8276AC58, 0x8276BB9C, 0x8276AEDC, 0x8276AF94, 0x8276AF38, 0x8276BB9C, 0x8276ACD8, 0x8276BB9C, 0x8276BB9C, 0x8276BB9C, 0x8276B35C, ] [[switch]] base = 0x8276C638 r = 11 default = 0x8276D700 labels = [ 0x8276C720, 0x8276D334, 0x8276C8E4, 0x8276C990, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276C96C, 0x8276C938, 0x8276C9B8, 0x8276C9E4, 0x8276CA30, 0x8276CA7C, 0x8276CB18, 0x8276CB64, 0x8276CBB4, 0x8276CBEC, 0x8276CC88, 0x8276CD4C, 0x8276CE48, 0x8276D700, 0x8276D700, 0x8276D3C4, 0x8276CE74, 0x8276D2C0, 0x8276D2E4, 0x8276D700, 0x8276D49C, 0x8276D468, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D700, 0x8276D4E0, 0x8276D700, 0x8276D530, 0x8276D588, 0x8276D700, 0x8276D450, 0x8276D698, 0x8276D6D0, ] [[switch]] base = 0x8276CEBC r = 11 default = 0x8276D294 labels = [ 0x8276CEE4, 0x8276CF20, 0x8276CFB0, 0x8276D03C, ] [[switch]] base = 0x8276DB20 r = 11 default = 0x8276DFB4 labels = [ 0x8276DB64, 0x8276DFB4, 0x8276DB68, 0x8276DBA8, 0x8276DFB4, 0x8276DFB4, 0x8276DFB4, 0x8276DFB4, 0x8276DFB4, 0x8276DBF4, 0x8276DC38, ] [[switch]] base = 0x8276DD68 r = 11 default = 0x8276DF40 labels = [ 0x8276DD90, 0x8276DDBC, 0x8276DDF0, 0x8276DE80, ] [[switch]] base = 0x8276E050 r = 11 default = 0x8276E770 labels = [ 0x8276E088, 0x8276E770, 0x8276E770, 0x8276E0C4, 0x8276E770, 0x8276E770, 0x8276E258, 0x8276E5E8, ] [[switch]] base = 0x82772908 r = 11 default = 0x82772CD8 labels = [ 0x82772944, 0x82772A28, 0x82772A60, 0x82772B04, 0x82772B8C, 0x82772C10, 0x82772C10, 0x82772CD8, 0x82772C5C, ] [[switch]] base = 0x82772D90 r = 11 default = 0x82772F9C labels = [ 0x82772DB8, 0x82772E30, 0x82772E7C, 0x82772F10, ] [[switch]] base = 0x82773290 r = 11 default = 0x82773284 labels = [ 0x827732C0, 0x82773364, 0x82773380, 0x8277339C, 0x827733B8, 0x827733D8, ] [[switch]] base = 0x82773578 r = 11 default = 0x82773568 labels = [ 0x827735A0, 0x827736BC, 0x8277368C, 0x827736F8, ] [[switch]] base = 0x82773A00 r = 11 default = 0x827739F0 labels = [ 0x82773A54, 0x82773B30, 0x82773BD0, 0x82773C5C, 0x82773CBC, 0x827739F0, 0x827739F0, 0x827739F0, 0x827739F0, 0x827739F0, 0x827739F0, 0x827739F0, 0x827739F0, 0x82773D84, 0x82773E18, ] [[switch]] base = 0x827744FC r = 11 default = 0x82774BF0 labels = [ 0x82774580, 0x82774BF0, 0x82774BF0, 0x82774BF0, 0x82774530, 0x82774934, 0x82774B1C, ] [[switch]] base = 0x82775018 r = 3 default = 0x827750D8 labels = [ 0x82775060, 0x82775070, 0x82775078, 0x82775084, 0x8277509C, 0x82775090, 0x827750A8, 0x827750B4, 0x827750C0, 0x82775068, 0x82775060, 0x827750CC, ] [[switch]] base = 0x827863A0 r = 11 default = 0x82786510 labels = [ 0x8278643C, 0x82786458, 0x82786474, 0x82786490, 0x827864A4, 0x827864B8, 0x827864E8, 0x827864FC, 0x82786404, 0x82786420, 0x827864D0, 0x827863E8, ] [[switch]] base = 0x82799D4C r = 11 default = 0x82799FB8 labels = [ 0x82799FB0, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799E80, 0x82799E40, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799FB8, 0x82799F80, 0x82799F50, 0x82799F20, 0x82799EF0, 0x82799EC0, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, 0x8279A130, ] [[switch]] base = 0x8279A550 r = 11 default = 0x8279A968 labels = [ 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A6F4, 0x8279A6C8, 0x8279A69C, 0x8279A670, 0x8279A644, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, 0x8279A968, ] [[switch]] base = 0x8279CBFC r = 28 default = 0x8279CFB8 labels = [ 0x8279CC34, 0x8279CC3C, 0x8279CC54, 0x8279CCA8, 0x8279CCC4, 0x8279CCE0, 0x8279CDF4, 0x8279CF08, ] [[switch]] base = 0x8279D804 r = 4 default = 0x8279D988 labels = [ 0x8279D834, 0x8279D874, 0x8279D8B8, 0x8279D8E4, 0x8279D90C, 0x8279D94C, ] [[switch]] base = 0x827B37B4 r = 11 default = 0x827B3934 labels = [ 0x827B3808, 0x827B38AC, 0x827B38D4, 0x827B3814, 0x827B3820, 0x827B3848, 0x827B3854, 0x827B3860, 0x827B3868, 0x827B3890, 0x827B38B8, 0x827B38E0, 0x827B38E8, 0x827B3910, 0x827B3918, ] [[switch]] base = 0x827B396C r = 11 default = 0x827B3C0C labels = [ 0x827B39C0, 0x827B3B28, 0x827B3B6C, 0x827B39F0, 0x827B3A18, 0x827B3A68, 0x827B3A90, 0x827B3AB8, 0x827B3AC0, 0x827B3B0C, 0x827B3B50, 0x827B3B94, 0x827B3B9C, 0x827B3BE8, 0x827B3BF0, ] [[switch]] base = 0x827C301C r = 19 default = 0x827C2F80 labels = [ 0x827C3088, 0x827C30C8, 0x827C2F80, 0x827C30E0, 0x827C30E0, 0x827C3120, 0x827C30E0, 0x827C3208, 0x827C3280, 0x827C3240, 0x827C2F80, 0x827C2F80, 0x827C3280, 0x827C3280, 0x827C2F80, 0x827C2F80, 0x827C3280, 0x827C2F80, 0x827C3280, 0x827C32C0, 0x827C32D8, ] [[switch]] base = 0x827C3B04 r = 11 default = 0x827C3ED8 labels = [ 0x827C3B70, 0x827C3B70, 0x827C3ED8, 0x827C3B70, 0x827C3B70, 0x827C3B70, 0x827C3B70, 0x827C3B70, 0x827C3B70, 0x827C3B70, 0x827C3BE0, 0x827C3ED8, 0x827C3B70, 0x827C3B70, 0x827C3ED8, 0x827C3ED8, 0x827C3B70, 0x827C3ED8, 0x827C3B70, 0x827C3B70, 0x827C3B70, ] [[switch]] base = 0x827D5D10 r = 4 default = 0x827D6EA4 labels = [ 0x827D5D58, 0x827D65AC, 0x827D6724, 0x827D682C, 0x827D6078, 0x827D6938, 0x827D6C30, 0x827D69E0, 0x827D5F68, 0x827D6D38, 0x827D6228, 0x827D63E0, ] [[switch]] base = 0x827D9084 r = 7 default = 0x827D91F4 labels = [ 0x827D90AC, 0x827D90F4, 0x827D914C, 0x827D91A0, ] [[switch]] base = 0x827D91FC r = 8 default = 0x0 labels = [ 0x827D9224, 0x827D9280, 0x827D92EC, 0x827D9354, ] [[switch]] base = 0x82803294 r = 10 default = 0x82803320 labels = [ 0x828032BC, 0x828032C8, 0x828032DC, 0x828032F0, ] [[switch]] base = 0x8289E3A0 r = 3 default = 0x8289E638 labels = [ 0x8289E3D4, 0x8289E40C, 0x8289E480, 0x8289E528, 0x8289E5D0, 0x8289E618, 0x8289E618, ] [[switch]] base = 0x828A0750 r = 10 default = 0x828A0794 labels = [ 0x828A0790, 0x828A0794, 0x828A077C, 0x828A0790, 0x828A0790, ] [[switch]] base = 0x828A27E0 r = 11 default = 0x828A2AE0 labels = [ 0x828A2818, 0x828A2AE0, 0x828A2AE0, 0x828A2834, 0x828A288C, 0x828A28E0, 0x828A2A34, 0x828A2A8C, ] [[switch]] base = 0x828A28F0 r = 11 default = 0x828A2AE0 labels = [ 0x828A291C, 0x828A2AE0, 0x828A2948, 0x828A299C, 0x828A29D0, ] [[switch]] base = 0x828A302C r = 11 default = 0x828A3408 labels = [ 0x828A3064, 0x828A3408, 0x828A3408, 0x828A307C, 0x828A3108, 0x828A315C, 0x828A32F0, 0x828A337C, ] [[switch]] base = 0x828A316C r = 11 default = 0x828A3408 labels = [ 0x828A31EC, 0x828A3408, 0x828A3198, 0x828A32A8, 0x828A3218, ] [[switch]] base = 0x828A7100 r = 10 default = 0x828A73FC labels = [ 0x828A7188, 0x828A7128, 0x828A7160, 0x828A7160, ] [[switch]] base = 0x828AD250 r = 3 default = 0x828AD3B0 labels = [ 0x828AD288, 0x828AD2A4, 0x828AD2A4, 0x828AD2A4, 0x828AD2D0, 0x828AD314, 0x828AD3B0, 0x828AD370, ] [[switch]] base = 0x828B3A00 r = 9 default = 0x828B3A3C labels = [ 0x828B3A38, 0x828B3A3C, 0x828B3A3C, 0x828B3A38, 0x828B3A3C, 0x828B3A38, 0x828B3A38, 0x828B3A38, ] [[switch]] base = 0x828B9CD0 r = 3 default = 0x828B9D40 labels = [ 0x828B9D40, 0x828B9D08, 0x828B9D10, 0x828B9D18, 0x828B9D20, 0x828B9D28, 0x828B9D30, 0x828B9D38, ] [[switch]] base = 0x828BFB20 r = 3 default = 0x828BFB58 labels = [ 0x828BFB58, 0x828BFB48, 0x828BFB50, 0x828BFB58, ] [[switch]] base = 0x828C4B10 r = 11 default = 0x828C4BB0 labels = [ 0x828C4B58, 0x828C4B38, 0x828C4B48, 0x828C4B48, ] [[switch]] base = 0x828C4BF8 r = 11 default = 0x0 labels = [ 0x828C4C64, 0x828C4C20, 0x828C4C40, 0x828C4C30, ] [[switch]] base = 0x82908A68 r = 11 default = 0x82908C64 labels = [ 0x82908ADC, 0x82908AEC, 0x82908AFC, 0x82908B2C, 0x82908B0C, 0x82908B40, 0x82908B4C, 0x82908B58, 0x82908B64, 0x82908B70, 0x82908B7C, 0x82908B88, 0x82908B94, 0x82908BA0, 0x82908BB4, 0x82908BC8, 0x82908BDC, 0x82908BF0, 0x82908C04, 0x82908C18, 0x82908C2C, 0x82908C40, 0x82908C54, ] [[switch]] base = 0x8290C928 r = 11 default = 0x8290C9C8 labels = [ 0x8290C958, 0x8290C970, 0x8290C988, 0x8290C9A0, 0x8290C9C8, 0x8290C9C0, ] [[switch]] base = 0x82912540 r = 10 default = 0x829125F0 labels = [ 0x82912570, 0x82912570, 0x82912578, 0x82912578, 0x82912580, 0x82912580, ] [[switch]] base = 0x829125A0 r = 10 default = 0x829125F0 labels = [ 0x829125D0, 0x829125D0, 0x829125D8, 0x829125D8, 0x829125E4, 0x829125E4, ] [[switch]] base = 0x82912648 r = 10 default = 0x82912714 labels = [ 0x82912678, 0x82912678, 0x82912680, 0x82912680, 0x82912698, 0x82912698, ] [[switch]] base = 0x829126C8 r = 10 default = 0x82912714 labels = [ 0x829126F8, 0x829126F8, 0x82912700, 0x82912700, 0x82912708, 0x82912708, ] [[switch]] base = 0x82912770 r = 10 default = 0x82912848 labels = [ 0x829127A0, 0x829127A0, 0x829127A8, 0x829127A8, 0x829127BC, 0x829127BC, ] [[switch]] base = 0x82912800 r = 10 default = 0x82912848 labels = [ 0x82912830, 0x82912830, 0x82912838, 0x82912838, 0x82912840, 0x82912840, ] [[switch]] base = 0x82912A24 r = 5 default = 0x0 labels = [ 0x82912A6C, 0x82912A6C, 0x82912A78, 0x82912A78, 0x82912A90, 0x82912A90, 0x82912AC0, 0x82912AC0, 0x82912B20, 0x82912B58, 0x82912BC0, 0x82912C08, ] [[switch]] base = 0x82914570 r = 3 default = 0x829145D8 labels = [ 0x829145B8, 0x829145B8, 0x829145C0, 0x829145C0, 0x829145C8, 0x829145C8, 0x829145D0, 0x829145D0, 0x829145C8, 0x829145D0, 0x829145C8, 0x829145D0, ] # ---- COMPUTED JUMPTABLE ---- [[switch]] base = 0x8253AEC8 r = 10 default = 0x8253B068 labels = [ 0x8253AF80, 0x8253AFB0, 0x8253AF34, 0x8253AEF0, 0x8253B068, 0x8253AFDC, 0x8253B008, 0x8253B024, 0x8253B040, ] [[switch]] base = 0x82546200 r = 10 default = 0x825463F8 labels = [ 0x825463F4, 0x825463F0, 0x825463EC, 0x82546228, 0x825463BC, 0x825463F0, 0x825463EC, 0x8254625C, 0x825462A0, 0x825462E0, 0x82546354, 0x82546374, 0x825463F4, 0x82546228, 0x825463BC, 0x825463F4, ] [[switch]] base = 0x82557084 r = 31 default = 0x825573E0 labels = [ 0x825570B0, 0x825570AC, 0x825570B0, 0x82557274, 0x82557198, 0x82557198, 0x82557364, 0x82557388, 0x825573E0, 0x825573E0, 0x825570B0, 0x825573E0, 0x82557274, 0x825573E0, 0x825573CC, 0x825573D4, ] [[switch]] base = 0x825DFFB4 r = 11 default = 0x825E01B0 labels = [ 0x825DFFDC, 0x825DFFF4, 0x825E000C, 0x825E0018, 0x825E01B0, 0x825E0018, 0x825E000C, 0x825E01B0, 0x825E01B0, 0x825E0030, 0x825E01B0, 0x825E01B0, 0x825E01B0, 0x825E01B0, 0x825E01B0, 0x825E01B0, 0x825E01B0, 0x825E0048, 0x825E0080, 0x825E01B0, 0x825E01B0, 0x825E01B0, 0x825E01B0, 0x825E00B4, 0x825E00B4, 0x825E012C, 0x825E0074, 0x825E0058, 0x825E01B0, 0x825E0144, 0x825E01B0, 0x825E01B0, 0x825E0154, ] [[switch]] base = 0x826E8324 r = 10 default = 0x826E85D0 labels = [ 0x826E834C, 0x826E85D0, 0x826E834C, 0x826E85D0, 0x826E8478, 0x826E85D0, 0x826E834C, 0x826E834C, 0x826E8478, 0x826E8478, 0x826E85D0, 0x826E834C, ] [[switch]] base = 0x826E860C r = 11 default = 0x826E83B8 labels = [ 0x826E8634, 0x826E8664, 0x826E8694, 0x826E86C4, 0x826E86DC, 0x826E86D0, 0x826E86E8, 0x826E86F0, 0x826E86FC, ] [[switch]] base = 0x826EEF14 r = 9 default = 0x826EF1DC labels = [ 0x826EEF3C, 0x826EEF3C, 0x826EEF3C, 0x826EEF50, 0x826EEF50, 0x826EEF5C, 0x826EEF5C, 0x826EEF68, 0x826EEF68, 0x826EF1DC, 0x826EEF74, 0x826EEF90, 0x826EEF80, 0x826EEFA4, 0x826EEFA4, 0x826EEFA4, 0x826EEFA4, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF1DC, 0x826EF018, ] [[switch]] base = 0x826F264C r = 11 default = 0x826F29B4 labels = [ 0x826F2674, 0x826F26E8, 0x826F2764, 0x826F27E0, 0x826F2850, 0x826F28C4, 0x826F28E4, 0x826F2968, 0x826F292C, 0x826F29C0, 0x826F29B4, 0x826F2980, ] [[switch]] base = 0x8283011C r = 10 default = 0x828302E0 labels = [ 0x82830144, 0x82830178, 0x828301B0, 0x828302E8, 0x828301D8, 0x828301FC, 0x82830220, 0x8283024C, 0x82830270, ] [[switch]] base = 0x8283724C r = 11 default = 0x828375BC labels = [ 0x828375D0, 0x8283751C, 0x82837534, 0x82837274, 0x828372C4, 0x828372D4, 0x828372E8, 0x82837310, 0x82837334, 0x82837348, 0x82837354, 0x82837384, 0x828373B4, 0x828373D8, 0x8283748C, 0x828374B0, 0x828373FC, 0x82837420, 0x82837444, 0x82837468, 0x828374D4, 0x828374F8, 0x82837540, 0x8283755C, 0x82837564, 0x82837588, ] [[switch]] base = 0x82860FDC r = 11 default = 0x8286116C labels = [ 0x82861004, 0x8286116C, 0x82861010, 0x8286101C, 0x82861028, 0x82861034, 0x82861064, 0x82861040, 0x8286104C, 0x82861058, 0x82861070, 0x8286107C, 0x82861088, 0x82861094, 0x828610A0, 0x82861124, 0x82861130, 0x828610AC, 0x828610B8, 0x828610C4, 0x828610D0, 0x828610E8, 0x828610DC, 0x828610F4, 0x82861100, 0x8286110C, 0x82861118, 0x8286113C, 0x82861148, 0x82861154, 0x82861160, ] [[switch]] base = 0x828920B8 r = 28 default = 0x828920A8 labels = [ 0x828922F4, 0x828922F4, 0x828920A8, 0x828922E8, 0x82892130, 0x828921A4, 0x828920E0, 0x82892100, 0x82892100, 0x828922F4, 0x828922F4, 0x828920E0, 0x828920E0, 0x82892100, 0x82892120, 0x82892274, 0x82892274, 0x82892120, 0x82892128, 0x828922E8, 0x828922E8, ] [[switch]] base = 0x82917564 r = 11 default = 0x82917740 labels = [ 0x8291758C, 0x829175A4, 0x8291758C, 0x8291758C, 0x8291758C, 0x82917600, 0x82917600, 0x82917600, 0x82917600, 0x82917610, 0x82917710, 0x82917710, ] [[switch]] base = 0x8291A7F4 r = 11 default = 0x8291AA30 labels = [ 0x8291A81C, 0x8291A828, 0x8291A834, 0x8291A840, 0x8291A84C, 0x8291A854, 0x8291A85C, 0x8291A868, 0x8291A874, 0x8291A880, 0x8291A88C, 0x8291A894, 0x8291A89C, 0x8291A8A4, 0x8291A8AC, 0x8291A8B4, 0x8291A8C0, 0x8291A8CC, 0x8291A8D8, 0x8291A8E4, 0x8291A8F0, 0x8291A8FC, 0x8291A908, 0x8291A914, 0x8291A920, 0x8291A92C, 0x8291A938, 0x8291A944, 0x8291A950, 0x8291A95C, 0x8291A968, 0x8291A974, 0x8291A980, 0x8291A98C, 0x8291A998, 0x8291A9A4, 0x8291A9B0, 0x8291A9BC, 0x8291A9C8, 0x8291A9D4, 0x8291A9E0, 0x8291A9EC, 0x8291A9F8, 0x8291AA04, 0x8291AA10, 0x8291AA18, 0x8291AA20, ] [[switch]] base = 0x82921C64 r = 11 default = 0x82921D9C labels = [ 0x82921C8C, 0x82921C98, 0x82921CB4, 0x82921CC4, 0x82921CD0, 0x82921CDC, 0x82921D00, 0x82921D24, 0x82921D48, 0x82921D6C, 0x82921D78, 0x82921D9C, 0x82921D84, 0x82921D90, ] [[switch]] base = 0x82927C10 r = 11 default = 0x82927D40 labels = [ 0x82927D40, 0x82927C38, 0x82927C4C, 0x82927C4C, 0x82927C4C, 0x82927C54, 0x82927C54, 0x82927C54, 0x82927C54, 0x82927C5C, 0x82927D40, 0x82927D40, 0x82927BDC, 0x82927BFC, ] [[switch]] base = 0x82927ED4 r = 11 default = 0x829281A4 labels = [ 0x82927EFC, 0x82927F04, 0x82927F10, 0x82927F1C, 0x82927F28, 0x82927F34, 0x82927F40, 0x82927F48, 0x82927F54, 0x82927F60, 0x82927F6C, 0x82927F78, 0x82927F84, 0x82927F90, 0x82927F9C, 0x82927FA8, 0x82927FB4, 0x82927FC0, 0x82927FCC, 0x82927FD8, 0x82927FE4, 0x82927FF0, 0x82927FFC, 0x82928008, 0x82928014, 0x82928020, 0x8292802C, 0x82928038, 0x82928044, 0x82928050, 0x8292805C, 0x82928068, 0x82928074, 0x82928080, 0x8292808C, 0x82928098, 0x829280A4, 0x829280B0, 0x829280B8, 0x829280C0, 0x829280CC, 0x829280D8, 0x829280E4, 0x829280F0, 0x829280FC, 0x82928108, 0x82928114, 0x82928120, 0x8292812C, 0x82928134, 0x8292813C, 0x82928144, 0x8292814C, 0x82928154, 0x8292815C, 0x82928164, 0x8292816C, 0x82928174, 0x8292817C, 0x82928184, 0x8292818C, 0x82928194, ] [[switch]] base = 0x82928A48 r = 11 default = 0x82928BA8 labels = [ 0x82928A80, 0x82928A70, 0x82928A90, 0x82928AA0, 0x82928AB0, 0x82928AC0, 0x82928AD0, 0x82928AE0, 0x82928AF0, 0x82928B00, 0x82928B10, 0x82928B20, 0x82928BA8, 0x82928B30, 0x82928B40, 0x82928B50, 0x82928B60, 0x82928B40, 0x82928B6C, 0x82928B60, 0x82928B40, 0x82928B50, 0x82928B78, 0x82928B88, 0x82928B98, ] [[switch]] base = 0x829448A4 r = 29 default = 0x8294476C labels = [ 0x8294496C, 0x82944980, 0x82944988, 0x82944990, 0x82944998, 0x829449A0, 0x829449A8, 0x829449B0, 0x829449C0, 0x8294476C, 0x829449CC, ] [[switch]] base = 0x82945ED0 r = 11 default = 0x82945C28 labels = [ 0x82945F20, 0x82945F10, 0x82945F30, 0x82945F28, 0x82945F18, 0x82945F08, 0x82945F00, 0x82945EF8, 0x82945F38, ] [[switch]] base = 0x82949D04 r = 11 default = 0x82949EB0 labels = [ 0x82949EB0, 0x82949D3C, 0x82949D50, 0x82949D50, 0x82949D50, 0x82949D58, 0x82949D58, 0x82949D58, 0x82949D58, 0x82949D60, 0x82949EB0, 0x82949EB0, 0x82949C48, 0x82949D34, ] [[switch]] base = 0x8294A1B8 r = 11 default = 0x8294A32C labels = [ 0x8294A1E0, 0x8294A1F0, 0x8294A200, 0x8294A20C, 0x8294A218, 0x8294A224, 0x8294A230, 0x8294A23C, 0x8294A248, 0x8294A254, 0x8294A260, 0x8294A26C, 0x8294A27C, 0x8294A288, 0x8294A290, 0x8294A29C, 0x8294A2A8, 0x8294A2B4, 0x8294A2C8, 0x8294A2F0, 0x8294A304, 0x8294A2DC, ] [[switch]] base = 0x829549D0 r = 30 default = 0x82954BA8 labels = [ 0x82954BA0, 0x829549F8, 0x82954A00, 0x82954A08, 0x82954A10, 0x82954A18, 0x82954A20, 0x82954A28, 0x82954A30, 0x82954A38, 0x82954A40, 0x82954A48, 0x82954A50, 0x82954A58, 0x82954A60, 0x82954BA8, 0x82954BA8, 0x82954A68, 0x82954A68, ] [[switch]] base = 0x82970CD4 r = 30 default = 0x82970DF0 labels = [ 0x82971028, 0x82970CFC, 0x82970DAC, 0x82970DB4, 0x82970DBC, 0x82970DC4, 0x82970DCC, 0x82970DD4, 0x82970DDC, 0x82970DE4, 0x82970E00, 0x82970E08, 0x82970E10, 0x82970E18, 0x82970E20, 0x82970DF0, 0x82970DF0, 0x82970E30, 0x82970E28, ] [[switch]] base = 0x8298EE5C r = 11 default = 0x8298EF8C labels = [ 0x8298EE84, 0x8298EE90, 0x8298EE90, 0x8298EE90, 0x8298EE90, 0x8298EE90, 0x8298EE90, 0x8298EE90, 0x8298EE90, 0x8298EE9C, 0x8298EEA8, 0x8298EE9C, 0x8298EEB4, 0x8298EEC0, 0x8298EECC, 0x8298EED8, 0x8298EEE4, 0x8298EEF0, 0x8298EEFC, 0x8298EF08, 0x8298EF14, 0x8298EF20, 0x8298EF2C, 0x8298EF38, 0x8298EF44, 0x8298EF50, 0x8298EF5C, 0x8298EF68, 0x8298EF74, 0x8298EF80, ] [[switch]] base = 0x8299389C r = 11 default = 0x82993BB0 labels = [ 0x829938C4, 0x82993924, 0x8299392C, 0x82993934, 0x8299393C, 0x82993960, 0x8299398C, 0x82993994, 0x8299399C, 0x829939A8, 0x829939B4, 0x829939C0, 0x829939CC, 0x829939D4, 0x829939DC, 0x829939E8, ] [[switch]] base = 0x829B5044 r = 30 default = 0x829B5288 labels = [ 0x829B52C0, 0x829B506C, 0x829B5244, 0x829B524C, 0x829B5254, 0x829B525C, 0x829B5264, 0x829B526C, 0x829B5274, 0x829B527C, 0x829B5298, 0x829B52A0, 0x829B52A8, 0x829B52B0, 0x829B52B8, ] [[switch]] base = 0x829C3A20 r = 11 default = 0x829C3A6C labels = [ 0x829C3A48, 0x829C3B00, 0x829C3A64, 0x829C3B18, 0x829C3B24, 0x829C3B3C, 0x829C3AB8, 0x829C3AD0, 0x829C3B54, 0x829C3AE8, 0x829C3A6C, 0x829C3B8C, 0x829C3A6C, 0x829C3B6C, ] [[switch]] base = 0x82A648BC r = 11 default = 0x82A64B30 labels = [ 0x82A648E4, 0x82A64B30, 0x82A64A5C, 0x82A64AA0, 0x82A649AC, 0x82A649AC, 0x82A649E4, 0x82A648FC, 0x82A648FC, 0x82A64A0C, 0x82A649F4, 0x82A649F4, 0x82A64A20, ] [[switch]] base = 0x82A68914 r = 11 default = 0x82A68C74 labels = [ 0x82A68A70, 0x82A68ABC, 0x82A68AD0, 0x82A68AAC, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A6893C, 0x82A68960, 0x82A68990, 0x82A689A8, 0x82A689D0, 0x82A68A00, 0x82A68A24, 0x82A68A2C, 0x82A68A34, 0x82A68B00, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68A54, 0x82A68A70, 0x82A68A84, 0x82A68A98, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68A70, 0x82A68ABC, 0x82A68AD0, 0x82A68AAC, 0x82A68AE4, 0x82A68A3C, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68C74, 0x82A68A44, 0x82A68A4C, ] [[switch]] base = 0x82A976DC r = 4 default = 0x82A9784C labels = [ 0x82A977FC, 0x82A977FC, 0x82A97704, 0x82A97718, 0x82A97730, 0x82A97750, 0x82A977A4, 0x82A977C0, 0x82A97704, 0x82A97704, 0x82A97774, 0x82A977A4, 0x82A977A4, 0x82A9784C, 0x82A977A4, 0x82A97790, 0x82A977CC, 0x82A977D8, 0x82A9781C, 0x82A97824, 0x82A97824, 0x82A9784C, 0x82A97804, 0x82A97804, 0x82A9779C, 0x82A977F4, 0x82A97814, 0x82A9779C, 0x82A977F4, 0x82A97814, 0x82A9779C, 0x82A977F4, 0x82A97814, 0x82A977FC, 0x82A9781C, 0x82A97824, 0x82A977FC, 0x82A9781C, 0x82A97824, 0x82A977A4, 0x82A977A4, 0x82A9779C, 0x82A977F4, 0x82A97704, 0x82A977A4, 0x82A977A4, 0x82A9779C, 0x82A9779C, 0x82A977F4, 0x82A97824, 0x82A977A4, 0x82A9781C, 0x82A97824, 0x82A97824, 0x82A977C0, 0x82A977CC, 0x82A977D8, 0x82A9782C, 0x82A9781C, 0x82A9781C, 0x82A9781C, 0x82A9781C, ] [[switch]] base = 0x82A97878 r = 10 default = 0x82A979B8 labels = [ 0x82A978A0, 0x82A978B4, 0x82A978CC, 0x82A978EC, 0x82A97940, 0x82A9795C, 0x82A978A0, 0x82A978A0, 0x82A97910, 0x82A979B8, 0x82A979B8, 0x82A979B8, 0x82A97940, 0x82A9792C, 0x82A97968, 0x82A97974, 0x82A979B8, 0x82A979B8, 0x82A979B8, 0x82A979B8, 0x82A979B8, 0x82A979B8, 0x82A97938, 0x82A97990, 0x82A979A0, 0x82A979B8, 0x82A979B8, 0x82A979B8, 0x82A97938, 0x82A97990, 0x82A979A0, 0x82A979B8, 0x82A979B8, 0x82A979B8, 0x82A97998, 0x82A979A8, 0x82A979B0, ] # ---- OFFSETED JUMPTABLE ---- [[switch]] base = 0x8253B77C r = 10 default = 0x0 labels = [ 0x8253B7A4, 0x8253B7F4, 0x8253B7CC, 0x8253B808, 0x8253B820, 0x8253B834, 0x8253B83C, 0x8253B844, 0x8253B844, ] [[switch]] base = 0x825478B0 r = 10 default = 0x8254792C labels = [ 0x8254790C, 0x8254790C, 0x8254792C, 0x8254792C, 0x825478D8, 0x825478D8, 0x825478F8, 0x8254792C, 0x825478F8, 0x8254792C, 0x8254790C, 0x8254790C, ] [[switch]] base = 0x825D8B7C r = 3 default = 0x825D8C14 labels = [ 0x825D8C80, 0x825D8BC8, 0x825D8C50, 0x825D8BA4, 0x825D8BB8, 0x825D8BF4, 0x825D8C38, 0x825D8C50, 0x825D8C68, ] [[switch]] base = 0x826EA534 r = 11 default = 0x826EA600 labels = [ 0x826EA55C, 0x826EA55C, 0x826EA568, 0x826EA568, 0x826EA590, 0x826EA574, 0x826EA580, 0x826EA580, ] [[switch]] base = 0x82705AD8 r = 11 default = 0x82705BB8 labels = [ 0x82705B00, 0x82705B40, 0x82705B90, 0x82705B54, 0x82705B2C, 0x82705B14, 0x82705BA4, 0x82705B7C, 0x82705B68, ] [[switch]] base = 0x82833094 r = 11 default = 0x0 labels = [ 0x828330BC, 0x828330D4, 0x828330EC, 0x828330F4, 0x828330FC, 0x82833104, 0x8283310C, 0x82833114, 0x8283311C, ] [[switch]] base = 0x8292BE18 r = 11 default = 0x8292BE80 labels = [ 0x8292BE40, 0x8292BE48, 0x8292BE50, 0x8292BE5C, 0x8292BE64, 0x8292BE48, 0x8292BE50, 0x8292BE5C, 0x8292BE64, 0x8292BE80, 0x8292BE6C, 0x8292BE74, 0x8292BE7C, ] [[switch]] base = 0x8293F258 r = 11 default = 0x8293F344 labels = [ 0x8293F280, 0x8293F294, 0x8293F280, 0x8293F280, 0x8293F280, 0x8293F2CC, 0x8293F2CC, 0x8293F2CC, 0x8293F2CC, 0x8293F2DC, 0x8293F30C, ] [[switch]] base = 0x82944EA4 r = 9 default = 0x82944F60 labels = [ 0x82944ECC, 0x82944F00, 0x82944EE4, 0x82944EE4, 0x82944EE4, 0x82944ECC, 0x82944EEC, 0x82944EEC, 0x82944ECC, 0x82944ECC, 0x82944F28, 0x82944EE4, 0x82944EE4, 0x82944F30, 0x82944F40, ] [[switch]] base = 0x8294C38C r = 10 default = 0x8294C404 labels = [ 0x8294C3F0, 0x8294C3E4, 0x8294C3B4, 0x8294C3B4, 0x8294C3B4, 0x8294C3C0, 0x8294C3D8, 0x8294C3FC, 0x8294C3CC, ] [[switch]] base = 0x829667EC r = 10 default = 0x829668B4 labels = [ 0x82966814, 0x8296681C, 0x8296681C, 0x8296681C, 0x8296681C, 0x8296681C, 0x8296681C, 0x8296681C, 0x8296681C, 0x82966824, 0x82966824, 0x82966824, 0x82966824, 0x8296681C, 0x8296682C, 0x82966834, 0x8296683C, 0x82966844, 0x8296684C, 0x82966854, 0x8296685C, 0x82966888, 0x82966864, 0x82966874, 0x82966890, 0x82966898, 0x829668A0, 0x829668A8, 0x829668B0, ] [[switch]] base = 0x8298D264 r = 11 default = 0x8298D29C labels = [ 0x8298D28C, 0x8298D2A4, 0x8298D2C4, 0x8298D2E8, 0x8298D28C, 0x8298D2A4, 0x8298D2C4, 0x8298D2E8, 0x8298D308, 0x8298D318, 0x8298D328, 0x8298D338, ] [[switch]] base = 0x8298D6FC r = 11 default = 0x8298D750 labels = [ 0x8298D724, 0x8298D72C, 0x8298D72C, 0x8298D72C, 0x8298D72C, 0x8298D734, 0x8298D734, 0x8298D734, 0x8298D734, 0x8298D73C, 0x8298D73C, 0x8298D73C, 0x8298D73C, 0x8298D74C, 0x8298D74C, 0x8298D74C, 0x8298D74C, 0x8298D74C, 0x8298D74C, 0x8298D74C, 0x8298D744, 0x8298D744, 0x8298D744, 0x8298D744, 0x8298D744, 0x8298D74C, 0x8298D74C, 0x8298D74C, 0x8298D74C, 0x8298D744, ] [[switch]] base = 0x8298E40C r = 11 default = 0x8298E4A0 labels = [ 0x8298E434, 0x8298E440, 0x8298E45C, 0x8298E45C, 0x8298E45C, 0x8298E468, 0x8298E468, 0x8298E468, 0x8298E468, 0x8298E474, 0x8298E47C, 0x8298E4A0, 0x8298E488, 0x8298E494, ] [[switch]] base = 0x829C7278 r = 9 default = 0x829C7330 labels = [ 0x829C72A0, 0x829C72A8, 0x829C72B0, 0x829C72B8, 0x829C72C0, 0x829C72C8, 0x829C72D4, 0x829C72DC, 0x829C72E4, 0x829C72EC, 0x829C72F4, 0x829C72FC, 0x829C7304, 0x829C730C, 0x829C7314, 0x829C731C, 0x829C7324, 0x829C732C, ] [[switch]] base = 0x829CDB24 r = 11 default = 0x829CDB68 labels = [ 0x829CDB60, 0x829CDB60, 0x829CDB4C, 0x829CDB4C, 0x829CDB4C, 0x829CDB58, 0x829CDB58, 0x829CDB60, 0x829CDB4C, 0x829CDB60, 0x829CDB4C, 0x829CDB68, 0x829CDB4C, 0x829CDB4C, 0x829CDB68, 0x829CDB68, 0x829CDB68, 0x829CDB68, 0x829CDB58, 0x829CDB4C, 0x829CDB50, 0x829CDB50, 0x829CDB50, ] [[switch]] base = 0x829CFD40 r = 11 default = 0x829CFDBC labels = [ 0x829CFD68, 0x829CFD68, 0x829CFD88, 0x829CFD88, 0x829CFD88, 0x829CFD88, 0x829CFD90, 0x829CFD90, 0x829CFD90, 0x829CFDBC, 0x829CFD90, ] [[switch]] base = 0x829CFE78 r = 11 default = 0x829CFF2C labels = [ 0x829CFEA0, 0x829CFEAC, 0x829CFEB8, 0x829CFEC4, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFED0, 0x829CFEDC, 0x829CFEE8, 0x829CFEF4, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF2C, 0x829CFF00, 0x829CFF0C, 0x829CFF18, 0x829CFF24, ] [[switch]] base = 0x829D9E9C r = 4 default = 0x829D9EE0 labels = [ 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9ECC, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9ECC, 0x829D9EC4, 0x829D9ECC, 0x829D9EDC, 0x829D9EC4, 0x829D9EC4, 0x829D9ECC, 0x829D9EC4, 0x829D9ECC, 0x829D9EDC, 0x829D9EC4, 0x829D9ECC, 0x829D9EDC, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9ECC, 0x829D9ECC, 0x829D9ECC, 0x829D9ECC, 0x829D9ECC, 0x829D9ECC, 0x829D9ECC, 0x829D9ED4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, 0x829D9EC4, ] [[switch]] base = 0x829DA63C r = 11 default = 0x0 labels = [ 0x829DA680, 0x829DA688, 0x829DA690, 0x829DA690, 0x829DA694, 0x829DA690, 0x829DA690, 0x829DA694, 0x829DA694, 0x829DA694, 0x829DA678, 0x829DA694, 0x829DA670, 0x829DA670, 0x829DA694, 0x829DA670, 0x829DA670, 0x829DA694, 0x829DA694, 0x829DA690, 0x829DA694, 0x829DA694, 0x829DA694, 0x829DA690, 0x829DA694, 0x829DA694, 0x829DA694, 0x829DA678, 0x829DA690, 0x829DA694, 0x829DA688, 0x829DA694, 0x829DA694, 0x829DA694, 0x829DA694, 0x829DA690, 0x829DA664, ] [[switch]] base = 0x82A4B87C r = 3 default = 0x82A4B8E0 labels = [ 0x82A4B8A4, 0x82A4B8AC, 0x82A4B8B4, 0x82A4B8BC, 0x82A4B8C4, 0x82A4B8CC, 0x82A4B8D4, 0x82A4B8DC, ] [[switch]] base = 0x82A51660 r = 11 default = 0x82A516C0 labels = [ 0x82A516B8, 0x82A516B8, 0x82A516B8, 0x82A516D8, 0x82A516D8, 0x82A516D8, 0x82A516B8, 0x82A516D8, 0x82A516B8, 0x82A516B8, 0x82A516B8, 0x82A516C0, 0x82A516C0, 0x82A516D8, 0x82A51688, 0x82A516B0, 0x82A516B0, 0x82A516B0, 0x82A516B0, 0x82A516B0, 0x82A516B8, 0x82A516B8, 0x82A516B8, 0x82A516B0, 0x82A516B0, 0x82A516C0, 0x82A51694, 0x82A51694, ] [[switch]] base = 0x82A51954 r = 9 default = 0x82A51A14 labels = [ 0x82A519D0, 0x82A519D0, 0x82A519D0, 0x82A519FC, 0x82A5197C, 0x82A5197C, 0x82A5197C, 0x82A519B0, 0x82A5197C, 0x82A519B0, 0x82A519C0, 0x82A51A14, 0x82A51A14, 0x82A519FC, 0x82A519FC, 0x82A5197C, 0x82A5197C, 0x82A5197C, 0x82A5197C, 0x82A5197C, 0x82A519A8, 0x82A519FC, 0x82A519A8, 0x82A519FC, 0x82A519FC, ] [[switch]] base = 0x82A65798 r = 10 default = 0x82A658A8 labels = [ 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A658A8, 0x82A657DC, 0x82A657DC, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A657F4, 0x82A658A0, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A658A8, 0x82A657DC, 0x82A657DC, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A6580C, 0x82A6580C, 0x82A6580C, 0x82A658A0, 0x82A658A0, 0x82A658A8, 0x82A658A8, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A657C0, 0x82A6580C, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A65824, 0x82A65824, 0x82A658A8, 0x82A658A8, 0x82A658A8, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A0, 0x82A658A8, 0x82A65834, 0x82A65860, 0x82A658A0, 0x82A65860, 0x82A65860, 0x82A658A0, 0x82A658A0, ] [[switch]] base = 0x82A8362C r = 4 default = 0x82A83684 labels = [ 0x82A836AC, 0x82A8366C, 0x82A83674, 0x82A8367C, 0x82A836A8, 0x82A83654, 0x82A8365C, 0x82A83664, ] [[switch]] base = 0x82A975A8 r = 3 default = 0x82A97600 labels = [ 0x82A975E0, 0x82A975E0, 0x82A975D0, 0x82A975D8, 0x82A975D8, 0x82A975D8, 0x82A975E0, 0x82A975E0, 0x82A975D0, 0x82A975D0, 0x82A975D8, 0x82A975E0, 0x82A975E0, 0x82A97600, 0x82A975E0, 0x82A975D8, 0x82A975E0, 0x82A975E0, 0x82A975E8, 0x82A975F0, 0x82A975F0, 0x82A97600, 0x82A975E0, 0x82A975E0, 0x82A975D8, 0x82A975E0, 0x82A975E8, 0x82A975D8, 0x82A975E0, 0x82A975E8, 0x82A975D8, 0x82A975E0, 0x82A975E8, 0x82A975E0, 0x82A975E8, 0x82A975F0, 0x82A975E0, 0x82A975E8, 0x82A975F0, 0x82A975E0, 0x82A975E0, 0x82A975D8, 0x82A975E0, 0x82A975D0, 0x82A975E0, 0x82A975E0, 0x82A975D8, 0x82A975D8, 0x82A975E0, 0x82A975F0, 0x82A975E0, 0x82A975E8, 0x82A975F0, 0x82A975F0, 0x82A975E0, 0x82A975E0, 0x82A975E0, 0x82A975F8, 0x82A975E8, 0x82A975E8, 0x82A975E8, 0x82A975E8, ] [[switch]] base = 0x82A97614 r = 11 default = 0x82A97664 labels = [ 0x82A9763C, 0x82A97644, 0x82A97644, 0x82A97644, 0x82A9764C, 0x82A9764C, 0x82A9763C, 0x82A9763C, 0x82A97644, 0x82A97664, 0x82A97664, 0x82A97664, 0x82A9764C, 0x82A97644, 0x82A9764C, 0x82A9764C, 0x82A97664, 0x82A97664, 0x82A97664, 0x82A97664, 0x82A97664, 0x82A97664, 0x82A97644, 0x82A9764C, 0x82A97654, 0x82A97664, 0x82A97664, 0x82A97664, 0x82A97644, 0x82A9764C, 0x82A97654, 0x82A97664, 0x82A97664, 0x82A97664, 0x82A9764C, 0x82A97654, 0x82A9765C, ] [[switch]] base = 0x82A9767C r = 11 default = 0x82A976C4 labels = [ 0x82A976BC, 0x82A976A4, 0x82A976AC, 0x82A976AC, 0x82A976A4, 0x82A976A4, 0x82A976BC, 0x82A976BC, 0x82A976B4, 0x82A976C4, 0x82A976C4, 0x82A976C4, 0x82A976A4, 0x82A976A4, 0x82A976AC, 0x82A976AC, 0x82A976C4, 0x82A976C4, 0x82A976C4, 0x82A976C4, 0x82A976C4, 0x82A976C4, 0x82A976BC, 0x82A976B4, 0x82A976A4, 0x82A976C4, 0x82A976C4, 0x82A976C4, 0x82A976BC, 0x82A976B4, 0x82A976A4, 0x82A976C4, 0x82A976C4, 0x82A976C4, 0x82A976BC, 0x82A976B4, 0x82A976A4, ] [[switch]] base = 0x82AAE258 r = 11 default = 0x82AAE310 labels = [ 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE280, 0x82AAE2F0, 0x82AAE290, 0x82AAE290, 0x82AAE2C8, 0x82AAE37C, 0x82AAE280, 0x82AAE2DC, 0x82AAE2CC, 0x82AAE300, 0x82AAE2BC, ] [[switch]] base = 0x8254BA8C r = 30 default = 0x8254C0D4 labels = [ 0x8254BAB4, 0x8254BAD4, 0x8254BB00, 0x8254BB18, 0x8254BB7C, 0x8254BB98, 0x8254BBD8, 0x8254BC7C, 0x8254BD28, 0x8254BDA8, 0x8254BE50, 0x8254BEAC, 0x8254BF38, 0x8254BF98, 0x8254BFEC, 0x8254C040, 0x8254C0A4, 0x8254BC6C, 0x8254BC7C, ] [[switch]] base = 0x825CF128 r = 8 default = 0x825CF5E0 labels = [ 0x825CF150, 0x825CF198, 0x825CF1F0, 0x825CF248, 0x825CF28C, 0x825CF2D8, 0x825CF324, 0x825CF388, 0x825CF3D4, 0x825CF428, 0x825CF474, 0x825CF4C0, 0x825CF50C, 0x825CF550, 0x825CF594, ] [[switch]] base = 0x825DD358 r = 11 default = 0x825DD2F0 labels = [ 0x825DD380, 0x825DD39C, 0x825DD3A8, 0x825DD3CC, 0x825DD3E8, 0x825DD400, 0x825DD45C, 0x825DD4C4, 0x825DD4E0, 0x825DD504, 0x825DD554, 0x825DD57C, 0x825DD5EC, 0x825DD674, 0x825DD6F0, 0x825DD76C, 0x825DD7E8, 0x825DD838, 0x825DD8AC, 0x825DD8EC, 0x825DD928, 0x825DD940, 0x825DDA0C, 0x825DDA80, 0x825DDB80, 0x825DDBD0, 0x825DDBD0, 0x825DDF44, 0x825DDC30, 0x825DDCF0, 0x825DDDA8, 0x825DDDF0, 0x825DDDF0, 0x825DDE78, 0x825DDE88, ] [[switch]] base = 0x826EBB08 r = 10 default = 0x826EBAE0 labels = [ 0x826EBB4C, 0x826EB814, 0x826EBBE4, 0x826EBB30, 0x826EBB30, 0x826EBD54, 0x826EBD54, 0x826EBAE0, 0x826EBB30, 0x826EBB30, 0x826EBAE0, 0x826EBAE0, 0x826EBAE0, 0x826EBAE0, 0x826EBAE0, 0x826EBD70, ] [[switch]] base = 0x826EFA74 r = 11 default = 0x826F038C labels = [ 0x826EFC68, 0x826EFA9C, 0x826EFABC, 0x826EFB0C, 0x826EFB58, 0x826EFB60, 0x826EFB98, 0x826EFCB8, ] [[switch]] base = 0x826EFCC4 r = 11 default = 0x826F01B8 labels = [ 0x826EFEC8, 0x826F01B8, 0x826EFCEC, 0x826F01B8, 0x826EFEC8, 0x826F01B8, 0x826EFEC8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826EFDD4, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F0008, 0x826F01B8, 0x826EFD58, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826EFED4, 0x826F01B8, 0x826EFCFC, 0x826EFFF8, 0x826EFED4, 0x826EFED4, 0x826EFED4, 0x826F01B8, 0x826EFFF8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826F01B8, 0x826EFE8C, 0x826F0040, 0x826F0004, 0x826F01B8, 0x826F01B8, 0x826EFDE4, 0x826F01B8, 0x826EFFFC, 0x826F01B8, 0x826F01B8, 0x826F0010, ] [[switch]] base = 0x826F1A10 r = 11 default = 0x826F23C4 labels = [ 0x826F1BEC, 0x826F1A38, 0x826F1A58, 0x826F1AA8, 0x826F1AE4, 0x826F1AEC, 0x826F1B24, 0x826F1C08, ] [[switch]] base = 0x826F1C14 r = 11 default = 0x826F2124 labels = [ 0x826F1E44, 0x826F2124, 0x826F1C3C, 0x826F2124, 0x826F1E44, 0x826F2124, 0x826F1E44, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F1D30, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F1F88, 0x826F2124, 0x826F1CB0, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F1E50, 0x826F2124, 0x826F1C4C, 0x826F1F78, 0x826F1E50, 0x826F1E50, 0x826F1E50, 0x826F2124, 0x826F1F78, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F2124, 0x826F1E08, 0x826F1FBC, 0x826F1F84, 0x826F2124, 0x826F2124, 0x826F1D40, 0x826F2124, 0x826F1F7C, 0x826F2124, 0x826F2124, 0x826F1F90, ] [[switch]] base = 0x826F37FC r = 11 default = 0x826F43C4 labels = [ 0x826F3824, 0x826F3CC0, 0x826F3FEC, 0x826F3FEC, 0x826F3FEC, 0x826F43C4, 0x826F3B60, 0x826F43C4, 0x826F43C4, 0x826F43C4, 0x826F43C4, 0x826F3F98, 0x826F3CC0, 0x826F3CBC, 0x826F43C4, 0x826F43C4, 0x826F3834, 0x826F43C4, 0x826F3CC0, 0x826F43C4, 0x826F43C4, 0x826F3B64, 0x826F43C4, 0x826F43C4, 0x826F3844, ] [[switch]] base = 0x826F6500 r = 11 default = 0x826F6E18 labels = [ 0x826F66F4, 0x826F6528, 0x826F6548, 0x826F6598, 0x826F65E4, 0x826F65EC, 0x826F6624, 0x826F6744, ] [[switch]] base = 0x826F6750 r = 11 default = 0x826F6C44 labels = [ 0x826F6954, 0x826F6C44, 0x826F6778, 0x826F6C44, 0x826F6954, 0x826F6C44, 0x826F6954, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6860, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6A94, 0x826F6C44, 0x826F67E4, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6960, 0x826F6C44, 0x826F6788, 0x826F6A84, 0x826F6960, 0x826F6960, 0x826F6960, 0x826F6C44, 0x826F6A84, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6C44, 0x826F6918, 0x826F6ACC, 0x826F6A90, 0x826F6C44, 0x826F6C44, 0x826F6870, 0x826F6C44, 0x826F6A88, 0x826F6C44, 0x826F6C44, 0x826F6A9C, ] [[switch]] base = 0x82722608 r = 10 default = 0x82722930 labels = [ 0x82722630, 0x827226BC, 0x82722950, 0x827229A0, 0x827229F8, 0x82722A4C, 0x82722AAC, 0x82722734, 0x827227B4, 0x82722804, 0x8272285C, 0x827228B4, 0x82722AD4, 0x82722ADC, ] [[switch]] base = 0x8272C1F8 r = 10 default = 0x8272C8BC labels = [ 0x8272C244, 0x8272C33C, 0x8272C390, 0x8272C4D0, 0x8272C594, 0x8272C774, 0x8272C818, 0x8272C990, 0x8272C9C0, 0x8272C928, ] [[switch]] base = 0x8272E1F0 r = 9 default = 0x8272E714 labels = [ 0x8272E218, 0x8272E2DC, 0x8272E3A8, 0x8272E414, 0x8272E4A8, 0x8272E504, 0x8272E634, 0x8272E77C, 0x8272E7BC, 0x8272E774, ] [[switch]] base = 0x82839C70 r = 11 default = 0x8283A108 labels = [ 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x82839C98, 0x82839CBC, 0x82839D34, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x82839DAC, 0x8283A0EC, 0x8283A0EC, 0x82839E6C, 0x82839EE4, 0x82839F5C, 0x8283A0EC, 0x8283A0EC, 0x8283A108, 0x8283A0EC, 0x82839FFC, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, 0x8283A074, 0x8283A0B0, 0x8283A108, 0x8283A0EC, 0x8283A0EC, 0x8283A0EC, ] [[switch]] base = 0x8283A300 r = 11 default = 0x8283AAF4 labels = [ 0x8283A328, 0x8283A348, 0x8283A784, 0x8283A368, 0x8283A380, 0x8283A3D8, 0x8283A448, 0x8283A4B4, 0x8283A4F0, 0x8283A510, 0x8283A5E0, 0x8283A5FC, 0x8283A674, 0x8283A690, 0x8283A6AC, 0x8283A718, 0x8283A7A8, 0x8283A80C, 0x8283A830, 0x8283A850, 0x8283A864, 0x8283AA04, 0x8283A870, 0x8283A8A4, 0x8283A8E0, 0x8283A91C, 0x8283A958, 0x8283A98C, 0x8283AAF4, 0x8283AAF4, 0x8283A9C8, 0x8283A9DC, 0x8283A9E8, 0x8283AAA4, ] [[switch]] base = 0x82884554 r = 11 default = 0x828852A8 labels = [ 0x82885468, 0x82884F58, 0x82884F58, 0x82884F58, 0x82884F58, 0x82884F04, 0x82884F14, 0x82884F30, 0x828852A8, 0x8288506C, 0x828850D8, 0x82885150, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828849C8, 0x828849C8, 0x828849C8, 0x8288457C, 0x828845E8, 0x82884640, 0x82884690, 0x828846E8, 0x82884738, 0x82884790, 0x828847E8, 0x82884848, 0x828848A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x82884908, 0x82884908, 0x82884908, 0x82884950, 0x82884950, 0x82884950, 0x82884DA4, 0x82884DA4, 0x82884DA4, 0x82884CF8, 0x82884CF8, 0x82884CF8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885368, 0x82885308, 0x82885310, 0x82885324, 0x82885340, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828852A8, 0x828851D8, 0x828851D8, 0x828851D8, 0x828851D8, 0x828851F4, 0x828851F4, 0x828851F4, 0x828851F4, 0x8288521C, 0x8288521C, 0x8288521C, 0x8288521C, ] [[switch]] base = 0x828934A8 r = 10 default = 0x828953D4 labels = [ 0x828953B0, 0x82893FE8, 0x828953D4, 0x8289521C, 0x8289390C, 0x828934D0, 0x828942A4, 0x82894C08, 0x82894920, 0x82893DE8, 0x82893B64, 0x8289402C, 0x82894110, 0x82894A5C, 0x82894420, 0x82894500, 0x8289463C, 0x828950F8, 0x82895164, 0x828950A8, 0x828950D8, 0x82894F1C, 0x828953D4, 0x82895228, 0x82895298, 0x828952F8, ] [[switch]] base = 0x82895694 r = 10 default = 0x828977A0 labels = [ 0x8289777C, 0x82896234, 0x828977A0, 0x828974F4, 0x82895B24, 0x828956BC, 0x828964F4, 0x82896E74, 0x82896B88, 0x8289601C, 0x82895D80, 0x82896278, 0x8289635C, 0x82896CC4, 0x82896680, 0x82896760, 0x828968A0, 0x82897384, 0x82897408, 0x82897338, 0x82897368, 0x8289719C, 0x828977A0, 0x82897500, 0x828975DC, 0x828976A4, ] [[switch]] base = 0x82897CC8 r = 4 default = 0x82897CB8 labels = [ 0x828989EC, 0x82897C38, 0x82897CB8, 0x828989E0, 0x82897DF0, 0x82897CF0, 0x82898124, 0x82897F64, 0x82897ECC, 0x8289824C, 0x82898380, 0x82898098, 0x82898018, 0x82897DF0, 0x82898018, 0x82897DF0, 0x82897DF0, 0x828989DC, 0x82897E98, 0x828989E0, 0x828989E0, 0x828981B0, 0x82898018, 0x82898514, 0x82898618, 0x82898728, ] [[switch]] base = 0x828CDD48 r = 11 default = 0x828CE53C labels = [ 0x828CDE74, 0x828CDEF8, 0x828CDF7C, 0x828CDFFC, 0x828CE07C, 0x828CE100, 0x828CE184, 0x828CE204, 0x828CE284, 0x828CE300, 0x828CE378, 0x828CE388, 0x828CE53C, 0x828CE404, 0x828CE414, 0x828CE434, 0x828CE51C, ] [[switch]] base = 0x8291A1C4 r = 28 default = 0x8291A600 labels = [ 0x8291A20C, 0x8291A224, 0x8291A238, 0x8291A268, 0x8291A2B0, 0x8291A2BC, 0x8291A2C8, 0x8291A2DC, 0x8291A2FC, 0x8291A324, 0x8291A338, 0x8291A344, 0x8291A350, 0x8291A36C, 0x8291A37C, 0x8291A388, 0x8291A388, 0x8291A390, 0x8291A3B4, 0x8291A388, 0x8291A3E4, 0x8291A400, 0x8291A388, 0x8291A388, 0x8291A410, 0x8291A428, 0x8291A388, 0x8291A474, 0x8291A48C, 0x8291A388, 0x8291A4A4, 0x8291A4C4, 0x8291A4DC, 0x8291A4FC, 0x8291A388, 0x8291A514, 0x8291A52C, 0x8291A388, 0x8291A550, 0x8291A388, 0x8291A580, 0x8291A388, 0x8291A5AC, 0x8291A388, 0x8291A5CC, 0x8291A5CC, 0x8291A5CC, ] [[switch]] base = 0x82926144 r = 4 default = 0x829266D8 labels = [ 0x829261C4, 0x82926190, 0x82926190, 0x82926198, 0x82926190, 0x82926190, 0x829261C4, 0x829261AC, 0x829261CC, 0x829261E0, 0x82926190, 0x82926224, 0x8292626C, 0x8292626C, 0x8292626C, 0x8292626C, 0x8292626C, 0x8292626C, 0x8292626C, 0x8292626C, 0x8292626C, 0x829262BC, 0x829262BC, 0x82926190, 0x829262F8, 0x82926190, 0x82926348, 0x82926374, 0x82926404, 0x82926190, 0x82926474, 0x829266D8, 0x829266D8, 0x829264C4, 0x829264DC, 0x82926190, 0x829264E4, 0x82926534, 0x82926564, 0x82926588, 0x829265CC, 0x82926190, 0x82926190, 0x82926610, 0x82926624, 0x82926624, 0x82926648, 0x82926190, 0x82926190, 0x8292666C, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x82926678, 0x829266A4, 0x829266A4, 0x829266A4, ] [[switch]] base = 0x82932418 r = 11 default = 0x82938F8C labels = [ 0x82932440, 0x829324FC, 0x82932558, 0x82932890, 0x8293291C, 0x82932964, 0x829329C0, 0x82932A10, 0x82932B24, 0x82932BF0, 0x82932C14, 0x82932C70, 0x82932F28, 0x8293308C, 0x829330E4, 0x82933134, 0x829331DC, 0x82933B10, 0x82933C90, 0x82933CF0, 0x82933D98, 0x82933ECC, 0x82933F14, 0x8293413C, 0x82934220, 0x8293445C, 0x829344AC, 0x829348F0, 0x82934B7C, 0x82934E2C, 0x829350AC, 0x82935354, 0x82935408, 0x82935504, 0x82935554, 0x8293581C, 0x8293593C, 0x82935A40, 0x82935A84, 0x82935ADC, 0x82935B34, 0x82935F98, 0x82935F98, 0x82935F98, 0x82936030, 0x829360B8, 0x829360B8, 0x82936030, 0x829360B8, 0x829360B8, 0x829366EC, 0x82936724, 0x82936938, 0x82936978, 0x82936A10, 0x82936BB0, 0x8293704C, 0x8293721C, 0x82937260, 0x82937374, 0x8293755C, 0x829375A4, 0x82937644, 0x8293794C, 0x82937C7C, 0x82937D34, 0x82937D98, 0x82937EE0, 0x82938294, 0x82938400, 0x829384B4, 0x82938530, 0x829385AC, 0x82938628, 0x829386A0, 0x82938778, 0x829387F4, 0x82938870, 0x829388EC, 0x82938964, 0x82938A3C, 0x82938AB8, 0x82938B34, 0x82938BB0, 0x82938C28, 0x82938D00, 0x82938D7C, 0x82938DF8, 0x82938E74, 0x82938EF8, ] [[switch]] base = 0x8293BD90 r = 10 default = 0x8293D84C labels = [ 0x8293BDB8, 0x8293BDB8, 0x8293BFF4, 0x8293C048, 0x8293C08C, 0x8293C190, 0x8293C1DC, 0x8293CA94, 0x8293CAE0, 0x8293CB94, 0x8293C220, 0x8293C274, 0x8293C328, 0x8293C374, 0x8293C3CC, 0x8293C41C, 0x8293C470, 0x8293C5AC, 0x8293C6E8, 0x8293C8C0, 0x8293BEB4, 0x8293BFA0, 0x8293CDD8, 0x8293CE0C, 0x8293CE9C, 0x8293D0B8, 0x8293D124, ] [[switch]] base = 0x82944F00 r = 9 default = 0x82944F60 labels = [ 0x82944F68, 0x82944F70, 0x82944F78, 0x82944F80, 0x82944F88, 0x82944F90, 0x82944F98, 0x82944FA0, 0x82944FA8, 0x82944FB0, 0x82944FB8, 0x82944FC4, 0x82944FCC, 0x82944FD4, 0x82944FDC, ] [[switch]] base = 0x8294B7CC r = 11 default = 0x8294BC6C labels = [ 0x8294B7F4, 0x8294B83C, 0x8294B870, 0x8294B870, 0x8294BC6C, 0x8294BC6C, 0x8294B8DC, 0x8294B974, 0x8294BA40, 0x8294BC6C, 0x8294B9E8, 0x8294BAAC, 0x8294B870, 0x8294B870, ] [[switch]] base = 0x8294C490 r = 4 default = 0x8294CC50 labels = [ 0x8294C5A8, 0x8294C4DC, 0x8294C4DC, 0x8294C4E4, 0x8294C4DC, 0x8294C4DC, 0x8294C4F8, 0x8294C5A8, 0x8294C590, 0x8294C5B0, 0x8294C5C4, 0x8294C4DC, 0x8294C5D0, 0x8294C4DC, 0x8294C5E4, 0x8294C5F4, 0x8294C5F4, 0x8294C5F4, 0x8294C5F4, 0x8294C5F4, 0x8294C5F4, 0x8294C5F4, 0x8294C5F4, 0x8294C5F4, 0x8294C648, 0x8294C648, 0x8294C684, 0x8294C694, 0x8294C6A4, 0x8294C6B4, 0x8294C6CC, 0x8294C5F4, 0x8294C6FC, 0x8294C5F4, 0x8294C6FC, 0x8294C5F4, 0x8294C684, 0x8294C6A4, 0x8294C6CC, 0x8294C648, 0x8294C6DC, 0x8294C768, 0x8294C798, 0x8294C7C4, 0x8294C7EC, 0x8294C878, 0x8294C4DC, 0x8294C89C, 0x8294C4DC, 0x8294C4DC, 0x8294C4DC, 0x8294C8E4, 0x8294C4DC, 0x8294C934, 0x8294C960, 0x8294C98C, 0x8294C4DC, 0x8294C9D4, 0x8294CC50, 0x8294CC50, 0x8294CA24, 0x8294CA3C, 0x8294C4DC, 0x8294CA44, 0x8294CAA4, 0x8294CAD4, 0x8294CAF8, 0x8294CB3C, 0x8294C4DC, 0x8294C4DC, 0x8294CB80, 0x8294CB94, 0x8294CB94, 0x8294CBB8, 0x8294C4DC, 0x8294C4DC, 0x8294CBDC, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CC50, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CBE8, 0x8294CC1C, 0x8294CC1C, 0x8294CC1C, ] [[switch]] base = 0x8294CE3C r = 11 default = 0x8294D290 labels = [ 0x8294CE64, 0x8294CE6C, 0x8294CE78, 0x8294CE84, 0x8294CE90, 0x8294CE9C, 0x8294CEA8, 0x8294CEB4, 0x8294CEBC, 0x8294CEC8, 0x8294CED4, 0x8294CEE0, 0x8294CEEC, 0x8294CEF8, 0x8294CF04, 0x8294CF10, 0x8294CF1C, 0x8294CF28, 0x8294CF34, 0x8294CF40, 0x8294CF4C, 0x8294CF58, 0x8294CF64, 0x8294CF70, 0x8294CF7C, 0x8294CF88, 0x8294CF94, 0x8294CFA0, 0x8294CFAC, 0x8294CFB8, 0x8294CFC4, 0x8294CFD0, 0x8294CFDC, 0x8294CFE8, 0x8294CFF4, 0x8294D000, 0x8294D00C, 0x8294D018, 0x8294D024, 0x8294D030, 0x8294D03C, 0x8294D048, 0x8294D054, 0x8294D060, 0x8294D06C, 0x8294D078, 0x8294D084, 0x8294D090, 0x8294D09C, 0x8294D0A8, 0x8294D0B4, 0x8294D0C0, 0x8294D0CC, 0x8294D0D8, 0x8294D0E4, 0x8294D0F0, 0x8294D0FC, 0x8294D108, 0x8294D114, 0x8294D120, 0x8294D12C, 0x8294D138, 0x8294D144, 0x8294D150, 0x8294D15C, 0x8294D164, 0x8294D16C, 0x8294D178, 0x8294D184, 0x8294D190, 0x8294D19C, 0x8294D1A8, 0x8294D1B4, 0x8294D1C0, 0x8294D1CC, 0x8294D1D8, 0x8294D1E0, 0x8294D1E8, 0x8294D1F0, 0x8294D1F8, 0x8294D200, 0x8294D208, 0x8294D210, 0x8294D218, 0x8294D220, 0x8294D228, 0x8294D230, 0x8294D238, 0x8294D240, 0x8294D240, 0x8294D248, 0x8294D250, 0x8294D258, 0x8294D260, 0x8294D268, 0x8294D270, 0x8294D278, 0x8294D280, ] [[switch]] base = 0x82954EA4 r = 30 default = 0x82955510 labels = [ 0x82955508, 0x82954ECC, 0x82954ED4, 0x82954EDC, 0x82954EE4, 0x82954EEC, 0x82954EF4, 0x82954EFC, 0x82954F04, 0x82954F0C, 0x82954F14, 0x82954F1C, 0x82954F24, 0x82954F2C, 0x82954F34, 0x82955510, 0x82955510, 0x82954F3C, 0x82954F44, ] [[switch]] base = 0x8298F460 r = 11 default = 0x829909A8 labels = [ 0x8298F494, 0x8298F4C8, 0x8298F53C, 0x8298F584, 0x8298F5D4, 0x8298F648, 0x8298F67C, 0x8298F6BC, 0x8298F6F0, 0x829909A8, 0x8298F750, 0x8298F784, 0x8298F7B8, 0x8298F818, 0x8298F844, 0x8298F870, 0x8298F8AC, 0x8298FA08, 0x8298FA50, 0x8298FA9C, 0x8298FAD0, 0x8298FB04, 0x8298FB44, 0x8298FBC0, 0x8298FBF4, 0x8298FC34, 0x829909A8, 0x8298FC90, 0x8298FCBC, 0x8298FD14, 0x8298FD6C, 0x8298FDC4, 0x8298FE14, 0x8298FE48, 0x8298FE9C, 0x8298FEF4, 0x8298FF58, 0x8298FFD4, 0x82990050, 0x82990098, 0x829909A8, 0x829900E0, 0x829900E0, 0x829900E0, 0x8299011C, 0x82990158, 0x82990158, 0x8299011C, 0x82990158, 0x82990158, 0x829909A8, 0x8299023C, 0x829902C0, 0x8299032C, 0x82990368, 0x829903E8, 0x829904B4, 0x829904F4, 0x8299058C, 0x829905EC, 0x82990654, 0x829909A8, 0x82990688, 0x829906BC, 0x82990764, 0x829907C8, 0x82990828, 0x8299085C, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x829909A8, 0x82990890, 0x8299090C, ] [[switch]] base = 0x829969EC r = 11 default = 0x82999E48 labels = [ 0x82996A14, 0x82996A1C, 0x82996BC0, 0x82996BC8, 0x82996BD0, 0x82996BD8, 0x82996BE0, 0x82996BE8, 0x82996BF0, 0x82996BF8, 0x82999E34, 0x82999E48, 0x82999E48, 0x82999E40, ] [[switch]] base = 0x82996C28 r = 11 default = 0x82999CD0 labels = [ 0x82996C50, 0x82996D5C, 0x82996E68, 0x82997418, 0x82997734, 0x82997948, 0x82997BE0, 0x82997C68, 0x82997CF0, 0x82999CD0, 0x82999CD0, 0x82997F88, 0x82998010, 0x8299811C, 0x82998228, 0x82998334, 0x82999CD0, 0x829985DC, 0x829987F0, 0x829990C8, 0x8299970C, 0x82999920, 0x82999C4C, ] [[switch]] base = 0x8299BB54 r = 27 default = 0x8299D29C labels = [ 0x8299BB98, 0x8299BBDC, 0x8299BC58, 0x8299BC2C, 0x8299CA6C, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC48, 0x8299BC60, 0x8299BC88, 0x8299BCA4, 0x8299BCC0, 0x8299BCDC, 0x8299BCF8, 0x8299BC58, 0x8299BD14, 0x8299BC58, 0x8299BC48, 0x8299BD3C, 0x8299BD58, 0x8299BD74, 0x8299BD90, 0x8299BDE8, 0x8299D29C, 0x8299D29C, 0x8299BE00, 0x8299BE2C, 0x8299BE5C, 0x8299BE70, 0x8299BC58, 0x8299BEAC, 0x8299BED8, 0x8299BC58, 0x8299BC58, 0x8299BF24, 0x8299BF8C, 0x8299BC58, 0x8299BFFC, 0x8299BC58, 0x8299C010, 0x8299CA6C, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299C024, 0x8299C040, 0x8299C070, 0x8299C08C, 0x8299C0A8, 0x8299C0C4, 0x8299C0E0, 0x8299C104, 0x8299C118, 0x8299C140, 0x8299C158, 0x8299C178, 0x8299C194, 0x8299C1B0, 0x8299C1CC, 0x8299C1E8, 0x8299C204, 0x8299C220, 0x8299C23C, 0x8299C258, 0x8299C274, 0x8299C290, 0x8299C2AC, 0x8299C2C8, 0x8299C2E4, 0x8299C300, 0x8299CA6C, 0x8299BE54, 0x8299C31C, 0x8299C330, 0x8299BC58, 0x8299C338, 0x8299C348, 0x8299BC58, 0x8299BC2C, 0x8299C360, 0x8299C388, 0x8299C3C8, 0x8299C3D8, 0x8299C3D8, 0x8299C3D8, 0x8299BC58, 0x8299C3F8, 0x8299CA6C, 0x8299BC58, 0x8299C408, 0x8299C42C, 0x8299C450, 0x8299C478, 0x8299CA6C, 0x8299BE54, 0x8299CA6C, 0x8299BC58, 0x8299C4C0, 0x8299BC58, 0x8299BC2C, 0x8299C3C8, 0x8299C4C8, 0x8299C4F4, 0x8299C520, 0x8299C540, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299CA6C, 0x8299BC58, 0x8299C560, 0x8299C584, 0x8299BC58, 0x8299BC2C, 0x8299BC58, 0x8299BC2C, 0x8299C59C, 0x8299BC58, 0x8299C5B8, 0x8299C5D8, 0x8299C6B4, 0x8299C610, 0x8299C5F8, 0x8299C708, 0x8299CA6C, 0x8299CA6C, 0x8299BE54, 0x8299C730, 0x8299CA6C, 0x8299BC58, 0x8299BC2C, 0x8299C560, 0x8299C73C, 0x8299C758, 0x8299D29C, 0x8299D29C, 0x8299BC58, 0x8299BC48, 0x8299C78C, 0x8299C7DC, 0x8299C830, 0x8299C84C, 0x8299C868, 0x8299C884, 0x8299BC58, 0x8299C8A0, 0x8299C928, 0x8299C9B0, 0x8299CA6C, 0x8299BE54, 0x8299CA5C, 0x8299C3C8, 0x8299BC58, 0x8299BC2C, 0x8299CA74, 0x8299CA6C, 0x8299BE54, 0x8299CAE8, 0x8299CB04, 0x8299BC58, 0x8299BC2C, 0x8299CB0C, 0x8299CA6C, 0x8299BC58, 0x8299CBA8, 0x8299CAFC, 0x8299CA6C, 0x8299BE54, 0x8299CBB0, 0x8299C3C8, 0x8299BC58, 0x8299BC2C, 0x8299CA6C, 0x8299CBB8, 0x8299CBCC, 0x8299CBDC, 0x8299CBE4, 0x8299CC00, 0x8299CC20, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299CC2C, 0x8299CC3C, 0x8299BC58, 0x8299CC5C, 0x8299BC58, 0x8299CC2C, 0x8299CC3C, 0x8299CC7C, 0x8299CC94, 0x8299CC9C, 0x8299CA6C, 0x8299CBB8, 0x8299BC58, 0x8299CA6C, 0x8299BC58, 0x8299CA6C, 0x8299BC58, 0x8299CCA4, 0x8299CCC4, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299CCE4, 0x8299CDAC, 0x8299CEAC, 0x8299CEAC, 0x8299CEBC, 0x8299CECC, 0x8299BC58, 0x8299CEE0, 0x8299CEE0, 0x8299CEFC, 0x8299CF14, 0x8299CA6C, 0x8299CF28, 0x8299CF38, 0x8299CF38, 0x8299CF40, 0x8299CF5C, 0x8299CF6C, 0x8299CF74, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299CF84, 0x8299CF98, 0x8299CFAC, 0x8299CFC8, 0x8299BC58, 0x8299CFD0, 0x8299CFD8, 0x8299CFE0, 0x8299CFE8, 0x8299CFF0, 0x8299BC58, 0x8299CFF8, 0x8299BC58, 0x8299D000, 0x8299D00C, 0x8299D018, 0x8299BC58, 0x8299D024, 0x8299D030, 0x8299BC58, 0x8299D03C, 0x8299D048, 0x8299D054, 0x8299D060, 0x8299BC58, 0x8299D06C, 0x8299D078, 0x8299BC58, 0x8299D084, 0x8299BC58, 0x8299D090, 0x8299BC58, 0x8299D0B4, 0x8299D0C0, 0x8299D0CC, 0x8299D0D8, 0x8299D0E4, 0x8299D0F0, 0x8299BC58, 0x8299D09C, 0x8299D0FC, 0x8299D11C, 0x8299CA6C, 0x8299BC58, 0x8299D16C, 0x8299BC58, 0x8299BC58, 0x8299BC58, 0x8299BC2C, 0x8299BC58, 0x8299BC58, 0x8299D18C, 0x8299BC58, 0x8299D1E0, 0x8299BC58, 0x8299D1F8, 0x8299BC58, 0x8299D210, 0x8299BC58, 0x8299D224, 0x8299D224, 0x8299D224, 0x8299D244, 0x8299D244, 0x8299CA6C, 0x8299BC58, 0x8299BC58, 0x8299D224, 0x8299D224, 0x8299D224, 0x8299D224, 0x8299D224, 0x8299D224, 0x8299D224, 0x8299BC58, 0x8299D258, 0x8299D224, 0x8299D224, 0x8299D224, 0x8299D26C, 0x8299D284, 0x8299D294, ] [[switch]] base = 0x8299D474 r = 11 default = 0x8299E1F4 labels = [ 0x8299D49C, 0x8299D4A4, 0x8299D4B0, 0x8299D4BC, 0x8299D4C8, 0x8299D4D0, 0x8299D4DC, 0x8299D4E8, 0x8299D4F4, 0x8299D500, 0x8299D50C, 0x8299D518, 0x8299D524, 0x8299D530, 0x8299D538, 0x8299D540, 0x8299D548, 0x8299D550, 0x8299D558, 0x8299D560, 0x8299D56C, 0x8299D578, 0x8299D584, 0x8299D590, 0x8299D598, 0x8299D5A0, 0x8299D5A8, 0x8299D5B4, 0x8299D5C0, 0x8299D5CC, 0x8299D5D8, 0x8299D5E4, 0x8299D5F0, 0x8299D5FC, 0x8299D608, 0x8299D614, 0x8299D620, 0x8299D62C, 0x8299D638, 0x8299D644, 0x8299D650, 0x8299D65C, 0x8299D668, 0x8299D674, 0x8299D67C, 0x8299D688, 0x8299D694, 0x8299D6A0, 0x8299D6AC, 0x8299D6B4, 0x8299D6BC, 0x8299D6C4, 0x8299D6CC, 0x8299D6D4, 0x8299D6DC, 0x8299D6E8, 0x8299D6F0, 0x8299D6FC, 0x8299D704, 0x8299D70C, 0x8299D714, 0x8299D71C, 0x8299D724, 0x8299D72C, 0x8299D734, 0x8299D73C, 0x8299D744, 0x8299D74C, 0x8299D754, 0x8299D75C, 0x8299D764, 0x8299D76C, 0x8299D774, 0x8299D77C, 0x8299D788, 0x8299D794, 0x8299D7A0, 0x8299D7AC, 0x8299D7B8, 0x8299D7C4, 0x8299D7CC, 0x8299D7D8, 0x8299D7E4, 0x8299D7F0, 0x8299D7FC, 0x8299D804, 0x8299D810, 0x8299D81C, 0x8299D828, 0x8299D834, 0x8299D840, 0x8299D848, 0x8299D854, 0x8299D860, 0x8299D86C, 0x8299D878, 0x8299D884, 0x8299D890, 0x8299D89C, 0x8299D8A4, 0x8299D8B0, 0x8299D8B8, 0x8299D8C4, 0x8299D8D0, 0x8299D8D8, 0x8299D8E4, 0x8299D8F0, 0x8299D8FC, 0x8299D908, 0x8299D914, 0x8299D920, 0x8299D92C, 0x8299D934, 0x8299D940, 0x8299D94C, 0x8299D958, 0x8299D964, 0x8299D970, 0x8299D97C, 0x8299D988, 0x8299D994, 0x8299D9A0, 0x8299D9AC, 0x8299D9B8, 0x8299D9C4, 0x8299D9D0, 0x8299D9DC, 0x8299D9E8, 0x8299D9F4, 0x8299DA00, 0x8299DA0C, 0x8299DA14, 0x8299DA1C, 0x8299DA28, 0x8299DA34, 0x8299DA40, 0x8299DA4C, 0x8299DA58, 0x8299DA64, 0x8299DA70, 0x8299DA7C, 0x8299DA88, 0x8299DA90, 0x8299DA98, 0x8299DAA0, 0x8299DAA8, 0x8299DAB4, 0x8299DAC0, 0x8299DACC, 0x8299DAD8, 0x8299DAE4, 0x8299DAF0, 0x8299DAF8, 0x8299DB00, 0x8299DB0C, 0x8299DB18, 0x8299DB24, 0x8299DB30, 0x8299DB3C, 0x8299DB44, 0x8299DB4C, 0x8299DB58, 0x8299DB64, 0x8299DB70, 0x8299DB78, 0x8299DB84, 0x8299DB8C, 0x8299DB94, 0x8299DBA0, 0x8299DBAC, 0x8299DBB4, 0x8299DBBC, 0x8299DBC8, 0x8299DBD4, 0x8299DBDC, 0x8299DBE8, 0x8299DBF0, 0x8299DBFC, 0x8299DC08, 0x8299DC14, 0x8299DC1C, 0x8299DC28, 0x8299DC34, 0x8299DC40, 0x8299DC4C, 0x8299DC58, 0x8299DC64, 0x8299DC70, 0x8299DC7C, 0x8299DC88, 0x8299DC94, 0x8299DCA0, 0x8299DCAC, 0x8299DCB8, 0x8299DCC0, 0x8299DCC8, 0x8299DCD4, 0x8299DCE0, 0x8299DCE8, 0x8299DCF4, 0x8299DCFC, 0x8299DD08, 0x8299DD14, 0x8299DD20, 0x8299DD2C, 0x8299DD38, 0x8299DD44, 0x8299DD50, 0x8299DD5C, 0x8299DD64, 0x8299DD6C, 0x8299DD78, 0x8299DD84, 0x8299DD90, 0x8299DD9C, 0x8299DDA8, 0x8299DDB4, 0x8299DDC0, 0x8299DDCC, 0x8299DDD8, 0x8299DDE4, 0x8299DDF0, 0x8299DDFC, 0x8299DE08, 0x8299DE14, 0x8299DE20, 0x8299DE2C, 0x8299DE38, 0x8299DE44, 0x8299DE50, 0x8299DE5C, 0x8299DE68, 0x8299DE74, 0x8299DE80, 0x8299DE8C, 0x8299DE98, 0x8299DEA4, 0x8299DEB0, 0x8299DEBC, 0x8299DEC8, 0x8299DED4, 0x8299DEE0, 0x8299DEEC, 0x8299DEF8, 0x8299DF04, 0x8299DF10, 0x8299DF1C, 0x8299DF28, 0x8299DF34, 0x8299DF40, 0x8299DF4C, 0x8299DF58, 0x8299DF64, 0x8299DF70, 0x8299DF7C, 0x8299DF88, 0x8299DF94, 0x8299DFA0, 0x8299DFAC, 0x8299DFB8, 0x8299DFC4, 0x8299DFD0, 0x8299DFDC, 0x8299DFE8, 0x8299DFF4, 0x8299E000, 0x8299E00C, 0x8299E018, 0x8299E024, 0x8299E030, 0x8299E03C, 0x8299E048, 0x8299E054, 0x8299E05C, 0x8299E068, 0x8299E074, 0x8299E080, 0x8299E08C, 0x8299E098, 0x8299E0A4, 0x8299E0B0, 0x8299E0BC, 0x8299E0C8, 0x8299E0D4, 0x8299E0E0, 0x8299E0EC, 0x8299E0F8, 0x8299E104, 0x8299E110, 0x8299E11C, 0x8299E124, 0x8299E12C, 0x8299E134, 0x8299E140, 0x8299E14C, 0x8299E154, 0x8299E160, 0x8299E16C, 0x8299E174, 0x8299E17C, 0x8299E184, 0x8299E18C, 0x8299E194, 0x8299E19C, 0x8299E1A4, 0x8299E1B0, 0x8299E1BC, 0x8299E1C4, 0x8299E1CC, 0x8299E1D4, 0x8299E1DC, 0x8299E1E4, ] [[switch]] base = 0x829CA8C8 r = 11 default = 0x829CAD08 labels = [ 0x829CA8F0, 0x829CA958, 0x829CAB30, 0x829CAB20, 0x829CAB20, 0x829CABBC, 0x829CAD08, 0x829CAD08, 0x829CAD08, 0x829CAD08, 0x829CAD08, 0x829CAD08, 0x829CAD08, 0x829CAB5C, 0x829CAB20, 0x829CAC20, 0x829CAD08, 0x829CAA88, 0x829CAAD8, 0x829CAB20, ] [[switch]] base = 0x829D0BD0 r = 29 default = 0x829D12E4 labels = [ 0x829D0BF8, 0x829D0C54, 0x829D0C28, 0x829D0DA0, 0x829D0DC8, 0x829D0DE4, 0x829D0E00, 0x829D0E1C, 0x829D0F04, 0x829D1044, 0x829D10A0, 0x829D10BC, 0x829D1214, ] [[switch]] base = 0x829D80B0 r = 10 default = 0x829D89BC labels = [ 0x829D8424, 0x829D8508, 0x829D8340, 0x829D844C, 0x829D8488, 0x829D83E0, 0x829D89BC, 0x829D89BC, 0x829D89BC, 0x829D89BC, 0x829D89BC, 0x829D89BC, 0x829D89BC, 0x829D8494, 0x829D8488, 0x829D80D8, 0x829D86D8, 0x829D8270, 0x829D8314, 0x829D86D0, 0x829D880C, ] [[switch]] base = 0x829D90AC r = 11 default = 0x829D95E0 labels = [ 0x829D95E0, 0x829D90D4, 0x829D9130, 0x829D913C, 0x829D9148, 0x829D9154, 0x829D9160, 0x829D916C, 0x829D9178, 0x829D9184, 0x829D9190, 0x829D919C, 0x829D91A8, 0x829D91B8, 0x829D91F0, 0x829D9208, 0x829D91C0, 0x829D91CC, 0x829D948C, 0x829D91FC, 0x829D9214, 0x829D9220, 0x829D9230, 0x829D9238, 0x829D9240, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D92F8, 0x829D9498, 0x829D94A4, 0x829D94B0, 0x829D94DC, 0x829D94E8, 0x829D94B0, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9464, 0x829D9248, 0x829D9454, 0x829D945C, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D91D8, 0x829D91E4, 0x829D95E0, 0x829D945C, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D95E0, 0x829D9258, 0x829D92C0, 0x829D9464, ] [[switch]] base = 0x829F4C5C r = 11 default = 0x829F51A0 labels = [ 0x829F4C84, 0x829F4C84, 0x829F500C, 0x829F500C, 0x829F5054, 0x829F505C, 0x829F5064, 0x829F506C, 0x829F50F8, 0x829F5100, 0x829F5108, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F51A0, 0x829F5074, 0x829F50E0, 0x829F50E8, 0x829F50F0, ] [[switch]] base = 0x82A15A34 r = 11 default = 0x82A1673C labels = [ 0x82A15A5C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A15B18, 0x82A15B18, 0x82A15B18, 0x82A1673C, 0x82A15C80, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A15DD0, 0x82A160E0, 0x82A15DD8, 0x82A160E8, 0x82A162C4, 0x82A15DE0, 0x82A15DE8, 0x82A1633C, 0x82A16344, 0x82A1634C, 0x82A16354, 0x82A165F8, 0x82A16600, 0x82A16608, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A15DD0, 0x82A160E0, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1673C, 0x82A1635C, 0x82A16364, 0x82A1636C, 0x82A1636C, 0x82A164E4, 0x82A1673C, 0x82A1673C, 0x82A15F74, 0x82A15F74, 0x82A15F6C, 0x82A15F6C, 0x82A15F6C, 0x82A15F6C, 0x82A1673C, 0x82A1673C, 0x82A160D8, ] [[switch]] base = 0x82A239D8 r = 11 default = 0x82A24A04 labels = [ 0x82A23A00, 0x82A23F58, 0x82A23F90, 0x82A242D8, 0x82A24634, 0x82A24664, 0x82A247B0, 0x82A2485C, ] [[switch]] base = 0x82A2573C r = 11 default = 0x82A2567C labels = [ 0x82A258D8, 0x82A25C34, 0x82A25C34, 0x82A2567C, 0x82A2567C, 0x82A25778, 0x82A25D28, 0x82A25D28, 0x82A25D28, 0x82A25764, 0x82A2567C, 0x82A2567C, 0x82A2567C, 0x82A2567C, 0x82A25D88, 0x82A25E8C, ] [[switch]] base = 0x82A3FBF4 r = 11 default = 0x82A47110 labels = [ 0x82A3FC1C, 0x82A3FC38, 0x82A3FCA0, 0x82A3FCA0, 0x82A3FC1C, 0x82A3FC1C, 0x82A3FC58, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FC90, 0x82A3FCC0, 0x82A3FCC0, 0x82A3FC6C, 0x82A3FCE4, 0x82A3FDE8, 0x82A3FE4C, 0x82A3FCF4, 0x82A47104, ] [[switch]] base = 0x82A405B8 r = 11 default = 0x82A40678 labels = [ 0x82A405E0, 0x82A405F8, 0x82A40678, 0x82A40678, 0x82A40610, 0x82A40610, 0x82A40610, 0x82A40610, 0x82A473AC, 0x82A473AC, 0x82A473AC, 0x82A473AC, 0x82A40678, 0x82A40678, 0x82A40624, 0x82A4064C, 0x82A40624, 0x82A4064C, 0x82A4738C, 0x82A40624, 0x82A40624, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A40624, 0x82A40624, 0x82A40624, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A4064C, 0x82A40678, 0x82A4064C, 0x82A473AC, 0x82A473AC, 0x82A473AC, 0x82A473AC, 0x82A473AC, 0x82A4064C, 0x82A40678, 0x82A4065C, 0x82A4065C, 0x82A4065C, 0x82A4065C, 0x82A4065C, 0x82A4065C, 0x82A4064C, 0x82A4064C, ] [[switch]] base = 0x82A41528 r = 11 default = 0x82A478D8 labels = [ 0x82A4156C, 0x82A41550, 0x82A419E0, 0x82A419E0, 0x82A41928, 0x82A41920, 0x82A478D8, 0x82A41D04, 0x82A41D04, 0x82A41B78, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41D04, 0x82A41B04, 0x82A41B04, 0x82A42068, 0x82A4172C, 0x82A42504, 0x82A469C8, 0x82A425DC, ] [[switch]] base = 0x82A41E34 r = 11 default = 0x82A475AC labels = [ 0x82A41E5C, 0x82A41E5C, 0x82A41E5C, 0x82A41E70, 0x82A41EA8, 0x82A41EC8, 0x82A41ED8, 0x82A41EE4, 0x82A41EF4, 0x82A41F00, 0x82A41F14, 0x82A41E5C, 0x82A41E84, ] [[switch]] base = 0x82A425F0 r = 22 default = 0x82A4781C labels = [ 0x82A42618, 0x82A42620, 0x82A42940, 0x82A42940, 0x82A42620, 0x82A42BFC, 0x82A42BFC, 0x82A43068, 0x82A43178, 0x82A43260, 0x82A433CC, 0x82A43580, 0x82A436E4, 0x82A466EC, 0x82A466EC, 0x82A437D0, 0x82A439E8, 0x82A4436C, 0x82A4452C, 0x82A4466C, 0x82A44C98, 0x82A44C04, 0x82A446F0, 0x82A44E88, 0x82A44848, 0x82A44E90, 0x82A44FFC, 0x82A46854, 0x82A449E4, 0x82A44AA8, 0x82A44B38, 0x82A44D24, 0x82A45178, 0x82A452A0, 0x82A453A8, 0x82A44DB4, 0x82A44DE0, 0x82A44BFC, 0x82A44F18, 0x82A44F20, 0x82A4557C, 0x82A45690, 0x82A45690, 0x82A45690, 0x82A45690, 0x82A458A0, 0x82A458A0, 0x82A45690, 0x82A458A0, 0x82A458A0, 0x82A4781C, 0x82A45C24, 0x82A45CD0, 0x82A4387C, 0x82A45D70, 0x82A45E88, 0x82A46184, 0x82A44C0C, 0x82A43100, 0x82A4626C, 0x82A433CC, 0x82A43420, 0x82A43580, 0x82A46330, 0x82A44C14, 0x82A464F4, 0x82A433CC, 0x82A43580, 0x82A466A4, 0x82A466A4, 0x82A466A4, 0x82A466A4, 0x82A466A4, 0x82A466C4, 0x82A466C4, 0x82A466C4, 0x82A466C4, 0x82A466C4, 0x82A466D0, 0x82A466D0, 0x82A466D0, 0x82A466D0, 0x82A466D0, 0x82A466E0, 0x82A466E0, 0x82A466E0, 0x82A466E0, 0x82A466E0, 0x82A4659C, 0x82A43928, ] [[switch]] base = 0x82A5B97C r = 11 default = 0x82A5C04C labels = [ 0x82A5BB84, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5BAF8, 0x82A5BB00, 0x82A5C04C, 0x82A5C04C, 0x82A5B9A4, 0x82A5C04C, 0x82A5C04C, 0x82A5BB98, 0x82A5BBAC, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5BBCC, 0x82A5C04C, 0x82A5C04C, 0x82A5BB34, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5BBE0, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5B9A4, 0x82A5B9A4, 0x82A5B9DC, 0x82A5BE00, 0x82A5BA2C, 0x82A5BB1C, 0x82A5C04C, 0x82A5B9A4, 0x82A5C04C, 0x82A5BBF4, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5B9C0, 0x82A5C04C, 0x82A5BC08, 0x82A5C04C, 0x82A5C04C, 0x82A5BCC4, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5BCA8, 0x82A5BA34, 0x82A5BCF8, 0x82A5C04C, 0x82A5C04C, 0x82A5BB6C, 0x82A5C04C, 0x82A5C04C, 0x82A5BD14, 0x82A5BD54, 0x82A5BC08, 0x82A5BD70, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5BD88, 0x82A5C04C, 0x82A5BD9C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5C04C, 0x82A5BDB0, 0x82A5BDC4, 0x82A5C04C, 0x82A5BDD8, 0x82A5C04C, 0x82A5BDEC, 0x82A5C04C, 0x82A5BB54, 0x82A5BB54, 0x82A5B9A4, 0x82A5B9A4, 0x82A5B9A4, ] [[switch]] base = 0x82A5C52C r = 11 default = 0x82A5CC40 labels = [ 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5C90C, 0x82A5C928, 0x82A5C6E8, 0x82A5C730, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC5C, 0x82A5C554, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC08, 0x82A5CA44, 0x82A5CC40, 0x82A5C568, 0x82A5C570, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC5C, 0x82A5C63C, 0x82A5C9DC, 0x82A5C690, 0x82A5C948, 0x82A5C9C4, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5C7CC, 0x82A5CC40, 0x82A5C578, 0x82A5C5AC, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5C820, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CABC, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5CC40, 0x82A5C7A8, 0x82A5CC40, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC5C, 0x82A5CC40, 0x82A5CC40, 0x82A5CC80, ] [[switch]] base = 0x82A68188 r = 10 default = 0x82A6860C labels = [ 0x82A681B0, 0x82A68218, 0x82A682C0, 0x82A6828C, 0x82A682F0, 0x82A6832C, 0x82A683B4, 0x82A6843C, 0x82A6846C, 0x82A68498, 0x82A684C4, 0x82A684E0, 0x82A68574, 0x82A685A0, 0x82A685C0, 0x82A6860C, 0x82A6860C, 0x82A6860C, 0x82A6860C, 0x82A6860C, 0x82A6860C, 0x82A6860C, 0x82A6860C, 0x82A6860C, 0x82A682F0, 0x82A6832C, 0x82A683B4, 0x82A6843C, ] [[switch]] base = 0x82AA2504 r = 10 default = 0x82AA67C4 labels = [ 0x82AA67BC, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA5498, 0x82AA54F0, 0x82AA568C, 0x82AA67E4, 0x82AA53FC, 0x82AA53FC, 0x82AA53FC, 0x82AA62E8, 0x82AA67C4, 0x82AA67C4, 0x82AA67C4, 0x82AA67C4, 0x82AA67C4, 0x82AA67C4, 0x82AA67C4, 0x82AA4F44, 0x82AA4F18, 0x82AA51C8, 0x82AA5264, 0x82AA6404, 0x82AA67C4, 0x82AA66DC, 0x82AA6058, 0x82AA6748, 0x82AA6104, 0x82AA6768, 0x82AA67C4, 0x82AA252C, 0x82AA55F0, 0x82AA6518, 0x82AA5C70, 0x82AA5A10, 0x82AA55C0, 0x82AA5858, 0x82AA5698, 0x82AA5470, 0x82AA4F38, 0x82AA67E4, 0x82AA5520, 0x82AA67E4, 0x82AA50F8, 0x82AA5504, 0x82AA50BC, 0x82AA67E4, 0x82AA67A4, 0x82AA67C4, 0x82AA5644, 0x82AA5444, 0x82AA5444, 0x82AA5444, 0x82AA5444, 0x82AA5444, 0x82AA5444, 0x82AA5444, 0x82AA510C, 0x82AA67E4, 0x82AA4F00, 0x82AA512C, 0x82AA508C, 0x82AA508C, 0x82AA4F64, 0x82AA506C, 0x82AA5680, 0x82AA552C, 0x82AA552C, 0x82AA552C, 0x82AA552C, 0x82AA552C, 0x82AA552C, 0x82AA552C, 0x82AA5BDC, 0x82AA67C4, 0x82AA6668, ] [[switch]] base = 0x82AA6CE4 r = 11 default = 0x82AA7BEC labels = [ 0x82AA6D0C, 0x82AA7BEC, 0x82AA6E3C, 0x82AA7018, 0x82AA6EAC, 0x82AA73B4, 0x82AA7450, 0x82AA7018, 0x82AA767C, 0x82AA7750, ] [[switch]] base = 0x82AB2E3C r = 11 default = 0x82AB33F8 labels = [ 0x82AB3110, 0x82AB3110, 0x82AB2E64, 0x82AB31DC, 0x82AB2F60, 0x82AB2E7C, 0x82AB31BC, 0x82AB2EA4, 0x82AB33F8, 0x82AB3070, 0x82AB3110, 0x82AB3110, 0x82AB2FC8, 0x82AB303C, 0x82AB3110, 0x82AB33F8, 0x82AB33F8, 0x82AB33F8, 0x82AB33F8, 0x82AB33F8, 0x82AB2FB4, 0x82AB31FC, 0x82AB3110, 0x82AB3054, ] [[switch]] base = 0x82ABDF34 r = 11 default = 0x82ABE464 labels = [ 0x82ABE538, 0x82ABE244, 0x82ABE244, 0x82ABE244, 0x82ABE244, 0x82ABE1F0, 0x82ABE200, 0x82ABE21C, 0x82ABE464, 0x82ABE2E8, 0x82ABE320, 0x82ABE364, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE054, 0x82ABE054, 0x82ABE054, 0x82ABDF5C, 0x82ABDF8C, 0x82ABDFB0, 0x82ABDFBC, 0x82ABDFC4, 0x82ABDFCC, 0x82ABDFD8, 0x82ABE008, 0x82ABE024, 0x82ABE030, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE098, 0x82ABE098, 0x82ABE098, 0x82ABE0DC, 0x82ABE0DC, 0x82ABE0DC, 0x82ABE154, 0x82ABE154, 0x82ABE154, 0x82ABE188, 0x82ABE188, 0x82ABE188, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE520, 0x82ABE4C8, 0x82ABE4D0, 0x82ABE4E4, 0x82ABE500, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE464, 0x82ABE3B8, 0x82ABE3B8, 0x82ABE3B8, 0x82ABE3B8, 0x82ABE3D4, 0x82ABE3D4, 0x82ABE3D4, 0x82ABE3D4, 0x82ABE3FC, 0x82ABE3FC, 0x82ABE3FC, 0x82ABE3FC, ] [[switch]] base = 0x82ABE6BC r = 11 default = 0x82ABECD8 labels = [ 0x82ABEDAC, 0x82ABEA88, 0x82ABEA88, 0x82ABEA88, 0x82ABEA88, 0x82ABEA34, 0x82ABEA44, 0x82ABEA60, 0x82ABECD8, 0x82ABEB5C, 0x82ABEB94, 0x82ABEBD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABE88C, 0x82ABE88C, 0x82ABE88C, 0x82ABE6E4, 0x82ABE710, 0x82ABE744, 0x82ABE758, 0x82ABE764, 0x82ABE770, 0x82ABE784, 0x82ABE7B8, 0x82ABE7E8, 0x82ABE810, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABE8D8, 0x82ABE8D8, 0x82ABE8D8, 0x82ABE918, 0x82ABE918, 0x82ABE918, 0x82ABE998, 0x82ABE998, 0x82ABE998, 0x82ABE9CC, 0x82ABE9CC, 0x82ABE9CC, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED94, 0x82ABED3C, 0x82ABED44, 0x82ABED58, 0x82ABED74, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABECD8, 0x82ABEC2C, 0x82ABEC2C, 0x82ABEC2C, 0x82ABEC2C, 0x82ABEC48, 0x82ABEC48, 0x82ABEC48, 0x82ABEC48, 0x82ABEC70, 0x82ABEC70, 0x82ABEC70, 0x82ABEC70, ] [[switch]] base = 0x82ABF210 r = 11 default = 0x82ABFE0C labels = [ 0x82ABFFD8, 0x82ABFB2C, 0x82ABFB2C, 0x82ABFB2C, 0x82ABFB2C, 0x82ABFAD8, 0x82ABFAE8, 0x82ABFB04, 0x82ABFE0C, 0x82ABFC30, 0x82ABFC84, 0x82ABFCE4, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABF5E8, 0x82ABF5E8, 0x82ABF5E8, 0x82ABF238, 0x82ABF27C, 0x82ABF2C8, 0x82ABF30C, 0x82ABF358, 0x82ABF39C, 0x82ABF3E8, 0x82ABF43C, 0x82ABF498, 0x82ABF4F4, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABF550, 0x82ABF550, 0x82ABF550, 0x82ABF584, 0x82ABF584, 0x82ABF584, 0x82ABF988, 0x82ABF988, 0x82ABF988, 0x82ABF8F4, 0x82ABF8F4, 0x82ABF8F4, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFECC, 0x82ABFE6C, 0x82ABFE74, 0x82ABFE88, 0x82ABFEA4, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFE0C, 0x82ABFD54, 0x82ABFD54, 0x82ABFD54, 0x82ABFD54, 0x82ABFD70, 0x82ABFD70, 0x82ABFD70, 0x82ABFD70, 0x82ABFD98, 0x82ABFD98, 0x82ABFD98, 0x82ABFD98, ] [[switch]] base = 0x82AC8C94 r = 29 default = 0x82AC9418 labels = [ 0x82AC8CBC, 0x82AC8CF8, 0x82AC8D44, 0x82AC8D6C, 0x82AC8DF0, 0x82AC8E18, 0x82AC8E48, 0x82AC8E78, 0x82AC8F44, 0x82AC9000, 0x82AC9108, 0x82AC9130, 0x82AC92A4, 0x82AC9350, 0x82AC93C8, 0x82AC93F0, ] ================================================ FILE: MarathonRecompLib/ppc/.gitignore ================================================ ppc_* ================================================ FILE: MarathonRecompLib/private/.gitignore ================================================ * !.gitignore ================================================ FILE: MarathonRecompLib/shader/.gitignore ================================================ * !shader_cache.h !.gitignore ================================================ FILE: MarathonRecompLib/shader/shader_cache.h ================================================ #pragma once struct ShaderCacheEntry { const uint64_t hash; const uint32_t dxilOffset; const uint32_t dxilSize; const uint32_t spirvOffset; const uint32_t spirvSize; const uint32_t airOffset; const uint32_t airSize; const uint32_t specConstantsMask; char filename[256]; struct GuestShader* guestShader; }; extern ShaderCacheEntry g_shaderCacheEntries[]; extern const size_t g_shaderCacheEntryCount; extern const uint8_t g_compressedDxilCache[]; extern const size_t g_dxilCacheCompressedSize; extern const size_t g_dxilCacheDecompressedSize; extern const uint8_t g_compressedAirCache[]; extern const size_t g_airCacheCompressedSize; extern const size_t g_airCacheDecompressedSize; extern const uint8_t g_compressedSpirvCache[]; extern const size_t g_spirvCacheCompressedSize; extern const size_t g_spirvCacheDecompressedSize; ================================================ FILE: README.md ================================================

--- > [!CAUTION] > This recompilation is still under active development and is NOT meant for public use. Support will not be provided until an official release. Marathon Recompiled is an unofficial PC port of the Xbox 360 version of Sonic the Hedgehog (2006) created through the process of static recompilation. The port offers Windows, Linux, and macOS support. **This project does not include any game assets. You must provide the files from your own legally acquired copy of the game to install or build Marathon Recompiled.** [XenonRecomp](https://github.com/sonicnext-dev/XenonRecomp) and [XenosRecomp](https://github.com/sonicnext-dev/XenosRecomp) are the main recompilers used for converting the game's original PowerPC code and Xenos shaders into compatible C++ and HLSL code respectively. The development of these recompilers was directly inspired by [N64: Recompiled](https://github.com/N64Recomp/N64Recomp), which was used to create [Zelda 64: Recompiled](https://github.com/Zelda64Recomp/Zelda64Recomp). ## Table of Contents - [Known Issues](#known-issues) - [FAQ](#faq) - [Building](#building) - [Credits](#credits) ## Known Issues Before reporting any issues, check if they are listed [here](https://github.com/sonicnext-dev/MarathonRecomp/issues). ### Original Game Bugs Game bugs present on the original hardware are intentionally preserved and will not be fixed apart from a few minor exceptions in [#44](https://github.com/sonicnext-dev/MarathonRecomp/issues/44). Please do not report issues for these bugs and verify that the issue does not occur on original hardware before reporting. Bug reports for issues found in the original game will be rejected. Bugs that only happen in Marathon Recompiled must be accompanied by footage captured on original Xbox 360 hardware showing that the bug does not happen there. ### File Picker Unavailable on Steam Deck in Game Mode Due to some restrictions of how the desktop environment on the Steam Deck works whilst in Game Mode, please note that you may need to at least first boot into Desktop Mode to be able to use the file picker to provide the game files. Simply booting at least once in Desktop Mode will enable the Deck to use the file picker when going back to Game Mode. You can complete the entire installation process while in Desktop Mode to save yourself the trouble of browsing through Game Mode if necessary. ## FAQ ### Do you have a website? Marathon Recompiled does not have an official website. **Please link here when directing anyone to the project.** > [!CAUTION] > Do not download builds of Marathon Recompiled from anywhere but our [Releases](https://github.com/sonicnext-dev/MarathonRecomp/releases/latest) page. > > **We will never distribute builds on other websites, via Discord servers or via third-party update tools.** ### Why does the installer say my files are invalid? The installer may display this error for several reasons. Please check the following to ensure your files are valid: - Please read the [How to Install](#how-to-install) section and make sure you've acquired all of the necessary files correctly. - Verify that you're not trying to add compressed files such as `.zip`, `.7z`, `.rar` or other formats. - Only use the **Add Folder** option if you're sure you have a directory with the content's files already extracted, which means it'll only contain files like `.xex`, `.ar.00`, `.arl` and others. **This option will not scan your folder for compatible content**. - Ensure that the files you've acquired correspond to the same region. **Discs and Title Updates from different regions can't be used together** and will fail to generate a patch. - The installer will only accept **original and unmodified files**. Do not attempt to provide modified files to the installer. ### What are the keyboard bindings? Pad|Key -|- A (Cross)|S B (Circle)|D X (Square)|A Y (Triangle)|W D-Pad - Up|Unbound D-Pad - Down|Unbound D-Pad - Left|Q D-Pad - Right|E Start|Return Back (Select)|Backspace Left Trigger (L2)|1 Right Trigger (R2)|3 Left Bumper (L1)|Unbound Right Bumper (R1)|Unbound Left Stick - Up|Up Arrow Left Stick - Down|Down Arrow Left Stick - Left|Left Arrow Left Stick - Right|Right Arrow Right Stick - Up|Unbound Right Stick - Down|Unbound Right Stick - Left|Unbound Right Stick - Right|Unbound --- You can change the keyboard bindings by editing `config.toml` located in the [configuration directory](#where-is-the-save-data-and-configuration-file-stored), although using a controller is highly recommended until Action Remapping is added in a future update. Refer to the left column of [this enum template](https://github.com/sonicnext-dev/MarathonRecomp/blob/main/MarathonRecomp/user/config.cpp#L40) for a list of valid keys. *The default keyboard layout is based on Devil's Details' keyboard layout for Sonic Generations (2011)*. ### Where is the save data and configuration file stored? The save data and configuration files are stored at the following locations: - Windows: `%APPDATA%\MarathonRecomp\` - Linux: `~/.config/MarathonRecomp/` - macOS: `~/Library/Application Support/MarathonRecomp/` You will find the save data under the `save` folder. The configuration file is named `config.toml`. ### I want to update the game. How can I avoid losing my save data? Do I need to reinstall the game? Updating the game can be done by simply copying and replacing the files from a [release](https://github.com/sonicnext-dev/MarathonRecomp/releases) on top of your existing installation. **Your save data and configuration will not be lost.** You won't need to reinstall the game, as the game files will always remain the same across versions of Marathon Recompiled. ### How can I force the game to store the save data and configuration in the installation folder? You can make the game ignore the [default configuration paths](#where-is-the-save-data-and-configuration-file-stored) and force it to save everything in the installation directory by creating an empty `portable.txt` file. You are directly responsible for the safekeeping of your save data and configuration if you choose this option. ### How can I force the game to run the installation again? While it's unlikely you'll need to do this unless you've modified your game files by accident, you can force the installer to run again by using the launch argument: `--install`. ### How can I force the game to run under X11 or Wayland? Use either of the following arguments to force SDL to run under the video driver you want: - X11: `--sdl-video-driver x11` - Wayland: `--sdl-video-driver wayland` The second argument will be passed directly to SDL as a hint to try to initialize the game with your preferred option. ### Where is the game data for the Flatpak version installed? Given it is not possible to run the game where the Flatpak is stored, the game data will be installed to `~/.var/app/io.github.sonicnext_dev.marathonrecomp/data`. The Flatpak build will only recognize this directory as valid. Feel free to reuse this data directory with a native Linux build if you wish to switch in the future. If you wish to move this data to another location, you can do so by creating a symlink from this directory to the one where you'll migrate your installation to. > [!WARNING] > Using external frame rate limiters or performance overlays may degrade performance or have negative consequences. ### Can I install the game with a PlayStation 3 copy? **You cannot use the files from the PlayStation 3 version of the game.** Supporting these files would require an entirely new recompilation, as they have proprietary formatting that only works on PS3 and the code for these formats is only present in that version. All significant differences present in the PS3 version of the game have been included in this project as options. ### Why is the game detecting my PlayStation controller as an Xbox controller? If you're using a third-party input translation layer (such as DS4Windows or Steam Input), it is recommended that you disable these for full controller support. ### What other platforms will be supported? This project does not plan to support any more platforms other than Windows, Linux and macOS at the moment. Any contributors who wish to support more platforms should do so through a fork. ## Building [Check out the building instructions here](/docs/BUILDING.md). ## Credits ### Marathon Recompiled - [ga2mer](https://github.com/ga2mer): Creator of the recompilation, laying the initial foundation for the project. Other responsibilities include maintaining the audio backend and providing various patches for the game. - [IsaacMarovitz](https://github.com/IsaacMarovitz): Graphics Programmer for the recompilation. Other responsibilities include maintaining macOS support and aiding in porting mod manager patches to the recompilation. - [squidbus](https://github.com/squidbus): Graphics Programmer for the recompilation. Aided in researching the game's internals. - [Hyper](https://github.com/hyperbx): Developer of system level features, such as achievement support and the custom menus, alongside various other patches and features to make the game feel right at home on modern systems. Aided in researching the game's internals. - [Rei-san](https://github.com/ReimousTH): Developer of quality of life patches and extensive amounts of research into the game's internals. - [Desko](https://github.com/FateForWindows): Aided in researching the game's internals and created many quality of life patches for the original game used by the recompilation. - [LJSTAR](https://github.com/LJSTARbird): Artist behind the project logo. Provided French localization for the custom menus. - [brianuuuSonic](https://github.com/brianuuu): Provided Japanese localization for the custom menus. - [Kitzuku](https://github.com/Kitzuku): Provided German localization for the options menu. - Ray Vassos: Provided German localization for the achievements menu. - [DaGuAr](https://x.com/TheDaguar): Provided Spanish localization for the custom menus. - [NextinHKRY](https://github.com/NextinMono): Provided Italian localization for the custom menus. - [Hotline Sehwani](https://www.youtube.com/channel/UC9NBX5UPq4fYvbr7bzvRvOg) & SilverIceSound: Produced the [installer music](https://www.youtube.com/watch?v=8mfOSTcTQNs) ([original prod.](https://www.youtube.com/watch?v=k_mGNwrxR5M) by [Tomoya Ohtani](https://www.youtube.com/@TomoyaOhtaniChannel)). ### Special Thanks - Unleashed Recompiled Development Team: Created much of the ground work that made all of this possible and sped up development time considerably. - [Skyth](https://github.com/blueskythlikesclouds): Provided graphics consultation and support for dynamic aspect ratio. - [Darío](https://github.com/DarioSamo): Creator of the graphics hardware abstraction layer [plume](https://github.com/renderbag/plume), used by the project's graphics backend. - [Syko](https://x.com/UltraSyko): Aided in identifying fonts used by the original SonicNext logo. - [ocornut](https://github.com/ocornut): Creator of [Dear ImGui](https://github.com/ocornut/imgui), which is used as the backbone of the custom menus. - Raymond Chen: Useful resources on Windows application development with his blog ["The Old New Thing"](https://devblogs.microsoft.com/oldnewthing/). ================================================ FILE: docs/BUILDING.md ================================================ # Building ## 1. Clone the Repository Clone **MarathonRecomp** with submodules using [Git](https://git-scm.com/). ``` git clone --recurse-submodules https://github.com/sonicnext-dev/MarathonRecomp.git ``` ### Windows If you skipped the `--recurse-submodules` argument during cloning, you can run `update_submodules.bat` to ensure the submodules are pulled. ## 2. Add the Required Game Files Copy the following files from the game and place them inside `./MarathonRecompLib/private/`: - `default.xex` - `shader.arc` - `shader_lt.arc` `default.xex` is located in the game's root directory, while the others are located in `/xenon/archives`. [//]: # (> [!TIP]) [//]: # (> It is recommended that you install the game using [an existing Marathon Recompiled release](https://github.com/sonicnext-dev/MarathonRecomp/releases/latest) to acquire these files, otherwise you'll need to rely on third-party tools to extract them.) [//]: # (>) [//]: # (> Using the Marathon Recompiled installation wizard will also ensure that these files are compatible with each other so that they can be used with the build environment.) [//]: # (>) [//]: # (> When sourcing these files from an Marathon Recompiled installation, they will be stored under `game` and `update` subdirectories.) ## 3. Install Dependencies ### Windows You will need to install [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/). In the installer, you must select the following **Workloads** and **Individual components** for installation: - Desktop development with C++ - C++ Clang Compiler for Windows - C++ CMake tools for Windows ### Linux The following command will install the required dependencies on a distro that uses `apt` (such as Debian-based distros). ```bash sudo apt install autoconf automake libtool pkg-config curl cmake ninja-build clang clang-tools libgtk-3-dev ``` The following command will install the required dependencies on a distro that uses `pacman` (such as Arch-based distros). ```bash sudo pacman -S base-devel ninja lld clang gtk3 ``` You can also find the equivalent packages for your preferred distro. > [!NOTE] > This list may not be comprehensive for your particular distro and you may be required to install additional packages, should an error occur during configuration. ### macOS You will need to install the latest Xcode from Apple. The following commands will install additional required dependencies, depending on which package manager you use. If you use Homebrew: ```bash brew install cmake ninja pkg-config ``` If you use MacPorts: ```bash sudo port install cmake ninja pkg-config ``` ## 4. Build the Project ### Windows 1. Open the repository directory in Visual Studio and wait for CMake generation to complete. If you don't plan to debug, switch to the `Release` configuration. > [!TIP] > If you need a Release-performant build and want to iterate on development without debugging, **it is highly recommended** that you use the `RelWithDebInfo` configuration for faster compile times. 2. Under **Solution Explorer**, right-click and choose **Switch to CMake Targets View**. 3. Right-click the **MarathonRecomp** project and choose **Set as Startup Item**, then choose **Add Debug Configuration**. 4. Add a `currentDir` property to the first element under `configurations` in the generated JSON and set its value to the path to your game directory (where root is the directory containing `dlc`, `game`, etc). 5. Start **MarathonRecomp**. The initial compilation may take a while to complete due to code and shader recompilation. ### Linux 1. Configure the project using CMake by navigating to the repository and running the following command. ```bash cmake . --preset linux-release ``` > [!NOTE] > The available presets are `linux-debug`, `linux-relwithdebinfo` and `linux-release`. 2. Build the project using the selected configuration. ```bash cmake --build ./out/build/linux-release --target MarathonRecomp ``` 3. Navigate to the directory that was specified as the output in the previous step and run the game. ```bash ./MarathonRecomp ``` ### macOS 1. Configure the project using CMake by navigating to the repository and running the following command. ```bash cmake . --preset macos-release ``` > [!NOTE] > The available presets are `macos-debug`, `macos-relwithdebinfo` and `macos-release`. 2. Build the project using the selected configuration. ```bash cmake --build ./out/build/macos-release --target MarathonRecomp ``` 3. Navigate to the directory that was specified as the output in the previous step and run the game. ```bash open -a MarathonRecomp.app ``` ================================================ FILE: docs/DUMPING-en.md ================================================ # Dumping ### Pre-requisites - Xbox 360 (modifications not necessary) - Xbox 360 Hard Drive (20 GB minimum) - Xbox 360 Hard Drive Transfer Cable (or a compatible SATA to USB adapter) - Sonic the Hedgehog (2006) for Xbox 360 - Retail Disc or Digital Copy. - All available DLC are optional. - [7-Zip](https://7-zip.org/download.html) (for extracting Velocity) - [Velocity](https://github.com/Gualdimar/Velocity/releases/download/xex%2Biso-branch/Velocity-XEXISO.rar) (Gualdimar's fork) > [!TIP] > If you do not have the Xbox 360 Hard Drive Transfer Cable, please ensure that you purchase the correct revision of it for your console. > > The latest revision works with both original Xbox 360 and Xbox 360 S|E hard drives, but the first revision only works with original Xbox 360 hard drives. > > To know which is which, the first revision cable is gray, whereas the latest revision (which supports any Xbox 360 hard drive) is black. ### Instructions > [!NOTE] > If you have a digital copy of Sonic the Hedgehog, skip to step 4. 1. Insert your retail disc copy of Sonic the Hedgehog into the Xbox 360 disc tray. 2. At the Xbox Dashboard, go over to the disc tile under the **home** tab and press X to view **Game Details**. 3. Under the **overview** tab, select the **Install** tile and choose to install to the primary hard drive. 4. Once installed, turn off your Xbox 360 and remove the hard drive from your console. > [!TIP] > You may consult the following guides if you're unsure on how to do this: > - [Xbox 360](https://www.ifixit.com/Guide/Xbox+360+Hard+Drive+Replacement/3326) > - [Xbox 360 S](https://www.ifixit.com/Guide/Xbox+360+S+Hard+Drive+Replacement/3184) > - [Xbox 360 E](https://www.ifixit.com/Guide/Xbox+360+E+Hard+Drive+Replacement/22179) 5. Using the Xbox 360 Hard Drive Transfer Cable (or compatible SATA to USB adapter), connect your Xbox 360 hard drive to your PC. > [!CAUTION] > If you're using an unofficial SATA to USB adapter, you may need to remove the hard drive from its enclosure in order to connect it. > > For original Xbox 360 hard drives, this process is as simple as [removing some screws and cracking open the enclosure](https://www.ifixit.com/Guide/Xbox+360+HDD+Replacement/3430). > > For Xbox 360 S|E hard drives, this enclosure is glued shut and removing the hard drive may be an irreversible process! > > **It is highly recommended** that you obtain the official Xbox 360 Hard Drive Transfer Cable in order to proceed. 6. Download [the latest release of Velocity](https://github.com/Gualdimar/Velocity/releases/download/xex%2Biso-branch/Velocity-XEXISO.rar) and open the `*.rar` file using [7-Zip](https://7-zip.org/download.html), then extract its contents anywhere that's convenient to you. 7. Create a new folder anywhere that's convenient to you for storing the game files. > [!NOTE] > If you're using Linux, skip to step 9. 8. Right-click `Velocity.exe` and click **Properties**, then under the **Compatibility** tab, tick **Run this program as an administrator** and click **OK**. This is required in order for the program to recognize the hard drive. You can now launch `Velocity.exe`. 9. You should see a **Device Detected** message appear on launch asking if you would like to open the **Device Content Viewer**. Click **Yes**. 10. You should now see a tree view of your hard drive's contents. Expand the tree nodes for `/Shared Items/Games/` (and optionally `/Shared Items/DLC/`, if you have the DLC installed). 11. Hold the CTRL key and click on **SONIC THE HEDGEHOG** under the `Games` node, as well as the **DLC** under the `DLC` node, if you have the DLC installed. Ensure all are selected before the next step. 12. Right-click any of the selected items and click **Copy Selected to Local Disk**, then navigate to the folder you created in step 7 and select it. Velocity will now begin copying the game files to your PC. 13. Once the transfer is complete, close the **Device Content Viewer** window and navigate to **Tools > Device Tools > Raw Device Viewer**. 14. Once the transfer is complete, you should now have all of the necessary files for installation. [Return to the readme and proceed to the next step](/README.md#how-to-install). ================================================ FILE: flatpak/README.md ================================================ Build ```sh flatpak-builder --force-clean --user --install-deps-from=flathub --repo=repo --install builddir io.github.sonicnext_dev.marathonrecomp.json ``` Bundle ```sh flatpak build-bundle repo io.github.sonicnext_dev.marathonrecomp.flatpak io.github.sonicnext_dev.marathonrecomp --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo ``` ================================================ FILE: flatpak/io.github.sonicnext_dev.marathonrecomp.desktop ================================================ [Desktop Entry] Name=Marathon Recompiled Exec=/app/bin/MarathonRecomp Type=Application Icon=io.github.sonicnext_dev.marathonrecomp Categories=Game; Comment=Static recompilation of Sonic the Hedgehog (2006). MimeType=x-scheme-handler/marathonrecomp ================================================ FILE: flatpak/io.github.sonicnext_dev.marathonrecomp.json ================================================ { "id": "io.github.sonicnext_dev.marathonrecomp", "runtime": "org.freedesktop.Platform", "runtime-version": "23.08", "sdk": "org.freedesktop.Sdk", "sdk-extensions" : [ "org.freedesktop.Sdk.Extension.llvm18" ], "finish-args": [ "--share=network", "--socket=wayland", "--socket=fallback-x11", "--socket=pulseaudio", "--device=all", "--talk-name=org.mpris.MediaPlayer2.*", "--filesystem=host", "--filesystem=/media", "--filesystem=/run/media", "--filesystem=/mnt" ], "modules": [ { "name": "MarathonRecomp", "buildsystem": "simple", "build-commands": [ "cmake --preset linux-release -DMARATHON_RECOMP_FLATPAK=ON -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache", "cmake --build out/build/linux-release --target MarathonRecomp", "mkdir -p /app/bin", "cp out/build/linux-release/MarathonRecomp/MarathonRecomp /app/bin/MarathonRecomp", "install -Dm644 MarathonRecompResources/images/game_icon.png /app/share/icons/hicolor/128x128/apps/${FLATPAK_ID}.png", "install -Dm644 flatpak/io.github.sonicnext_dev.marathonrecomp.metainfo.xml /app/share/metainfo/${FLATPAK_ID}.metainfo.xml", "install -Dm644 flatpak/io.github.sonicnext_dev.marathonrecomp.desktop /app/share/applications/${FLATPAK_ID}.desktop" ], "sources": [ { "type": "dir", "path": "../" } ], "build-options": { "append-path": "/usr/lib/sdk/llvm18/bin", "prepend-ld-library-path": "/usr/lib/sdk/llvm18/lib", "build-args": [ "--share=network" ] } } ] } ================================================ FILE: flatpak/io.github.sonicnext_dev.marathonrecomp.metainfo.xml ================================================ io.github.sonicnext_dev.marathonrecomp Marathon Recompiled An unofficial PC port of Sonic the Hedgehog (2006). CC0-1.0 GPL-3.0+ pointing keyboard touch

An unofficial PC port of the Xbox 360 version of Sonic the Hedgehog (2006) created through the process of static recompilation. https://github.com/sonicnext-dev/MarathonRecomp

io.github.sonicnext_dev.marathonrecomp.desktop
================================================ FILE: thirdparty/.gitignore ================================================ !* # Visual Studio 2015/2017 cache/options directory .vs/ # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ ================================================ FILE: thirdparty/CMakeLists.txt ================================================ cmake_policy(SET CMP0077 NEW) set(MSDF_ATLAS_BUILD_STANDALONE OFF) set(MSDF_ATLAS_USE_SKIA OFF) set(MSDF_ATLAS_NO_ARTERY_FONT ON) set(MSDFGEN_DISABLE_PNG ON) set(SDL2MIXER_DEPS_SHARED OFF) set(SDL2MIXER_VENDORED ON) set(SDL2MIXER_FLAC OFF) set(SDL2MIXER_MOD OFF) set(SDL2MIXER_MP3 OFF) set(SDL2MIXER_MIDI OFF) set(SDL2MIXER_OPUS OFF) set(SDL2MIXER_VORBIS "VORBISFILE") set(SDL2MIXER_WAVPACK OFF) if (CMAKE_SYSTEM_NAME MATCHES "Linux") set(PLUME_SDL_VULKAN_ENABLED ON CACHE BOOL "") endif() if (WIN32) set(PLUME_D3D12_AGILITY_SDK_ENABLED ON CACHE BOOL "") endif() if (APPLE AND CMAKE_OSX_ARCHITECTURES) set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}") elseif (CMAKE_SYSTEM_PROCESSOR) set(BASE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") else() set(BASE_ARCHITECTURE "${CMAKE_HOST_SYSTEM_PROCESSOR}") endif() # Next, match common architecture strings down to a known common value. if (BASE_ARCHITECTURE MATCHES "(x86)|(X86)|(amd64)|(AMD64)") set(ARCHITECTURE "x86_64") elseif (BASE_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)") set(ARCHITECTURE "arm64") else() message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}") endif() add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/msdf-atlas-gen") add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/nativefiledialog-extended") add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/o1heap") add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/SDL") add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/SDL_mixer") add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/plume") add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/ffmpeg-core") if (APPLE) add_subdirectory("${MARATHON_RECOMP_THIRDPARTY_ROOT}/MoltenVK") endif() ================================================ FILE: thirdparty/MoltenVK/CMakeLists.txt ================================================ # Simple CMake script to compile MoltenVK with SPIRV-Cross as a shared library target # Prepare MoltenVK Git revision find_package(Git) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD OUTPUT_VARIABLE MVK_GIT_REV WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) endif() set(MVK_GENERATED_INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/Generated) file(WRITE ${MVK_GENERATED_INCLUDES}/mvkGitRevDerived.h "static const char* mvkRevString = \"${MVK_GIT_REV}\";") message(STATUS "MoltenVK revision: ${MVK_GIT_REV}") # Prepare MoltenVK version file(READ ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK/MoltenVK/API/mvk_private_api.h MVK_PRIVATE_API) string(REGEX MATCH "#define MVK_VERSION_MAJOR [0-9]+" MVK_VERSION_MAJOR_LINE "${MVK_PRIVATE_API}") string(REGEX MATCH "[0-9]+" MVK_VERSION_MAJOR "${MVK_VERSION_MAJOR_LINE}") string(REGEX MATCH "#define MVK_VERSION_MINOR [0-9]+" MVK_VERSION_MINOR_LINE "${MVK_PRIVATE_API}") string(REGEX MATCH "[0-9]+" MVK_VERSION_MINOR "${MVK_VERSION_MINOR_LINE}") string(REGEX MATCH "#define MVK_VERSION_PATCH [0-9]+" MVK_VERSION_PATCH_LINE "${MVK_PRIVATE_API}") string(REGEX MATCH "[0-9]+" MVK_VERSION_PATCH "${MVK_VERSION_PATCH_LINE}") set(MVK_VERSION "${MVK_VERSION_MAJOR}.${MVK_VERSION_MINOR}.${MVK_VERSION_PATCH}") message(STATUS "MoltenVK version: ${MVK_VERSION}") # Find required system libraries find_library(APPKIT_LIBRARY AppKit REQUIRED) find_library(FOUNDATION_LIBRARY Foundation REQUIRED) find_library(IOKIT_LIBRARY IOKit REQUIRED) find_library(IOSURFACE_LIBRARY IOSurface REQUIRED) find_library(METAL_LIBRARY Metal REQUIRED) find_library(QUARTZCORE_LIBRARY QuartzCore REQUIRED) # SPIRV-Cross option(SPIRV_CROSS_CLI "" OFF) option(SPIRV_CROSS_ENABLE_TESTS "" OFF) option(SPIRV_CROSS_ENABLE_HLSL "" OFF) option(SPIRV_CROSS_ENABLE_CPP "" OFF) option(SPIRV_CROSS_SKIP_INSTALL "" ON) add_subdirectory(SPIRV-Cross) # Common set(MVK_COMMON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/Common) file(GLOB_RECURSE MVK_COMMON_SOURCES CONFIGURE_DEPENDS ${MVK_COMMON_DIR}/*.cpp ${MVK_COMMON_DIR}/*.m ${MVK_COMMON_DIR}/*.mm) set(MVK_COMMON_INCLUDES ${MVK_COMMON_DIR}) add_library(MoltenVKCommon STATIC ${MVK_COMMON_SOURCES}) target_include_directories(MoltenVKCommon PUBLIC ${MVK_COMMON_INCLUDES}) target_compile_options(MoltenVKCommon PRIVATE -w) # MoltenVKShaderConverter set(MVK_SHADER_CONVERTER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVKShaderConverter) file(GLOB_RECURSE MVK_SHADER_CONVERTER_SOURCES CONFIGURE_DEPENDS ${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.cpp ${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.m ${MVK_SHADER_CONVERTER_DIR}/MoltenVKShaderConverter/*.mm) set(MVK_SHADER_CONVERTER_INCLUDES ${MVK_SHADER_CONVERTER_DIR} ${MVK_SHADER_CONVERTER_DIR}/include) add_library(MoltenVKShaderConverter STATIC ${MVK_SHADER_CONVERTER_SOURCES}) target_include_directories(MoltenVKShaderConverter PUBLIC ${MVK_SHADER_CONVERTER_INCLUDES}) target_compile_options(MoltenVKShaderConverter PRIVATE -w) target_link_libraries(MoltenVKShaderConverter PRIVATE spirv-cross-msl spirv-cross-reflect MoltenVKCommon) target_compile_definitions(MoltenVKShaderConverter PRIVATE MVK_EXCLUDE_SPIRV_TOOLS=1) # MoltenVK set(MVK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/MoltenVK) file(GLOB_RECURSE MVK_SOURCES CONFIGURE_DEPENDS ${MVK_DIR}/MoltenVK/*.cpp ${MVK_DIR}/MoltenVK/*.m ${MVK_DIR}/MoltenVK/*.mm) file(GLOB MVK_SRC_INCLUDES LIST_DIRECTORIES ON ${MVK_DIR}/MoltenVK/*) set(MVK_INCLUDES ${MVK_SRC_INCLUDES} ${MVK_GENERATED_INCLUDES} ${MVK_DIR}/include ${MARATHON_RECOMP_THIRDPARTY_ROOT}/plume/contrib/Vulkan-Headers/include) add_library(MoltenVK SHARED ${MVK_SOURCES}) target_include_directories(MoltenVK PRIVATE ${MVK_INCLUDES}) target_compile_options(MoltenVK PRIVATE -w) target_link_libraries(MoltenVK PRIVATE ${APPKIT_LIBRARY} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY} ${IOSURFACE_LIBRARY} ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY} spirv-cross-msl MoltenVKCommon MoltenVKShaderConverter) target_compile_definitions(MoltenVK PRIVATE MVK_FRAMEWORK_VERSION=${MVK_VERSION} MVK_USE_CEREAL=0) ================================================ FILE: thirdparty/o1heap/CMakeLists.txt ================================================ project("o1heap") add_library(o1heap "o1heap.h" "o1heap.c") target_include_directories(o1heap PUBLIC ".") ================================================ FILE: thirdparty/o1heap/o1heap.c ================================================ // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions // of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2020 Pavel Kirienko // Authors: Pavel Kirienko #include "o1heap.h" #include #include // ---------------------------------------- BUILD CONFIGURATION OPTIONS ---------------------------------------- /// Define this macro to include build configuration header. This is an alternative to the -D compiler flag. /// Usage example with CMake: "-DO1HEAP_CONFIG_HEADER=\"${CMAKE_CURRENT_SOURCE_DIR}/my_o1heap_config.h\"" #ifdef O1HEAP_CONFIG_HEADER # include O1HEAP_CONFIG_HEADER #endif /// The assertion macro defaults to the standard assert(). /// It can be overridden to manually suppress assertion checks or use a different error handling policy. #ifndef O1HEAP_ASSERT // Intentional violation of MISRA: the assertion check macro cannot be replaced with a function definition. # define O1HEAP_ASSERT(x) assert(x) // NOSONAR #endif /// Allow usage of compiler intrinsics for branch annotation and CLZ. #ifndef O1HEAP_USE_INTRINSICS # define O1HEAP_USE_INTRINSICS 1 #endif /// Branch probability annotations are used to improve the worst case execution time (WCET). They are entirely optional. #if O1HEAP_USE_INTRINSICS && !defined(O1HEAP_LIKELY) # if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) // Intentional violation of MISRA: branch hinting macro cannot be replaced with a function definition. # define O1HEAP_LIKELY(x) __builtin_expect((x), 1) // NOSONAR # endif #endif #ifndef O1HEAP_LIKELY # define O1HEAP_LIKELY(x) x #endif /// This option is used for testing only. Do not use in production. #ifndef O1HEAP_PRIVATE # define O1HEAP_PRIVATE static inline #endif /// Count leading zeros (CLZ) is used for fast computation of binary logarithm (which needs to be done very often). /// Most of the modern processors (including the embedded ones) implement dedicated hardware support for fast CLZ /// computation, which is available via compiler intrinsics. The default implementation will automatically use /// the intrinsics for some of the compilers; for others it will default to the slow software emulation, /// which can be overridden by the user via O1HEAP_CONFIG_HEADER. The library guarantees that the argument is positive. #if O1HEAP_USE_INTRINSICS && !defined(O1HEAP_CLZ) # if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) # if SIZE_MAX == 0xFFFFFFFFFFFFFFFF # define O1HEAP_CLZ __builtin_clzll # else # define O1HEAP_CLZ __builtin_clzl # endif # endif #endif #ifndef O1HEAP_CLZ O1HEAP_PRIVATE uint_fast8_t O1HEAP_CLZ(const size_t x) { O1HEAP_ASSERT(x > 0); size_t t = ((size_t)1U) << ((sizeof(size_t) * CHAR_BIT) - 1U); uint_fast8_t r = 0; while ((x & t) == 0) { t >>= 1U; r++; } return r; } #endif // ---------------------------------------- INTERNAL DEFINITIONS ---------------------------------------- #if __STDC_VERSION__ < 201112L // Intentional violation of MISRA: static assertion macro cannot be replaced with a function definition. # define static_assert(x, ...) typedef char _static_assert_gl(_static_assertion_, __LINE__)[(x) ? 1 : -1] // NOSONAR # define _static_assert_gl(a, b) _static_assert_gl_impl(a, b) // NOSONAR // Intentional violation of MISRA: the paste operator ## cannot be avoided in this context. # define _static_assert_gl_impl(a, b) a##b // NOSONAR #endif /// The overhead is at most O1HEAP_ALIGNMENT bytes large, /// then follows the user data which shall keep the next fragment aligned. #define FRAGMENT_SIZE_MIN (O1HEAP_ALIGNMENT * 2U) /// This is risky, handle with care: if the allocation amount plus per-fragment overhead exceeds 2**(b-1), /// where b is the pointer bit width, then ceil(log2(amount)) yields b; then 2**b causes an integer overflow. /// To avoid this, we put a hard limit on fragment size (which is amount + per-fragment overhead): 2**(b-1) #define FRAGMENT_SIZE_MAX ((SIZE_MAX >> 1U) + 1U) /// Normally we should subtract log2(FRAGMENT_SIZE_MIN) but log2 is bulky to compute using the preprocessor only. /// We will certainly end up with unused bins this way, but it is cheap to ignore. #define NUM_BINS_MAX (sizeof(size_t) * CHAR_BIT) static_assert((O1HEAP_ALIGNMENT& (O1HEAP_ALIGNMENT - 1U)) == 0U, "Not a power of 2"); static_assert((FRAGMENT_SIZE_MIN& (FRAGMENT_SIZE_MIN - 1U)) == 0U, "Not a power of 2"); static_assert((FRAGMENT_SIZE_MAX& (FRAGMENT_SIZE_MAX - 1U)) == 0U, "Not a power of 2"); typedef struct Fragment Fragment; typedef struct FragmentHeader { Fragment* next; Fragment* prev; size_t size; bool used; } FragmentHeader; static_assert(sizeof(FragmentHeader) <= O1HEAP_ALIGNMENT, "Memory layout error"); struct Fragment { FragmentHeader header; // Everything past the header may spill over into the allocatable space. The header survives across alloc/free. Fragment* next_free; // Next free fragment in the bin; NULL in the last one. Fragment* prev_free; // Same but points back; NULL in the first one. }; static_assert(sizeof(Fragment) <= FRAGMENT_SIZE_MIN, "Memory layout error"); struct O1HeapInstance { Fragment* bins[NUM_BINS_MAX]; ///< Smallest fragments are in the bin at index 0. size_t nonempty_bin_mask; ///< Bit 1 represents a non-empty bin; bin at index 0 is for the smallest fragments. O1HeapDiagnostics diagnostics; }; /// The amount of space allocated for the heap instance. /// Its size is padded up to O1HEAP_ALIGNMENT to ensure correct alignment of the allocation arena that follows. #define INSTANCE_SIZE_PADDED ((sizeof(O1HeapInstance) + O1HEAP_ALIGNMENT - 1U) & ~(O1HEAP_ALIGNMENT - 1U)) static_assert(INSTANCE_SIZE_PADDED >= sizeof(O1HeapInstance), "Invalid instance footprint computation"); static_assert((INSTANCE_SIZE_PADDED% O1HEAP_ALIGNMENT) == 0U, "Invalid instance footprint computation"); /// Undefined for zero argument. O1HEAP_PRIVATE uint_fast8_t log2Floor(const size_t x) { O1HEAP_ASSERT(x > 0); // NOLINTNEXTLINE redundant cast to the same type. return (uint_fast8_t)(((sizeof(x) * CHAR_BIT) - 1U) - ((uint_fast8_t)O1HEAP_CLZ(x))); } /// Special case: if the argument is zero, returns zero. O1HEAP_PRIVATE uint_fast8_t log2Ceil(const size_t x) { // NOLINTNEXTLINE redundant cast to the same type. return (x <= 1U) ? 0U : (uint_fast8_t)((sizeof(x) * CHAR_BIT) - ((uint_fast8_t)O1HEAP_CLZ(x - 1U))); } /// Raise 2 into the specified power. /// You might be tempted to do something like (1U << power). WRONG! We humans are prone to forgetting things. /// If you forget to cast your 1U to size_t or ULL, you may end up with undefined behavior. O1HEAP_PRIVATE size_t pow2(const uint_fast8_t power) { return ((size_t)1U) << power; } /// This is equivalent to pow2(log2Ceil(x)). Undefined for x<2. O1HEAP_PRIVATE size_t roundUpToPowerOf2(const size_t x) { O1HEAP_ASSERT(x >= 2U); // NOLINTNEXTLINE redundant cast to the same type. return ((size_t)1U) << ((sizeof(x) * CHAR_BIT) - ((uint_fast8_t)O1HEAP_CLZ(x - 1U))); } /// Links two fragments so that their next/prev pointers point to each other; left goes before right. O1HEAP_PRIVATE void interlink(Fragment* const left, Fragment* const right) { if (O1HEAP_LIKELY(left != NULL)) { left->header.next = right; } if (O1HEAP_LIKELY(right != NULL)) { right->header.prev = left; } } /// Adds a new fragment into the appropriate bin and updates the lookup mask. O1HEAP_PRIVATE void rebin(O1HeapInstance* const handle, Fragment* const fragment) { O1HEAP_ASSERT(handle != NULL); O1HEAP_ASSERT(fragment != NULL); O1HEAP_ASSERT(fragment->header.size >= FRAGMENT_SIZE_MIN); O1HEAP_ASSERT((fragment->header.size % FRAGMENT_SIZE_MIN) == 0U); const uint_fast8_t idx = log2Floor(fragment->header.size / FRAGMENT_SIZE_MIN); // Round DOWN when inserting. O1HEAP_ASSERT(idx < NUM_BINS_MAX); // Add the new fragment to the beginning of the bin list. // I.e., each allocation will be returning the most-recently-used fragment -- good for caching. fragment->next_free = handle->bins[idx]; fragment->prev_free = NULL; if (O1HEAP_LIKELY(handle->bins[idx] != NULL)) { handle->bins[idx]->prev_free = fragment; } handle->bins[idx] = fragment; handle->nonempty_bin_mask |= pow2(idx); } /// Removes the specified fragment from its bin. O1HEAP_PRIVATE void unbin(O1HeapInstance* const handle, const Fragment* const fragment) { O1HEAP_ASSERT(handle != NULL); O1HEAP_ASSERT(fragment != NULL); O1HEAP_ASSERT(fragment->header.size >= FRAGMENT_SIZE_MIN); O1HEAP_ASSERT((fragment->header.size % FRAGMENT_SIZE_MIN) == 0U); const uint_fast8_t idx = log2Floor(fragment->header.size / FRAGMENT_SIZE_MIN); // Round DOWN when removing. O1HEAP_ASSERT(idx < NUM_BINS_MAX); // Remove the bin from the free fragment list. if (O1HEAP_LIKELY(fragment->next_free != NULL)) { fragment->next_free->prev_free = fragment->prev_free; } if (O1HEAP_LIKELY(fragment->prev_free != NULL)) { fragment->prev_free->next_free = fragment->next_free; } // Update the bin header. if (O1HEAP_LIKELY(handle->bins[idx] == fragment)) { O1HEAP_ASSERT(fragment->prev_free == NULL); handle->bins[idx] = fragment->next_free; if (O1HEAP_LIKELY(handle->bins[idx] == NULL)) { handle->nonempty_bin_mask &= ~pow2(idx); } } } // ---------------------------------------- PUBLIC API IMPLEMENTATION ---------------------------------------- O1HeapInstance* o1heapInit(void* const base, const size_t size) { O1HeapInstance* out = NULL; if ((base != NULL) && ((((size_t)base) % O1HEAP_ALIGNMENT) == 0U) && (size >= (INSTANCE_SIZE_PADDED + FRAGMENT_SIZE_MIN))) { // Allocate the core heap metadata structure in the beginning of the arena. O1HEAP_ASSERT(((size_t)base) % sizeof(O1HeapInstance*) == 0U); out = (O1HeapInstance*)base; out->nonempty_bin_mask = 0U; for (size_t i = 0; i < NUM_BINS_MAX; i++) { out->bins[i] = NULL; } // Limit and align the capacity. size_t capacity = size - INSTANCE_SIZE_PADDED; if (capacity > FRAGMENT_SIZE_MAX) { capacity = FRAGMENT_SIZE_MAX; } while ((capacity % FRAGMENT_SIZE_MIN) != 0) { O1HEAP_ASSERT(capacity > 0U); capacity--; } O1HEAP_ASSERT((capacity % FRAGMENT_SIZE_MIN) == 0); O1HEAP_ASSERT((capacity >= FRAGMENT_SIZE_MIN) && (capacity <= FRAGMENT_SIZE_MAX)); // Initialize the root fragment. Fragment* const frag = (Fragment*)(void*)(((char*)base) + INSTANCE_SIZE_PADDED); O1HEAP_ASSERT((((size_t)frag) % O1HEAP_ALIGNMENT) == 0U); frag->header.next = NULL; frag->header.prev = NULL; frag->header.size = capacity; frag->header.used = false; frag->next_free = NULL; frag->prev_free = NULL; rebin(out, frag); O1HEAP_ASSERT(out->nonempty_bin_mask != 0U); // Initialize the diagnostics. out->diagnostics.capacity = capacity; out->diagnostics.allocated = 0U; out->diagnostics.peak_allocated = 0U; out->diagnostics.peak_request_size = 0U; out->diagnostics.oom_count = 0U; } return out; } void* o1heapAllocate(O1HeapInstance* const handle, const size_t amount) { O1HEAP_ASSERT(handle != NULL); O1HEAP_ASSERT(handle->diagnostics.capacity <= FRAGMENT_SIZE_MAX); void* out = NULL; // If the amount approaches approx. SIZE_MAX/2, an undetected integer overflow may occur. // To avoid that, we do not attempt allocation if the amount exceeds the hard limit. // We perform multiple redundant checks to account for a possible unaccounted overflow. if (O1HEAP_LIKELY((amount > 0U) && (amount <= (handle->diagnostics.capacity - O1HEAP_ALIGNMENT)))) { // Add the header size and align the allocation size to the power of 2. // See "Timing-Predictable Memory Allocation In Hard Real-Time Systems", Herter, page 27. const size_t fragment_size = roundUpToPowerOf2(amount + O1HEAP_ALIGNMENT); O1HEAP_ASSERT(fragment_size <= FRAGMENT_SIZE_MAX); O1HEAP_ASSERT(fragment_size >= FRAGMENT_SIZE_MIN); O1HEAP_ASSERT(fragment_size >= amount + O1HEAP_ALIGNMENT); O1HEAP_ASSERT((fragment_size & (fragment_size - 1U)) == 0U); // Is power of 2. const uint_fast8_t optimal_bin_index = log2Ceil(fragment_size / FRAGMENT_SIZE_MIN); // Use CEIL when fetching. O1HEAP_ASSERT(optimal_bin_index < NUM_BINS_MAX); const size_t candidate_bin_mask = ~(pow2(optimal_bin_index) - 1U); // Find the smallest non-empty bin we can use. const size_t suitable_bins = handle->nonempty_bin_mask & candidate_bin_mask; const size_t smallest_bin_mask = suitable_bins & ~(suitable_bins - 1U); // Clear all bits but the lowest. if (O1HEAP_LIKELY(smallest_bin_mask != 0)) { O1HEAP_ASSERT((smallest_bin_mask & (smallest_bin_mask - 1U)) == 0U); // Is power of 2. const uint_fast8_t bin_index = log2Floor(smallest_bin_mask); O1HEAP_ASSERT(bin_index >= optimal_bin_index); O1HEAP_ASSERT(bin_index < NUM_BINS_MAX); // The bin we found shall not be empty, otherwise it's a state divergence (memory corruption?). Fragment* const frag = handle->bins[bin_index]; O1HEAP_ASSERT(frag != NULL); O1HEAP_ASSERT(frag->header.size >= fragment_size); O1HEAP_ASSERT((frag->header.size % FRAGMENT_SIZE_MIN) == 0U); O1HEAP_ASSERT(!frag->header.used); unbin(handle, frag); // Split the fragment if it is too large. const size_t leftover = frag->header.size - fragment_size; frag->header.size = fragment_size; O1HEAP_ASSERT(leftover < handle->diagnostics.capacity); // Overflow check. O1HEAP_ASSERT(leftover % FRAGMENT_SIZE_MIN == 0U); // Alignment check. if (O1HEAP_LIKELY(leftover >= FRAGMENT_SIZE_MIN)) { Fragment* const new_frag = (Fragment*)(void*)(((char*)frag) + fragment_size); O1HEAP_ASSERT(((size_t)new_frag) % O1HEAP_ALIGNMENT == 0U); new_frag->header.size = leftover; new_frag->header.used = false; interlink(new_frag, frag->header.next); interlink(frag, new_frag); rebin(handle, new_frag); } // Update the diagnostics. O1HEAP_ASSERT((handle->diagnostics.allocated % FRAGMENT_SIZE_MIN) == 0U); handle->diagnostics.allocated += fragment_size; O1HEAP_ASSERT(handle->diagnostics.allocated <= handle->diagnostics.capacity); if (O1HEAP_LIKELY(handle->diagnostics.peak_allocated < handle->diagnostics.allocated)) { handle->diagnostics.peak_allocated = handle->diagnostics.allocated; } // Finalize the fragment we just allocated. O1HEAP_ASSERT(frag->header.size >= amount + O1HEAP_ALIGNMENT); frag->header.used = true; out = ((char*)frag) + O1HEAP_ALIGNMENT; } } // Update the diagnostics. if (O1HEAP_LIKELY(handle->diagnostics.peak_request_size < amount)) { handle->diagnostics.peak_request_size = amount; } if (O1HEAP_LIKELY((out == NULL) && (amount > 0U))) { handle->diagnostics.oom_count++; } return out; } void o1heapFree(O1HeapInstance* const handle, void* const pointer) { O1HEAP_ASSERT(handle != NULL); O1HEAP_ASSERT(handle->diagnostics.capacity <= FRAGMENT_SIZE_MAX); if (O1HEAP_LIKELY(pointer != NULL)) // NULL pointer is a no-op. { Fragment* const frag = (Fragment*)(void*)(((char*)pointer) - O1HEAP_ALIGNMENT); // Check for heap corruption in debug builds. O1HEAP_ASSERT(((size_t)frag) % sizeof(Fragment*) == 0U); O1HEAP_ASSERT(((size_t)frag) >= (((size_t)handle) + INSTANCE_SIZE_PADDED)); O1HEAP_ASSERT(((size_t)frag) <= (((size_t)handle) + INSTANCE_SIZE_PADDED + handle->diagnostics.capacity - FRAGMENT_SIZE_MIN)); O1HEAP_ASSERT(frag->header.used); // Catch double-free O1HEAP_ASSERT(((size_t)frag->header.next) % sizeof(Fragment*) == 0U); O1HEAP_ASSERT(((size_t)frag->header.prev) % sizeof(Fragment*) == 0U); O1HEAP_ASSERT(frag->header.size >= FRAGMENT_SIZE_MIN); O1HEAP_ASSERT(frag->header.size <= handle->diagnostics.capacity); O1HEAP_ASSERT((frag->header.size % FRAGMENT_SIZE_MIN) == 0U); // Even if we're going to drop the fragment later, mark it free anyway to prevent double-free. frag->header.used = false; // Update the diagnostics. It must be done before merging because it invalidates the fragment size information. O1HEAP_ASSERT(handle->diagnostics.allocated >= frag->header.size); // Heap corruption check. handle->diagnostics.allocated -= frag->header.size; // Merge with siblings and insert the returned fragment into the appropriate bin and update metadata. Fragment* const prev = frag->header.prev; Fragment* const next = frag->header.next; const bool join_left = (prev != NULL) && (!prev->header.used); const bool join_right = (next != NULL) && (!next->header.used); if (join_left && join_right) // [ prev ][ this ][ next ] => [ ------- prev ------- ] { unbin(handle, prev); unbin(handle, next); prev->header.size += frag->header.size + next->header.size; frag->header.size = 0; // Invalidate the dropped fragment headers to prevent double-free. next->header.size = 0; O1HEAP_ASSERT((prev->header.size % FRAGMENT_SIZE_MIN) == 0U); interlink(prev, next->header.next); rebin(handle, prev); } else if (join_left) // [ prev ][ this ][ next ] => [ --- prev --- ][ next ] { unbin(handle, prev); prev->header.size += frag->header.size; frag->header.size = 0; O1HEAP_ASSERT((prev->header.size % FRAGMENT_SIZE_MIN) == 0U); interlink(prev, next); rebin(handle, prev); } else if (join_right) // [ prev ][ this ][ next ] => [ prev ][ --- this --- ] { unbin(handle, next); frag->header.size += next->header.size; next->header.size = 0; O1HEAP_ASSERT((frag->header.size % FRAGMENT_SIZE_MIN) == 0U); interlink(frag, next->header.next); rebin(handle, frag); } else { rebin(handle, frag); } } } bool o1heapDoInvariantsHold(const O1HeapInstance* const handle) { O1HEAP_ASSERT(handle != NULL); bool valid = true; // Check the bin mask consistency. for (size_t i = 0; i < NUM_BINS_MAX; i++) // Dear compiler, feel free to unroll this loop. { const bool mask_bit_set = (handle->nonempty_bin_mask & pow2((uint_fast8_t)i)) != 0U; const bool bin_nonempty = handle->bins[i] != NULL; valid = valid && (mask_bit_set == bin_nonempty); } // Create a local copy of the diagnostics struct. const O1HeapDiagnostics diag = handle->diagnostics; // Capacity check. valid = valid && (diag.capacity <= FRAGMENT_SIZE_MAX) && (diag.capacity >= FRAGMENT_SIZE_MIN) && ((diag.capacity % FRAGMENT_SIZE_MIN) == 0U); // Allocation info check. valid = valid && (diag.allocated <= diag.capacity) && ((diag.allocated % FRAGMENT_SIZE_MIN) == 0U) && (diag.peak_allocated <= diag.capacity) && (diag.peak_allocated >= diag.allocated) && ((diag.peak_allocated % FRAGMENT_SIZE_MIN) == 0U); // Peak request check valid = valid && ((diag.peak_request_size < diag.capacity) || (diag.oom_count > 0U)); if (diag.peak_request_size == 0U) { valid = valid && (diag.peak_allocated == 0U) && (diag.allocated == 0U) && (diag.oom_count == 0U); } else { valid = valid && // Overflow on summation is possible but safe to ignore. (((diag.peak_request_size + O1HEAP_ALIGNMENT) <= diag.peak_allocated) || (diag.oom_count > 0U)); } return valid; } O1HeapDiagnostics o1heapGetDiagnostics(const O1HeapInstance* const handle) { O1HEAP_ASSERT(handle != NULL); const O1HeapDiagnostics out = handle->diagnostics; return out; } ================================================ FILE: thirdparty/o1heap/o1heap.h ================================================ // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions // of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2020 Pavel Kirienko // Authors: Pavel Kirienko // // READ THE DOCUMENTATION IN README.md. #ifndef O1HEAP_H_INCLUDED #define O1HEAP_H_INCLUDED #include #include #include #ifdef __cplusplus extern "C" { #endif /// The semantic version number of this distribution. #define O1HEAP_VERSION_MAJOR 2 /// The guaranteed alignment depends on the platform pointer width. #define O1HEAP_ALIGNMENT (sizeof(void*) * 4U) /// The definition is private, so the user code can only operate on pointers. This is done to enforce encapsulation. typedef struct O1HeapInstance O1HeapInstance; /// Runtime diagnostic information. This information can be used to facilitate runtime self-testing, /// as required by certain safety-critical development guidelines. /// If assertion checks are not disabled, the library will perform automatic runtime self-diagnostics that trigger /// an assertion failure if a heap corruption is detected. /// Health checks and validation can be done with o1heapDoInvariantsHold(). typedef struct { /// The total amount of memory available for serving allocation requests (heap size). /// The maximum allocation size is (capacity - O1HEAP_ALIGNMENT). /// This parameter does not include the overhead used up by O1HeapInstance and arena alignment. /// This parameter is constant. size_t capacity; /// The amount of memory that is currently allocated, including the per-fragment overhead and size alignment. /// For example, if the application requested a fragment of size 1 byte, the value reported here may be 32 bytes. size_t allocated; /// The maximum value of 'allocated' seen since initialization. This parameter is never decreased. size_t peak_allocated; /// The largest amount of memory that the allocator has attempted to allocate (perhaps unsuccessfully) /// since initialization (not including the rounding and the allocator's own per-fragment overhead, /// so the total is larger). This parameter is never decreased. The initial value is zero. size_t peak_request_size; /// The number of times an allocation request could not be completed due to the lack of memory or /// excessive fragmentation. OOM stands for "out of memory". This parameter is never decreased. uint64_t oom_count; } O1HeapDiagnostics; /// The arena base pointer shall be aligned at O1HEAP_ALIGNMENT, otherwise NULL is returned. /// /// The total heap capacity cannot exceed approx. (SIZE_MAX/2). If the arena size allows for a larger heap, /// the excess will be silently truncated away (no error). This is not a realistic use case because a typical /// application is unlikely to be able to dedicate that much of the address space for the heap. /// /// The function initializes a new heap instance allocated in the provided arena, taking some of its space for its /// own needs (normally about 40..600 bytes depending on the architecture, but this parameter is not characterized). /// A pointer to the newly initialized instance is returned. /// /// If the provided space is insufficient, NULL is returned. /// /// An initialized instance does not hold any resources. Therefore, if the instance is no longer needed, /// it can be discarded without any de-initialization procedures. /// /// The heap is not thread-safe; external synchronization may be required. O1HeapInstance* o1heapInit(void* const base, const size_t size); /// The semantics follows malloc() with additional guarantees the full list of which is provided below. /// /// If the allocation request is served successfully, a pointer to the newly allocated memory fragment is returned. /// The returned pointer is guaranteed to be aligned at O1HEAP_ALIGNMENT. /// /// If the allocation request cannot be served due to the lack of memory or its excessive fragmentation, /// a NULL pointer is returned. /// /// The function is executed in constant time. /// The allocated memory is NOT zero-filled (because zero-filling is a variable-complexity operation). void* o1heapAllocate(O1HeapInstance* const handle, const size_t amount); /// The semantics follows free() with additional guarantees the full list of which is provided below. /// /// If the pointer does not point to a previously allocated block and is not NULL, the behavior is undefined. /// Builds where assertion checks are enabled may trigger an assertion failure for some invalid inputs. /// /// The function is executed in constant time. void o1heapFree(O1HeapInstance* const handle, void* const pointer); /// Performs a basic sanity check on the heap. /// This function can be used as a weak but fast method of heap corruption detection. /// If the handle pointer is NULL, the behavior is undefined. /// The time complexity is constant. /// The return value is truth if the heap looks valid, falsity otherwise. bool o1heapDoInvariantsHold(const O1HeapInstance* const handle); /// Samples and returns a copy of the diagnostic information, see O1HeapDiagnostics. /// This function merely copies the structure from an internal storage, so it is fast to return. /// If the handle pointer is NULL, the behavior is undefined. O1HeapDiagnostics o1heapGetDiagnostics(const O1HeapInstance* const handle); #ifdef __cplusplus } #endif #endif // O1HEAP_H_INCLUDED ================================================ FILE: toolchains/linux-clang.cmake ================================================ # Copied and adapted from: # https://github.com/microsoft/vcpkg/blob/7adc2e4d49e8d0efc07a369079faa6bc3dbb90f3/scripts/toolchains/linux.cmake # # The original seems to be written only for GCC. This version is changed to use clang and # LLVM bin tools. if(NOT _VCPKG_LINUX_CLANG_TOOLCHAIN) set(_VCPKG_LINUX_CLANG_TOOLCHAIN 1) if(POLICY CMP0056) cmake_policy(SET CMP0056 NEW) endif() if(POLICY CMP0066) cmake_policy(SET CMP0066 NEW) endif() if(POLICY CMP0067) cmake_policy(SET CMP0067 NEW) endif() if(POLICY CMP0137) cmake_policy(SET CMP0137 NEW) endif() list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES VCPKG_CRT_LINKAGE VCPKG_TARGET_ARCHITECTURE VCPKG_C_FLAGS VCPKG_CXX_FLAGS VCPKG_C_FLAGS_DEBUG VCPKG_CXX_FLAGS_DEBUG VCPKG_C_FLAGS_RELEASE VCPKG_CXX_FLAGS_RELEASE VCPKG_LINKER_FLAGS VCPKG_LINKER_FLAGS_RELEASE VCPKG_LINKER_FLAGS_DEBUG ) set(CMAKE_SYSTEM_NAME Linux CACHE STRING "") # Set compiler to clang set(CMAKE_C_COMPILER clang) set(CMAKE_CXX_COMPILER clang++) SET(CMAKE_ASM_COMPILER clang) # Pick target architecture for clang if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") set(CMAKE_SYSTEM_PROCESSOR x86_64 CACHE STRING "") set(CLANG_TARGET x86_64-unknown-linux-gnu) elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") set(CMAKE_SYSTEM_PROCESSOR i686 CACHE STRING "") set(CLANG_TARGET i686-unknown-linux-gnu) elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") set(CMAKE_SYSTEM_PROCESSOR armv7l CACHE STRING "") set(CLANG_TARGET armv7-unknown-linux-gnueabihf) elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") set(CMAKE_SYSTEM_PROCESSOR aarch64 CACHE STRING "") set(CLANG_TARGET aarch64-unknown-linux-gnu) endif() if(DEFINED CLANG_TARGET) set(CMAKE_C_COMPILER_TARGET ${CLANG_TARGET}) set(CMAKE_CXX_COMPILER_TARGET ${CLANG_TARGET}) set(CMAKE_ASM_COMPILER_TARGET ${CLANG_TARGET}) endif() if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_HOST_SYSTEM_PROCESSOR) set(CMAKE_CROSSCOMPILING OFF CACHE BOOL "") endif() string(APPEND CMAKE_C_FLAGS_INIT " -fPIC ${VCPKG_C_FLAGS} ") string(APPEND CMAKE_CXX_FLAGS_INIT " -fPIC ${VCPKG_CXX_FLAGS} ") string(APPEND CMAKE_C_FLAGS_DEBUG_INIT " ${VCPKG_C_FLAGS_DEBUG} ") string(APPEND CMAKE_CXX_FLAGS_DEBUG_INIT " ${VCPKG_CXX_FLAGS_DEBUG} ") string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " ${VCPKG_C_FLAGS_RELEASE} ") string(APPEND CMAKE_CXX_FLAGS_RELEASE_INIT " ${VCPKG_CXX_FLAGS_RELEASE} ") # Use LLVM's lld linker set(CMAKE_LINKER_TYPE LLD) string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " ${VCPKG_LINKER_FLAGS} ") string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " ${VCPKG_LINKER_FLAGS} ") string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " ${VCPKG_LINKER_FLAGS} ") if(VCPKG_CRT_LINKAGE STREQUAL "static") string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT "-static ") string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT "-static ") string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-static ") endif() string(APPEND CMAKE_MODULE_LINKER_FLAGS_DEBUG_INIT " ${VCPKG_LINKER_FLAGS_DEBUG} ") string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG_INIT " ${VCPKG_LINKER_FLAGS_DEBUG} ") string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG_INIT " ${VCPKG_LINKER_FLAGS_DEBUG} ") string(APPEND CMAKE_MODULE_LINKER_FLAGS_RELEASE_INIT " ${VCPKG_LINKER_FLAGS_RELEASE} ") string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT " ${VCPKG_LINKER_FLAGS_RELEASE} ") string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT " ${VCPKG_LINKER_FLAGS_RELEASE} ") string(APPEND CMAKE_ASM_FLAGS_INIT " ${VCPKG_C_FLAGS} ") endif() ================================================ FILE: tools/CMakeLists.txt ================================================ option(MARATHON_RECOMP_OPTIMIZE_TOOLS "Apply compiler optimizations to build tools." ON) if (MARATHON_RECOMP_OPTIMIZE_TOOLS) if (WIN32) add_compile_options(/O2) else() add_compile_options(-O3) endif() set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) endif() add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}/bc_diff) add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}/file_to_c) add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}/fshasher) add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}/u8extract) add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}/x_decompress) add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}/XenonRecomp) add_subdirectory(${MARATHON_RECOMP_TOOLS_ROOT}/XenosRecomp) ================================================ FILE: tools/bc_diff/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.20) project("bc_diff") set(CMAKE_CXX_STANDARD 17) add_executable(bc_diff "bc_diff.cpp") add_compile_definitions(bc_diff PRIVATE _CRT_SECURE_NO_WARNINGS) target_link_libraries(bc_diff PRIVATE xxHash::xxhash) ================================================ FILE: tools/bc_diff/bc_diff.cpp ================================================ #include "bc_diff.h" #include #include #include #include #include #include static std::vector readAllBytes(const char* filePath) { FILE* file = fopen(filePath, "rb"); if (!file) return {}; fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); std::vector data(fileSize); fread(data.data(), 1, fileSize, file); fclose(file); return data; } int main(int argc, char** argv) { if (argc != 4) { printf("Usage: %s [old directory] [new directory] [destination file]", argv[0]); return 0; } // Debug configuration doesn't compile without this??? assert(argc == 4); std::filesystem::path oldDirectoryPath = argv[1]; std::filesystem::path newDirectoryPath = argv[2]; std::vector entries; std::vector patches; std::vector patchBytes; for (auto& oldFile : std::filesystem::recursive_directory_iterator(oldDirectoryPath)) { auto newFile = newDirectoryPath / std::filesystem::relative(oldFile, oldDirectoryPath); if (!std::filesystem::exists(newFile)) { fprintf(stderr, "Cannot locate %s\n", newFile.string().c_str()); continue; } auto oldFileData = readAllBytes(oldFile.path().string().c_str()); auto newFileData = readAllBytes(newFile.string().c_str()); constexpr size_t BC_STRIDE = 8; if (oldFileData.size() != newFileData.size()) { fprintf(stderr, "%s does not match %s in file size\n", oldFile.path().string().c_str(), newFile.string().c_str()); continue; } if ((oldFileData.size() % BC_STRIDE) != 0) { fprintf(stderr, "%s is not aligned to %d bytes\n", oldFile.path().string().c_str(), BC_STRIDE); continue; } if (oldFileData.size() >= BC_STRIDE) { size_t patchIndex = patches.size(); for (size_t i = 0; i < oldFileData.size() - BC_STRIDE + 1; i += BC_STRIDE) { if (memcmp(&oldFileData[i], &newFileData[i], BC_STRIDE) == 0) continue; size_t patchBytesOffset = patchBytes.size(); patchBytes.insert(patchBytes.end(), newFileData.begin() + i, newFileData.begin() + i + BC_STRIDE); if (patchIndex >= patches.size() || ((patches.back().destinationOffset + patches.back().patchBytesSize) != i)) { auto& patch = patches.emplace_back(); patch.destinationOffset = i; patch.patchBytesOffset = patchBytesOffset; patch.patchBytesSize = BC_STRIDE; } else { patches.back().patchBytesSize += BC_STRIDE; } } size_t patchCount = patches.size() - patchIndex; if (patchCount != 0) { auto& entry = entries.emplace_back(); entry.hash = XXH3_64bits(oldFileData.data(), oldFileData.size()); entry.patchesOffset = patchIndex * sizeof(BlockCompressionDiffPatch); entry.patchCount = patchCount; printf("Generated BC patch for %s\n", oldFile.path().string().c_str()); } else { printf("Skipping %s, files are identical\n", oldFile.path().string().c_str()); } } } std::sort(entries.begin(), entries.end(), [](auto& lhs, auto& rhs) { return lhs.hash < rhs.hash; }); BlockCompressionDiffPatchHeader header; header.entriesOffset = sizeof(BlockCompressionDiffPatchHeader); header.entryCount = entries.size(); size_t patchesOffset = header.entriesOffset + sizeof(BlockCompressionDiffPatchEntry) * entries.size(); size_t patchBytesOffset = patchesOffset + sizeof(BlockCompressionDiffPatch) * patches.size(); for (auto& entry : entries) entry.patchesOffset += patchesOffset; for (auto& patch : patches) patch.patchBytesOffset += patchBytesOffset; FILE* file = fopen(argv[3], "wb"); if (!file) { fprintf(stderr, "Cannot open %s for writing\n", argv[3]); return 1; } fwrite(&header, sizeof(header), 1, file); fwrite(entries.data(), sizeof(BlockCompressionDiffPatchEntry), entries.size(), file); fwrite(patches.data(), sizeof(BlockCompressionDiffPatch), patches.size(), file); fwrite(patchBytes.data(), 1, patchBytes.size(), file); fclose(file); return 0; } ================================================ FILE: tools/bc_diff/bc_diff.h ================================================ #pragma once #include struct BlockCompressionDiffPatch { uint32_t destinationOffset; uint32_t patchBytesOffset; uint32_t patchBytesSize; }; struct BlockCompressionDiffPatchEntry { uint64_t hash; uint32_t patchesOffset; uint32_t patchCount; }; struct BlockCompressionDiffPatchHeader { uint32_t entriesOffset; uint32_t entryCount; }; ================================================ FILE: tools/file_to_c/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.20) include(CMakeParseArguments) project("file_to_c") set(CMAKE_CXX_STANDARD 17) add_executable(file_to_c "file_to_c.cpp") target_link_libraries(file_to_c PRIVATE $,libzstd_static,libzstd_shared>) ================================================ FILE: tools/file_to_c/file_to_c.cpp ================================================ /* MIT License Copyright (c) 2024 RT64 Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include std::vector read_file(const char* path) { std::ifstream input_file{path, std::ios::binary}; std::vector ret{}; if (!input_file.good()) { return ret; } // Get the length of the file input_file.seekg(0, std::ios::end); ret.resize(input_file.tellg()); // Read the file contents into the vector input_file.seekg(0, std::ios::beg); input_file.read(ret.data(), ret.size()); return ret; } void create_parent_if_needed(const char* path) { std::filesystem::path parent_path = std::filesystem::path{path}.parent_path(); if (!parent_path.empty()) { std::filesystem::create_directories(parent_path); } } int main(int argc, const char** argv) { if (argc != 6) { printf("Usage: %s [input file] [array name] [compression type] [output C file] [output C header]\n", argv[0]); return EXIT_SUCCESS; } const char* input_path = argv[1]; const char* array_name = argv[2]; std::string compression_type = argv[3]; const char* output_c_path = argv[4]; const char* output_h_path = argv[5]; // Read the input file's contents std::vector contents = read_file(input_path); if (contents.empty()) { fprintf(stderr, "Failed to open file %s! (Or it's empty)\n", input_path); return EXIT_FAILURE; } // Compress if requested. std::vector compressed_contents; std::transform(compression_type.begin(), compression_type.end(), compression_type.begin(), tolower); if (compression_type == "zstd") { size_t bound_size = ZSTD_compressBound(contents.size()); compressed_contents.resize(bound_size); size_t compressed_size = ZSTD_compress(compressed_contents.data(), bound_size, contents.data(), contents.size(), ZSTD_maxCLevel()); compressed_contents.resize(compressed_size); } else if (compression_type != "none") { fprintf(stderr, "Unknown compression type %s!", compression_type.c_str()); return EXIT_FAILURE; } // Create the output directories if they don't exist create_parent_if_needed(output_c_path); create_parent_if_needed(output_h_path); // Write the C file with the array std::vector& contents_to_write = !compressed_contents.empty() ? compressed_contents : contents; { std::ofstream output_c_file{output_c_path}; output_c_file << "extern unsigned char " << array_name << "[" << contents_to_write.size() << "];\n"; output_c_file << "unsigned char " << array_name << "[" << contents_to_write.size() << "] = {"; for (char x : contents_to_write) { output_c_file << (int)(unsigned char)x << ", "; } output_c_file << "};\n"; // Write decompressed size. if (!compressed_contents.empty()) { output_c_file << "extern unsigned long long " << array_name << "_uncompressed_size;\n"; output_c_file << "unsigned long long " << array_name << "_uncompressed_size = " << contents.size() << ";\n"; } } // Write the header file with the extern array { std::ofstream output_h_file{output_h_path}; output_h_file << "#ifdef __cplusplus\n" " extern \"C\" {\n" "#endif\n" "extern unsigned char " << array_name << "[" << contents_to_write.size() << "];\n"; // Write decompressed size. if (!compressed_contents.empty()) { output_h_file << "extern unsigned long long " << array_name << "_uncompressed_size;\n"; } output_h_file << "#ifdef __cplusplus\n" " }\n" "#endif\n"; } } ================================================ FILE: tools/fshasher/CMakeLists.txt ================================================ project("fshasher") add_executable(fshasher "fshasher.cpp") target_link_libraries(fshasher PRIVATE xxHash::xxhash) ================================================ FILE: tools/fshasher/fshasher.cpp ================================================ // // fshasher - CLI tool to generate a hash map from a file system. // // This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any // means. // // In jurisdictions that recognize copyright laws, the author or authors // of this software dedicate any and all copyright interest in the // software to the public domain. We make this dedication for the benefit // of the public at large and to the detriment of our heirs and // successors. We intend this dedication to be an overt act of // relinquishment in perpetuity of all present and future rights to this // software under copyright law. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #include #include #include #include #include #include #include #include #include "plainargs.h" void showHelp() { std::cout << "fshasher --directory --source --header
--variable " << std::endl; } int process(const std::list &searchDirectories, std::ofstream &outputSourceStream, std::ofstream &outputHeaderStream, const std::string &variableName) { auto writeExterns = [&](std::ofstream &outputStream) { outputStream << "extern const uint64_t " << variableName << "Hashes[];" << std::endl; outputStream << "extern const std::pair " << variableName << "Files[];" << std::endl; outputStream << "extern const size_t " << variableName << "FilesSize;" << std::endl << std::endl; }; // Generate header. outputHeaderStream << "// File automatically generated by fshasher" << std::endl << std::endl; outputHeaderStream << "#pragma once" << std::endl << std::endl; outputHeaderStream << "#include " << std::endl << std::endl; writeExterns(outputHeaderStream); if (outputHeaderStream.bad()) { std::cerr << "Failed to write to output header." << std::endl; return 1; } outputSourceStream << "// File automatically generated by fshasher" << std::endl << std::endl; outputSourceStream << "#include " << std::endl << std::endl; writeExterns(outputSourceStream); std::map> fileHashSets; char fileData[65536]; XXH3_state_t xxh3; for (const std::filesystem::path &searchDirectory : searchDirectories) { if (!std::filesystem::is_directory(searchDirectory)) { std::cerr << "Specified directory " << searchDirectory << " does not exist." << std::endl; return 1; } for (const std::filesystem::directory_entry &entry : std::filesystem::recursive_directory_iterator(searchDirectory)) { if (!entry.is_regular_file()) { continue; } std::filesystem::path entryPath = entry.path(); std::filesystem::path entryRelative = std::filesystem::relative(entryPath, searchDirectory); std::ifstream entryStream(entryPath, std::ios::binary); if (!entryStream.is_open()) { std::cerr << "Could not open " << entryPath << " for reading." << std::endl; return 1; } std::cout << "Reading " << entryRelative << "." << std::endl; XXH3_64bits_reset(&xxh3); while (!entryStream.eof() && !entryStream.bad()) { entryStream.read(fileData, sizeof(fileData)); XXH3_64bits_update(&xxh3, fileData, entryStream.gcount()); } if (entryStream.bad()) { std::cerr << "Could not read " << entryPath << " successfully." << std::endl; return 1; } std::u8string entryRelativeU8 = entryRelative.u8string(); std::replace(entryRelativeU8.begin(), entryRelativeU8.end(), '\\', '/'); fileHashSets[entryRelativeU8].insert(XXH3_64bits_digest(&xxh3)); } } outputSourceStream << "const uint64_t " << variableName << "Hashes[] = {" << std::endl; for (auto &it : fileHashSets) { for (uint64_t hash : it.second) { outputSourceStream << " " << hash << "ULL," << std::endl; } if (outputSourceStream.bad()) { std::cerr << "Failed to write to output source." << std::endl; return 1; } } outputSourceStream << "};" << std::endl << std::endl; outputSourceStream << "const std::pair " << variableName << "Files[] = {" << std::endl; for (const auto &it : fileHashSets) { outputSourceStream << " { \"" << (const char *)(it.first.c_str()) << "\", " << it.second.size() << " }," << std::endl; if (outputSourceStream.bad()) { std::cerr << "Failed to write to output source." << std::endl; return 1; } } outputSourceStream << "};" << std::endl << std::endl; outputSourceStream << "const size_t " << variableName << "FilesSize = std::size(" << variableName << "Files);" << std::endl; if (outputSourceStream.bad()) { std::cerr << "Failed to write to output source." << std::endl; return 1; } return 0; } int main(int argc, char *argv[]) { plainargs::Result argsResult = plainargs::parse(argc, argv); std::vector directories = argsResult.getValues("directory", "d"); std::string variable = argsResult.getValue("variable", "v"); std::string source = argsResult.getValue("source", "s"); std::string header = argsResult.getValue("header", "h"); if (directories.empty() || variable.empty() || source.empty() || header.empty()) { showHelp(); return 1; } std::filesystem::path sourcePath(source); std::ofstream sourceStream(sourcePath); if (!sourceStream.is_open()) { std::cerr << "Could not open " << sourcePath << " for writing." << std::endl; return 1; } std::filesystem::path headerPath(header); std::ofstream headerStream(headerPath); if (!headerStream.is_open()) { std::cerr << "Could not open " << headerPath << " for writing." << std::endl; return 1; } std::list searchDirectories; for (std::string &directory : directories) { searchDirectories.emplace_back(directory); } int resultCode = process(searchDirectories, sourceStream, headerStream, variable); sourceStream.close(); headerStream.close(); if (resultCode != 0) { std::cerr << "Failed to generate " << sourcePath << "and" << headerPath << "." << std::endl; std::filesystem::remove(sourcePath); std::filesystem::remove(headerPath); } return resultCode; } ================================================ FILE: tools/fshasher/plainargs.h ================================================ // // plainargs - A very plain CLI arguments parsing header-only library. // // This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any // means. // // In jurisdictions that recognize copyright laws, the author or authors // of this software dedicate any and all copyright interest in the // software to the public domain. We make this dedication for the benefit // of the public at large and to the detriment of our heirs and // successors. We intend this dedication to be an overt act of // relinquishment in perpetuity of all present and future rights to this // software under copyright law. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #include #include #include #include namespace plainargs { class Result { private: struct Option { uint32_t keyIndex; uint32_t valueCount; }; std::string directory; std::vector arguments; std::vector