From 381293e2b8dc8ec004fc13730cc209cd92d41519 Mon Sep 17 00:00:00 2001 From: CraftyBoss Date: Thu, 16 Jun 2022 14:33:18 -0700 Subject: [PATCH] First Commit --- .clang-format | 74 + .clangd | 2 + .gitignore | 9 + Makefile | 55 + MakefileNSO | 176 + README.md | 82 + exported.txt | 5 + include/Keyboard.hpp | 49 + include/SocketBase.hpp | 41 + include/actors/PuppetActor.h | 85 + include/actors/PuppetCapActor.h | 28 + include/actors/PuppetHackActor.h | 28 + include/agl/DisplayList.h | 52 + include/agl/DrawContext.h | 41 + include/agl/RenderBuffer.h | 48 + include/agl/RenderTargetColor.h | 34 + include/agl/RenderTargetDepth.h | 29 + include/agl/TextureData.h | 63 + include/agl/detail/FileIOMgr.h | 77 + include/agl/detail/MemoryPool.h | 39 + include/agl/detail/MemoryPoolHeap.h | 36 + include/agl/detail/ShaderHolder.h | 32 + include/agl/detail/Surface.h | 56 + include/agl/driver/NVNimage.h | 26 + include/agl/driver/NVNsampler.h | 31 + include/agl/driver/NVNtexture.h | 39 + include/agl/g3d.h | 3 + include/agl/g3d/g3d_ResFile.h | 16 + include/agl/gpu.h | 93 + include/agl/shader/Shader.h | 69 + include/agl/shader/ShaderCompileInfo.h | 42 + include/agl/shader/ShaderProgram.h | 67 + include/agl/shader/ShaderProgramArchive.h | 38 + include/agl/shtxt/Clause.h | 56 + include/agl/shtxt/DefineLinker.h | 45 + include/agl/shtxt/ExpressionEvaluator.h | 53 + include/agl/shtxt/Lexer.h | 36 + include/agl/shtxt/Preprocessor.h | 63 + include/agl/shtxt/SyntaxTree.h | 39 + include/agl/util.h | 109 + include/agl/utl.h | 23 + include/al/HtmlViewer.h | 12 + include/al/LiveActor/LiveActor.h | 124 + include/al/LiveActor/LiveActorGroup.h | 21 + include/al/PlayerHolder/PlayerHolder.h | 30 + include/al/Scene.hpp | 9 + include/al/action/ActionEffectCtrl.h | 11 + include/al/actor/ActorActionKeeper.h | 47 + include/al/actor/ActorCameraTarget.h | 25 + include/al/actor/ActorDimensionKeeper.h | 21 + include/al/actor/ActorExecuteInfo.h | 12 + include/al/actor/ActorInitInfo.h | 77 + include/al/actor/ActorSceneInfo.h | 53 + include/al/actor/DemoActor.h | 43 + include/al/actor/IUseName.h | 10 + include/al/actor/LiveActorKit.h | 9 + include/al/actor/Placement.h | 36 + include/al/actor/SubActorKeeper.h | 16 + include/al/actor/alPlacementFunction.h | 11 + include/al/actor/misc.h | 11 + include/al/area/AreaObjDirector.h | 12 + include/al/area/AreaObjGroup.h | 10 + include/al/area/ChangeStageInfo.h | 41 + include/al/async/AsyncFunctorThread.h | 28 + include/al/async/FunctorBase.h | 13 + include/al/async/FunctorV0M.hpp | 41 + include/al/audio/AudioDirector.h | 7 + include/al/audio/AudioKeeper.h | 18 + include/al/byaml/ByamlContainerHeader.h | 13 + include/al/byaml/ByamlData.h | 34 + include/al/byaml/ByamlHashPair.h | 19 + include/al/byaml/ByamlHeader.h | 22 + include/al/byaml/ByamlIter.h | 63 + include/al/byaml/ByamlStringTableIter.h | 23 + include/al/byaml/writer/ByamlWriter.h | 74 + .../al/byaml/writer/ByamlWriterBigDataList.h | 27 + include/al/byaml/writer/ByamlWriterData.h | 268 + .../al/byaml/writer/ByamlWriterStringTable.h | 29 + include/al/camera/CameraAngleCtrlInfo.h | 9 + include/al/camera/CameraAngleSwingInfo.h | 9 + include/al/camera/CameraArrowCollider.h | 11 + include/al/camera/CameraDirector.h | 66 + include/al/camera/CameraObjectRequestInfo.h | 7 + include/al/camera/CameraPoseUpdater.h | 9 + include/al/camera/CameraPoser.h | 132 + include/al/camera/CameraPoserFlag.h | 25 + include/al/camera/CameraPoserFollowLimit.h | 9 + include/al/camera/CameraPoserSceneInfo.h | 7 + include/al/camera/CameraStartInfo.h | 7 + include/al/camera/CameraTargetBase.h | 31 + include/al/camera/CameraTargetHolder.h | 10 + include/al/camera/CameraTicket.h | 32 + include/al/camera/CameraTurnInfo.h | 7 + include/al/camera/CameraVerticalAbsorber.h | 10 + include/al/camera/GyroCameraCtrl.h | 9 + include/al/camera/Projection.h | 10 + include/al/camera/SnapshotCameraCtrl.h | 9 + include/al/camera/alCameraPoserFunction.h | 228 + include/al/collision/Collider.h | 43 + include/al/collision/CollisionDirector.h | 12 + include/al/debug/GpuPerf.h | 19 + include/al/effect/Effect.h | 9 + include/al/effect/EffectInfo.h | 10 + include/al/effect/EffectKeeper.h | 17 + include/al/effect/EffectSystemInfo.h | 8 + include/al/effect/EffectUserInfo.h | 19 + include/al/event/IEventFlowEventReceiver.h | 8 + include/al/execute/ExecuteDirector.h | 8 + include/al/execute/IUseExecutor.h | 9 + include/al/factory/ActorFactory.h | 29 + include/al/factory/ActorFactoryEntries100.h | 645 + include/al/factory/CameraPoserFactory.h | 41 + .../al/factory/CameraPoserFactoryEntries100.h | 70 + include/al/factory/Factory.h | 42 + include/al/factory/ProjectActorFactory.h | 9 + .../al/factory/ProjectCameraPoserFactory.h | 9 + include/al/gamepad/util.h | 9 + include/al/hio/HioNode.h | 16 + include/al/layout/BalloonMessage.h | 68 + include/al/layout/CoinCounter.h | 9 + include/al/layout/HtmlViewer.h | 10 + include/al/layout/IUseLayout.h | 18 + include/al/layout/KeyRepeatCtrl.h | 17 + include/al/layout/LayoutActor.h | 87 + include/al/layout/LayoutInitInfo.h | 34 + include/al/layout/LayoutKit.h | 13 + include/al/layout/LayoutSceneInfo.h | 19 + include/al/layout/MenuSelectParts.h | 89 + include/al/layout/SimpleLayoutAppearWaitEnd.h | 24 + include/al/layout/TalkMessageVoicePlayer.h | 19 + include/al/layout/WindowConfirm.h | 38 + include/al/layout/WindowConfirmWait.h | 56 + include/al/message/IUseMessageSystem.h | 10 + include/al/message/MessageSystem.h | 7 + include/al/nerve/ActorStateBase.h | 14 + include/al/nerve/HostStateBase.h | 13 + include/al/nerve/Nerve.h | 25 + include/al/nerve/NerveAction.h | 34 + include/al/nerve/NerveExecutor.h | 21 + include/al/nerve/NerveKeeper.h | 28 + include/al/nerve/NerveStateBase.h | 21 + include/al/nerve/NerveStateCtrl.h | 34 + include/al/pose/ActorPoseKeeper.h | 64 + include/al/rail/RailKeeper.h | 14 + include/al/rail/RailRider.h | 12 + include/al/rumble/PadRumbleDirector.h | 7 + include/al/rumble/PadRumbleParam.h | 15 + include/al/rumble/alPadRumbleFunction.h | 52 + include/al/scene/ISceneObj.h | 12 + include/al/scene/Scene.h | 37 + include/al/scene/SceneCreator.h | 13 + include/al/scene/SceneInitInfo.h | 15 + include/al/scene/SceneObjHolder.h | 27 + include/al/screen/ScreenPointKeeper.h | 10 + include/al/sensor/HitSensor.h | 44 + include/al/sensor/HitSensorKeeper.h | 27 + include/al/sensor/SensorHitGroup.h | 20 + include/al/sensor/SensorMsg.h | 8 + include/al/sequence/Sequence.h | 12 + include/al/sequence/SequenceInitInfo.h | 11 + include/al/stage/StageInfo.h | 12 + include/al/string/StringTmp.h | 17 + include/al/switch/StageSwitchKeeper.h | 21 + include/al/util.hpp | 510 + include/al/util/AudioUtil.h | 18 + include/al/util/CameraUtil.h | 42 + include/al/util/ControllerUtil.h | 40 + include/al/util/GraphicsUtil.h | 26 + include/al/util/LayoutUtil.h | 53 + include/al/util/LiveActorUtil.h | 169 + include/al/util/MathUtil.h | 65 + include/al/util/NerveUtil.h | 59 + include/al/util/VectorUtil.h | 69 + include/algorithms/CaptureAnims.h | 493 + include/algorithms/CaptureTypes.h | 103 + include/algorithms/PlayerAnims.h | 1159 + include/algorithms/WipeTypes.h | 77 + include/algorithms/crc32.h | 122 + include/cameras/CameraPoserCustom.h | 33 + include/debugMenu.hpp | 21 + include/game/Actors/ChurchDoor.h | 7 + include/game/Actors/CoinStackGroup.h | 12 + include/game/Actors/IUseDemoSkip.h | 5 + include/game/Actors/KuriboHack.h | 7 + include/game/Actors/ReflectBombGenerator.h | 3 + include/game/Actors/Shine.h | 38 + include/game/Actors/ShineTowerRocket.h | 9 + include/game/Actors/WorldEndBorderKeeper.h | 21 + .../Controller/ControllerAppletFunction.h | 9 + include/game/GameData/GameDataFile.h | 310 + include/game/GameData/GameDataFunction.h | 141 + include/game/GameData/GameDataHolder.h | 111 + .../game/GameData/GameDataHolderAccessor.h | 21 + include/game/GameData/GameDataHolderBase.h | 12 + include/game/GameData/GameDataHolderWriter.h | 8 + include/game/GameData/GameProgressData.h | 8 + .../game/GameData/SaveDataAccessFunction.h | 10 + include/game/GameData/UniqueObjInfo.h | 14 + .../game/HakoniwaSequence/HakoniwaSequence.h | 101 + .../HakoniwaStateBootLoadData.h | 9 + .../HakoniwaStateDeleteScene.h | 8 + .../HakoniwaStateDemoEnding.h | 9 + .../HakoniwaStateDemoOpening.h | 9 + .../HakoniwaStateDemoWorldWarp.h | 9 + .../HakoniwaStateSimpleDemo.h | 9 + include/game/Info/QuestInfo.h | 39 + include/game/Info/QuestInfoHolder.h | 55 + include/game/Info/ShineInfo.h | 39 + .../InformationMovieUpdater.h | 11 + include/game/Input/InputSeparator.h | 37 + include/game/Interfaces/IUseDimension.h | 13 + include/game/Interfaces/IUsePlayerCollision.h | 6 + include/game/Interfaces/IUsePlayerHack.h | 10 + include/game/Layouts/CoinCounter.h | 29 + include/game/Layouts/CommonVerticalList.h | 95 + include/game/Layouts/MapLayout.h | 58 + include/game/Layouts/MapMini.h | 16 + include/game/Layouts/SimpleLayoutMenu.h | 20 + .../Actions/PlayerActionGroundMoveControl.h | 73 + .../game/Player/Attacks/PlayerSpinCapAttack.h | 47 + include/game/Player/CapFunction.h | 10 + include/game/Player/HackCap.h | 206 + include/game/Player/HackCap/CapTargetInfo.h | 6 + .../HackCap/HackCapJointControlKeeper.h | 17 + include/game/Player/HackCapThrowParam.h | 34 + include/game/Player/PlayerActorBase.h | 17 + include/game/Player/PlayerActorHakoniwa.h | 74 + include/game/Player/PlayerAnimControlRun.h | 29 + include/game/Player/PlayerAnimFrameCtrl.h | 9 + include/game/Player/PlayerAnimator.h | 50 + include/game/Player/PlayerCameraTarget.h | 12 + include/game/Player/PlayerCollider.h | 7 + include/game/Player/PlayerColliderHakoniwa.h | 18 + include/game/Player/PlayerConst.h | 1231 + include/game/Player/PlayerCostumeInfo.h | 78 + include/game/Player/PlayerFactory.h | 25 + .../PlayerFormSensorCollisionArranger.h | 39 + include/game/Player/PlayerFunction.h | 37 + include/game/Player/PlayerHackKeeper.h | 76 + include/game/Player/PlayerInfo.h | 50 + include/game/Player/PlayerInitInfo.h | 5 + include/game/Player/PlayerInput.h | 11 + .../Player/PlayerJointControlPartsDynamics.h | 5 + .../game/Player/PlayerModelChangerHakoniwa.h | 13 + include/game/Player/PlayerModelHolder.h | 25 + include/game/Player/PlayerModelKeeper.h | 6 + include/game/Player/PlayerPuppet.h | 18 + .../Player/States/PlayerStateRunHakoniwa.h | 59 + include/game/Player/YukimaruRacePlayer.h | 20 + include/game/StageScene/StageScene.h | 25 + include/game/StageScene/StageSceneLayout.h | 82 + .../game/StageScene/StageSceneStateOption.h | 128 + .../StageScene/StageSceneStatePauseMenu.h | 138 + .../StageSceneStateServerConfig.hpp | 82 + include/game/StaticInstances.h | 2 + include/game/System/Application.h | 13 + include/game/System/GameDrawInfo.h | 21 + include/game/System/GameFrameWorkNx.h | 26 + include/game/System/GameSystem.h | 12 + include/game/System/GameSystemInfo.h | 44 + include/game/UI/MenuSelectParts.h | 58 + include/game/WorldList/WorldList.h | 10 + include/game/WorldList/WorldResourceLoader.h | 7 + include/helpers.hpp | 168 + include/layouts/HideAndSeekIcon.h | 36 + include/layouts/NameTag.h | 45 + include/logger.hpp | 27 + include/main.hpp | 75 + include/nn.h | 12 + include/nn/account.h | 82 + include/nn/atk.h | 21 + include/nn/atk/SoundArchivePlayer.h | 40 + include/nn/atk/SoundDataManager.h | 28 + include/nn/atk/SoundPlayer.h | 60 + .../nn/atk/detail/AdvancedWaveSoundRuntime.h | 32 + include/nn/atk/detail/BasicSound.h | 143 + include/nn/atk/detail/SequenceSoundRuntime.h | 41 + include/nn/atk/detail/SoundArchiveManager.h | 46 + include/nn/atk/detail/StreamSoundRuntime.h | 26 + include/nn/atk/detail/WaveSoundRuntime.h | 33 + include/nn/audio.h | 49 + include/nn/crypto.h | 89 + include/nn/diag.h | 18 + include/nn/err.h | 22 + include/nn/friends.h | 44 + include/nn/fs.h | 91 + include/nn/g3d.h | 3 + include/nn/g3d/BindFuncTable.h | 18 + include/nn/g3d/ResFile.h | 69 + include/nn/g3d/ResFogAnim.h | 25 + include/nn/g3d/ResLightAnim.h | 15 + include/nn/g3d/ResMaterial.h | 32 + include/nn/g3d/ResMaterialAnim.h | 22 + include/nn/g3d/ResModel.h | 35 + include/nn/g3d/ResSceneAnim.h | 43 + include/nn/g3d/ResShapeAnim.h | 18 + include/nn/g3d/ResSkeletalAnim.h | 18 + include/nn/g3d/g3d_ResFile.h | 24 + include/nn/gfx/api.h | 33 + include/nn/gfx/buffer.h | 32 + include/nn/gfx/detail/bufferimpl.h | 51 + include/nn/gfx/detail/deviceimpl.h | 28 + include/nn/gfx/detail/pool.h | 41 + include/nn/gfx/device.h | 27 + include/nn/gfx/memory.h | 16 + include/nn/hid.h | 24 + include/nn/image.h | 64 + include/nn/init.h | 24 + include/nn/mem.h | 33 + include/nn/nex/RootObject.h | 33 + include/nn/nex/auth.h | 25 + include/nn/nex/berkeley.h | 52 + include/nn/nex/buffer.h | 28 + include/nn/nex/cache.h | 36 + include/nn/nex/checksum.h | 66 + include/nn/nex/client.h | 119 + include/nn/nex/data.h | 24 + include/nn/nex/ddl.h | 44 + include/nn/nex/dynamic.h | 24 + include/nn/nex/encryption.h | 65 + include/nn/nex/hash.h | 40 + include/nn/nex/instance.h | 50 + include/nn/nex/key.h | 42 + include/nn/nex/plugin.h | 36 + include/nn/nex/pseudo.h | 87 + include/nn/nex/reference.h | 21 + include/nn/nex/string.h | 36 + include/nn/nex/system.h | 70 + include/nn/nex/time.h | 54 + include/nn/nifm.h | 15 + include/nn/nn.h | 27 + include/nn/oe.h | 34 + include/nn/os.h | 265 + include/nn/result.h | 24 + include/nn/settings.h | 12 + include/nn/socket.h | 37 + include/nn/ssl.h | 38 + include/nn/swkbd/swkbd.h | 217 + include/nn/system_settings.ini | 53 + include/nn/time.h | 82 + include/nn/ui2d/Layout.h | 55 + include/nn/ui2d/Material.h | 32 + include/nn/ui2d/Pane.h | 83 + include/nn/ui2d/Parts.h | 32 + include/nn/ui2d/Texture.h | 8 + include/nn/ui2d/detail/TexCoordArray.h | 38 + include/nn/util.h | 10 + include/nn/util/Float2.h | 22 + include/nn/util/util_AccessorBase.h | 17 + include/nn/vfx/Config.h | 18 + include/nn/vfx/Heap.h | 20 + include/nn/vfx/System.h | 27 + include/nn/vi.h | 34 + include/packets/CaptureInf.h | 13 + include/packets/ChangeStagePacket.h | 14 + include/packets/CostumeInf.h | 15 + include/packets/GameInf.h | 22 + include/packets/HackCapInf.h | 11 + include/packets/InitPacket.h | 8 + include/packets/Packet.h | 81 + include/packets/PlayerConnect.h | 10 + include/packets/PlayerDC.h | 7 + include/packets/PlayerInfPacket.h | 34 + include/packets/ServerCommand.h | 8 + include/packets/ShineCollect.h | 9 + include/packets/TagInf.h | 17 + include/puppets/HackModelHolder.hpp | 38 + include/puppets/PuppetHolder.hpp | 39 + include/puppets/PuppetInfo.h | 50 + include/rs/util.hpp | 79 + include/rs/util/InputUtil.h | 23 + include/rs/util/ItemUtil.h | 37 + include/rs/util/LiveActorUtil.h | 9 + include/rs/util/SensorUtil.h | 1018 + include/sead/basis/seadNew.h | 70 + include/sead/basis/seadRawPrint.h | 95 + include/sead/basis/seadTypes.h | 27 + include/sead/camera/PerspectiveProjection.h | 11 + include/sead/codec/seadBase64.h | 14 + include/sead/codec/seadHashCRC16.h | 33 + include/sead/codec/seadHashCRC32.h | 33 + include/sead/container/seadBuffer.h | 442 + include/sead/container/seadFreeList.h | 74 + include/sead/container/seadListImpl.h | 121 + include/sead/container/seadObjArray.h | 271 + include/sead/container/seadObjList.h | 245 + include/sead/container/seadOffsetList.h | 219 + include/sead/container/seadOrderedSet.h | 189 + include/sead/container/seadPtrArray.h | 377 + include/sead/container/seadRingBuffer.h | 337 + include/sead/container/seadSafeArray.h | 158 + include/sead/container/seadStrTreeMap.h | 171 + include/sead/container/seadTList.h | 206 + include/sead/container/seadTreeMap.h | 584 + include/sead/container/seadTreeNode.h | 64 + include/sead/controller/seadController.h | 50 + include/sead/controller/seadControllerBase.h | 68 + include/sead/controller/seadControllerMgr.h | 45 + include/sead/devenv/seadAssertConfig.h | 22 + include/sead/devenv/seadDebugFontMgrNvn.h | 73 + include/sead/devenv/seadEnvUtil.h | 18 + include/sead/devenv/seadFontBase.h | 8 + include/sead/devenv/seadGameConfig.h | 24 + include/sead/devenv/seadStackTrace.h | 55 + .../cafe/seadCafeFSAFileDeviceCafe.h | 65 + .../filedevice/nin/seadNinAocFileDeviceNin.h | 14 + .../nin/seadNinContentFileDeviceNin.h | 14 + .../filedevice/nin/seadNinFileDeviceBaseNin.h | 51 + .../filedevice/nin/seadNinHostIOFileDevice.h | 17 + .../filedevice/nin/seadNinSDFileDeviceNin.h | 16 + .../filedevice/nin/seadNinSaveFileDeviceNin.h | 15 + .../sead/filedevice/seadArchiveFileDevice.h | 55 + include/sead/filedevice/seadFileDevice.h | 419 + include/sead/filedevice/seadFileDeviceMgr.h | 81 + include/sead/filedevice/seadMainFileDevice.h | 112 + include/sead/filedevice/seadPath.h | 22 + include/sead/framework/seadCalculateTask.h | 32 + include/sead/framework/seadFramework.h | 91 + include/sead/framework/seadGameFramework.h | 47 + include/sead/framework/seadHeapPolicies.h | 47 + include/sead/framework/seadInfLoopChecker.h | 40 + include/sead/framework/seadMethodTree.h | 88 + include/sead/framework/seadMethodTreeMgr.h | 30 + include/sead/framework/seadProcessMeter.h | 19 + include/sead/framework/seadProcessMeterBar.h | 82 + include/sead/framework/seadTaskBase.h | 103 + include/sead/framework/seadTaskID.h | 53 + include/sead/framework/seadTaskMgr.h | 125 + include/sead/framework/seadTaskParameter.h | 11 + .../sead/gfx/cafe/seadPrimitiveRendererCafe.h | 126 + include/sead/gfx/cafe/seadTextureCafeGX2.h | 28 + include/sead/gfx/seadCamera.h | 45 + include/sead/gfx/seadColor.h | 147 + include/sead/gfx/seadDrawContext.h | 28 + include/sead/gfx/seadFrameBuffer.h | 82 + include/sead/gfx/seadPrimitiveRenderer.h | 171 + include/sead/gfx/seadPrimitiveRendererUtil.h | 36 + include/sead/gfx/seadProjection.h | 54 + include/sead/gfx/seadTextWriter.h | 49 + include/sead/gfx/seadTexture.h | 23 + include/sead/gfx/seadViewport.h | 32 + include/sead/heap/seadArena.h | 23 + include/sead/heap/seadDisposer.h | 132 + include/sead/heap/seadExpHeap.h | 110 + include/sead/heap/seadFrameHeap.h | 60 + include/sead/heap/seadHeap.h | 129 + include/sead/heap/seadHeapMgr.h | 102 + include/sead/heap/seadMemBlock.h | 24 + include/sead/hostio/seadHostIOCurve.h | 167 + include/sead/hostio/seadHostIOEventListener.h | 112 + include/sead/hostio/seadHostIOMgr.h | 16 + include/sead/hostio/seadHostIONode.h | 51 + include/sead/hostio/seadHostIOReflexible.h | 90 + include/sead/hostio/seadHostIOThreadLock.h | 25 + include/sead/math/seadBoundBox.h | 109 + include/sead/math/seadBoundBox.hpp | 282 + include/sead/math/seadMathBase.h | 101 + include/sead/math/seadMathCalcCommon.h | 190 + include/sead/math/seadMathCalcCommon.hpp | 565 + include/sead/math/seadMathNumbers.h | 46 + include/sead/math/seadMathPolicies.h | 21 + include/sead/math/seadMatrix.h | 287 + include/sead/math/seadMatrix.hpp | 695 + include/sead/math/seadMatrixCalcCommon.h | 166 + include/sead/math/seadMatrixCalcCommon.hpp | 2685 + include/sead/math/seadQuat.h | 84 + include/sead/math/seadQuat.hpp | 94 + include/sead/math/seadQuatCalcCommon.h | 30 + include/sead/math/seadQuatCalcCommon.hpp | 152 + include/sead/math/seadVector.h | 259 + include/sead/math/seadVector.hpp | 289 + include/sead/math/seadVectorCalcCommon.h | 62 + include/sead/math/seadVectorCalcCommon.hpp | 250 + include/sead/mc/seadCoreInfo.h | 117 + include/sead/mc/seadJob.h | 25 + include/sead/mc/seadJobQueue.h | 154 + include/sead/mc/seadWorker.h | 53 + include/sead/mc/seadWorkerMgr.h | 57 + include/sead/prim/cafe/seadMemUtilCafe.hpp | 36 + include/sead/prim/nin/seadMemUtilNin.hpp | 41 + include/sead/prim/seadBitFlag.h | 126 + include/sead/prim/seadBitUtil.h | 48 + include/sead/prim/seadContainerIterator.h | 135 + include/sead/prim/seadDelegate.h | 632 + include/sead/prim/seadDelegateEventSlot.h | 96 + include/sead/prim/seadEndian.h | 114 + include/sead/prim/seadEnum.h | 418 + include/sead/prim/seadFormatPrint.h | 166 + include/sead/prim/seadLongBitFlag.h | 123 + include/sead/prim/seadMemUtil.h | 38 + include/sead/prim/seadNamable.h | 23 + include/sead/prim/seadPtrUtil.h | 60 + include/sead/prim/seadRuntimeTypeInfo.h | 128 + include/sead/prim/seadSafeString.h | 492 + include/sead/prim/seadSafeString.hpp | 939 + include/sead/prim/seadScopeGuard.h | 41 + include/sead/prim/seadScopedLock.h | 52 + include/sead/prim/seadSizedEnum.h | 26 + include/sead/prim/seadStorageFor.h | 85 + include/sead/prim/seadStringBuilder.h | 350 + include/sead/prim/seadStringUtil.h | 79 + include/sead/prim/seadTypedBitFlag.h | 119 + include/sead/prim/seadTypedLongBitFlag.h | 137 + include/sead/random/seadGlobalRandom.h | 13 + include/sead/random/seadRandom.h | 107 + include/sead/resource/seadArchiveRes.h | 88 + include/sead/resource/seadDecompressor.h | 38 + include/sead/resource/seadResource.h | 102 + include/sead/resource/seadResourceMgr.h | 101 + include/sead/resource/seadSZSDecompressor.h | 80 + include/sead/resource/seadSharcArchiveRes.h | 85 + include/sead/stream/seadBufferStream.h | 93 + include/sead/stream/seadStream.h | 98 + include/sead/stream/seadStreamFormat.h | 44 + include/sead/stream/seadStreamSrc.h | 17 + .../thread/nin/seadThreadLocalStorageNin.hpp | 30 + include/sead/thread/seadAtomic.h | 287 + include/sead/thread/seadCriticalSection.h | 46 + include/sead/thread/seadDelegateThread.h | 25 + include/sead/thread/seadEvent.h | 52 + include/sead/thread/seadMessageQueue.h | 47 + include/sead/thread/seadMutex.h | 43 + include/sead/thread/seadReadWriteLock.h | 40 + include/sead/thread/seadSemaphore.h | 59 + include/sead/thread/seadSpinLock.h | 29 + include/sead/thread/seadThread.h | 199 + include/sead/thread/seadThreadLocalStorage.h | 36 + include/sead/thread/seadThreadUtil.h | 14 + include/sead/time/seadCalendarSpan.h | 53 + include/sead/time/seadCalendarTime.h | 141 + include/sead/time/seadDateSpan.h | 82 + include/sead/time/seadDateTime.h | 98 + include/sead/time/seadDateUtil.h | 22 + include/sead/time/seadTickSpan.h | 120 + include/sead/time/seadTickTime.h | 44 + include/server/Client.hpp | 292 + include/server/DeltaTime.hpp | 14 + include/server/HideAndSeekConfigMenu.hpp | 19 + include/server/HideAndSeekMode.hpp | 46 + include/server/SocketClient.hpp | 35 + include/server/gamemode/GameModeBase.hpp | 71 + .../server/gamemode/GameModeConfigMenu.hpp | 17 + include/server/gamemode/GameModeFactory.hpp | 59 + include/server/gamemode/GameModeInfoBase.hpp | 21 + include/server/gamemode/GameModeTimer.hpp | 49 + include/syssocket/sockdefines.h | 276 + include/types.h | 78 + linkerscripts/application.ld | 172 + linkerscripts/syms100.ld | 1 + linkerscripts/syms310.ld | 179828 +++++++++++++++ patches/codehook.slpatch | 121 + patches/configs/100.config | 9 + patches/maps/100/main.map | 2 + patches/tests.slpatch | 51 + romfs/DebugData/Font/nvn_font_jis1.ntx | Bin 0 -> 983612 bytes romfs/DebugData/Font/nvn_font_jis1_tbl.bin | Bin 0 -> 7442 bytes romfs/DebugData/Font/nvn_font_shader.bin | Bin 0 -> 8448 bytes romfs/DebugData/Font/nvn_font_shader_jis1.bin | Bin 0 -> 9472 bytes romfs/LayoutData/HideAndSeekIcon.szs | Bin 0 -> 13284 bytes romfs/LayoutData/TitleLogoParts.szs | Bin 0 -> 310356 bytes romfs/LayoutData/TitleLogoSmallParts.szs | Bin 0 -> 54836 bytes scripts/calcJump.py | 49 + scripts/createActorTable.py | 48 + scripts/genLinkerScript.py | 83 + scripts/genPatch.py | 268 + scripts/sendPatch.py | 108 + scripts/tcpServer.py | 33 + source/Factory.cpp | 10 + source/Keyboard.cpp | 63 + source/cameras/CameraPoserCustom.cpp | 135 + source/cameras/ProjectCameraPoserFactory.cpp | 5 + source/crt0.s | 18 + source/debugMenu.cpp | 46 + source/helpers.cpp | 197 + source/hooks.cpp | 155 + source/layouts/HideAndSeekIcon.cpp | 129 + source/layouts/NameTag.cpp | 156 + source/main.cpp | 399 + source/module.cpp | 14 + source/puppets/PuppetActor.cpp | 381 + source/puppets/PuppetCapActor.cpp | 69 + source/puppets/PuppetHackActor.cpp | 91 + source/puppets/PuppetHolder.cpp | 65 + source/puppets/PuppetMain.cpp | 73 + source/sead/seadNew.cpp | 172 + source/sead/time/seadCalendarTime.cpp | 187 + source/sead/time/seadDateSpan.cpp | 41 + source/sead/time/seadDateTime.cpp | 218 + source/sead/time/seadDateTimeUtc.cpp | 124 + source/sead/time/seadDateUtil.cpp | 196 + source/sead/time/seadTickSpan.cpp | 51 + source/server/Client.cpp | 1438 + source/server/DeltaTime.cpp | 11 + source/server/GameModeTimer.cpp | 119 + source/server/HackModelHolder.cpp | 79 + source/server/HideAndSeekConfigMenu.cpp | 52 + source/server/HideAndSeekMode.cpp | 208 + source/server/SocketBase.cpp | 82 + source/server/SocketClient.cpp | 177 + source/server/captureSync.cpp | 101 + source/server/logger.cpp | 111 + source/states/StageSceneStatePauseMenu.cpp | 24 + source/states/StageSceneStateServerConfig.cpp | 279 + switch.specs | 7 + 604 files changed, 227753 insertions(+) create mode 100644 .clang-format create mode 100644 .clangd create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 MakefileNSO create mode 100644 README.md create mode 100644 exported.txt create mode 100644 include/Keyboard.hpp create mode 100644 include/SocketBase.hpp create mode 100644 include/actors/PuppetActor.h create mode 100644 include/actors/PuppetCapActor.h create mode 100644 include/actors/PuppetHackActor.h create mode 100644 include/agl/DisplayList.h create mode 100644 include/agl/DrawContext.h create mode 100644 include/agl/RenderBuffer.h create mode 100644 include/agl/RenderTargetColor.h create mode 100644 include/agl/RenderTargetDepth.h create mode 100644 include/agl/TextureData.h create mode 100644 include/agl/detail/FileIOMgr.h create mode 100644 include/agl/detail/MemoryPool.h create mode 100644 include/agl/detail/MemoryPoolHeap.h create mode 100644 include/agl/detail/ShaderHolder.h create mode 100644 include/agl/detail/Surface.h create mode 100644 include/agl/driver/NVNimage.h create mode 100644 include/agl/driver/NVNsampler.h create mode 100644 include/agl/driver/NVNtexture.h create mode 100644 include/agl/g3d.h create mode 100644 include/agl/g3d/g3d_ResFile.h create mode 100644 include/agl/gpu.h create mode 100644 include/agl/shader/Shader.h create mode 100644 include/agl/shader/ShaderCompileInfo.h create mode 100644 include/agl/shader/ShaderProgram.h create mode 100644 include/agl/shader/ShaderProgramArchive.h create mode 100644 include/agl/shtxt/Clause.h create mode 100644 include/agl/shtxt/DefineLinker.h create mode 100644 include/agl/shtxt/ExpressionEvaluator.h create mode 100644 include/agl/shtxt/Lexer.h create mode 100644 include/agl/shtxt/Preprocessor.h create mode 100644 include/agl/shtxt/SyntaxTree.h create mode 100644 include/agl/util.h create mode 100644 include/agl/utl.h create mode 100644 include/al/HtmlViewer.h create mode 100644 include/al/LiveActor/LiveActor.h create mode 100644 include/al/LiveActor/LiveActorGroup.h create mode 100644 include/al/PlayerHolder/PlayerHolder.h create mode 100644 include/al/Scene.hpp create mode 100644 include/al/action/ActionEffectCtrl.h create mode 100644 include/al/actor/ActorActionKeeper.h create mode 100644 include/al/actor/ActorCameraTarget.h create mode 100644 include/al/actor/ActorDimensionKeeper.h create mode 100644 include/al/actor/ActorExecuteInfo.h create mode 100644 include/al/actor/ActorInitInfo.h create mode 100644 include/al/actor/ActorSceneInfo.h create mode 100644 include/al/actor/DemoActor.h create mode 100644 include/al/actor/IUseName.h create mode 100644 include/al/actor/LiveActorKit.h create mode 100644 include/al/actor/Placement.h create mode 100644 include/al/actor/SubActorKeeper.h create mode 100644 include/al/actor/alPlacementFunction.h create mode 100644 include/al/actor/misc.h create mode 100644 include/al/area/AreaObjDirector.h create mode 100644 include/al/area/AreaObjGroup.h create mode 100644 include/al/area/ChangeStageInfo.h create mode 100644 include/al/async/AsyncFunctorThread.h create mode 100644 include/al/async/FunctorBase.h create mode 100644 include/al/async/FunctorV0M.hpp create mode 100644 include/al/audio/AudioDirector.h create mode 100644 include/al/audio/AudioKeeper.h create mode 100644 include/al/byaml/ByamlContainerHeader.h create mode 100644 include/al/byaml/ByamlData.h create mode 100644 include/al/byaml/ByamlHashPair.h create mode 100644 include/al/byaml/ByamlHeader.h create mode 100644 include/al/byaml/ByamlIter.h create mode 100644 include/al/byaml/ByamlStringTableIter.h create mode 100644 include/al/byaml/writer/ByamlWriter.h create mode 100644 include/al/byaml/writer/ByamlWriterBigDataList.h create mode 100644 include/al/byaml/writer/ByamlWriterData.h create mode 100644 include/al/byaml/writer/ByamlWriterStringTable.h create mode 100644 include/al/camera/CameraAngleCtrlInfo.h create mode 100644 include/al/camera/CameraAngleSwingInfo.h create mode 100644 include/al/camera/CameraArrowCollider.h create mode 100644 include/al/camera/CameraDirector.h create mode 100644 include/al/camera/CameraObjectRequestInfo.h create mode 100644 include/al/camera/CameraPoseUpdater.h create mode 100644 include/al/camera/CameraPoser.h create mode 100644 include/al/camera/CameraPoserFlag.h create mode 100644 include/al/camera/CameraPoserFollowLimit.h create mode 100644 include/al/camera/CameraPoserSceneInfo.h create mode 100644 include/al/camera/CameraStartInfo.h create mode 100644 include/al/camera/CameraTargetBase.h create mode 100644 include/al/camera/CameraTargetHolder.h create mode 100644 include/al/camera/CameraTicket.h create mode 100644 include/al/camera/CameraTurnInfo.h create mode 100644 include/al/camera/CameraVerticalAbsorber.h create mode 100644 include/al/camera/GyroCameraCtrl.h create mode 100644 include/al/camera/Projection.h create mode 100644 include/al/camera/SnapshotCameraCtrl.h create mode 100644 include/al/camera/alCameraPoserFunction.h create mode 100644 include/al/collision/Collider.h create mode 100644 include/al/collision/CollisionDirector.h create mode 100644 include/al/debug/GpuPerf.h create mode 100644 include/al/effect/Effect.h create mode 100644 include/al/effect/EffectInfo.h create mode 100644 include/al/effect/EffectKeeper.h create mode 100644 include/al/effect/EffectSystemInfo.h create mode 100644 include/al/effect/EffectUserInfo.h create mode 100644 include/al/event/IEventFlowEventReceiver.h create mode 100644 include/al/execute/ExecuteDirector.h create mode 100644 include/al/execute/IUseExecutor.h create mode 100644 include/al/factory/ActorFactory.h create mode 100644 include/al/factory/ActorFactoryEntries100.h create mode 100644 include/al/factory/CameraPoserFactory.h create mode 100644 include/al/factory/CameraPoserFactoryEntries100.h create mode 100644 include/al/factory/Factory.h create mode 100644 include/al/factory/ProjectActorFactory.h create mode 100644 include/al/factory/ProjectCameraPoserFactory.h create mode 100644 include/al/gamepad/util.h create mode 100644 include/al/hio/HioNode.h create mode 100644 include/al/layout/BalloonMessage.h create mode 100644 include/al/layout/CoinCounter.h create mode 100644 include/al/layout/HtmlViewer.h create mode 100644 include/al/layout/IUseLayout.h create mode 100644 include/al/layout/KeyRepeatCtrl.h create mode 100644 include/al/layout/LayoutActor.h create mode 100644 include/al/layout/LayoutInitInfo.h create mode 100644 include/al/layout/LayoutKit.h create mode 100644 include/al/layout/LayoutSceneInfo.h create mode 100644 include/al/layout/MenuSelectParts.h create mode 100644 include/al/layout/SimpleLayoutAppearWaitEnd.h create mode 100644 include/al/layout/TalkMessageVoicePlayer.h create mode 100644 include/al/layout/WindowConfirm.h create mode 100644 include/al/layout/WindowConfirmWait.h create mode 100644 include/al/message/IUseMessageSystem.h create mode 100644 include/al/message/MessageSystem.h create mode 100644 include/al/nerve/ActorStateBase.h create mode 100644 include/al/nerve/HostStateBase.h create mode 100644 include/al/nerve/Nerve.h create mode 100644 include/al/nerve/NerveAction.h create mode 100644 include/al/nerve/NerveExecutor.h create mode 100644 include/al/nerve/NerveKeeper.h create mode 100644 include/al/nerve/NerveStateBase.h create mode 100644 include/al/nerve/NerveStateCtrl.h create mode 100644 include/al/pose/ActorPoseKeeper.h create mode 100644 include/al/rail/RailKeeper.h create mode 100644 include/al/rail/RailRider.h create mode 100644 include/al/rumble/PadRumbleDirector.h create mode 100644 include/al/rumble/PadRumbleParam.h create mode 100644 include/al/rumble/alPadRumbleFunction.h create mode 100644 include/al/scene/ISceneObj.h create mode 100644 include/al/scene/Scene.h create mode 100644 include/al/scene/SceneCreator.h create mode 100644 include/al/scene/SceneInitInfo.h create mode 100644 include/al/scene/SceneObjHolder.h create mode 100644 include/al/screen/ScreenPointKeeper.h create mode 100644 include/al/sensor/HitSensor.h create mode 100644 include/al/sensor/HitSensorKeeper.h create mode 100644 include/al/sensor/SensorHitGroup.h create mode 100644 include/al/sensor/SensorMsg.h create mode 100644 include/al/sequence/Sequence.h create mode 100644 include/al/sequence/SequenceInitInfo.h create mode 100644 include/al/stage/StageInfo.h create mode 100644 include/al/string/StringTmp.h create mode 100644 include/al/switch/StageSwitchKeeper.h create mode 100644 include/al/util.hpp create mode 100644 include/al/util/AudioUtil.h create mode 100644 include/al/util/CameraUtil.h create mode 100644 include/al/util/ControllerUtil.h create mode 100644 include/al/util/GraphicsUtil.h create mode 100644 include/al/util/LayoutUtil.h create mode 100644 include/al/util/LiveActorUtil.h create mode 100644 include/al/util/MathUtil.h create mode 100644 include/al/util/NerveUtil.h create mode 100644 include/al/util/VectorUtil.h create mode 100644 include/algorithms/CaptureAnims.h create mode 100644 include/algorithms/CaptureTypes.h create mode 100644 include/algorithms/PlayerAnims.h create mode 100644 include/algorithms/WipeTypes.h create mode 100644 include/algorithms/crc32.h create mode 100644 include/cameras/CameraPoserCustom.h create mode 100644 include/debugMenu.hpp create mode 100644 include/game/Actors/ChurchDoor.h create mode 100644 include/game/Actors/CoinStackGroup.h create mode 100644 include/game/Actors/IUseDemoSkip.h create mode 100644 include/game/Actors/KuriboHack.h create mode 100644 include/game/Actors/ReflectBombGenerator.h create mode 100644 include/game/Actors/Shine.h create mode 100644 include/game/Actors/ShineTowerRocket.h create mode 100644 include/game/Actors/WorldEndBorderKeeper.h create mode 100644 include/game/Controller/ControllerAppletFunction.h create mode 100644 include/game/GameData/GameDataFile.h create mode 100644 include/game/GameData/GameDataFunction.h create mode 100644 include/game/GameData/GameDataHolder.h create mode 100644 include/game/GameData/GameDataHolderAccessor.h create mode 100644 include/game/GameData/GameDataHolderBase.h create mode 100644 include/game/GameData/GameDataHolderWriter.h create mode 100644 include/game/GameData/GameProgressData.h create mode 100644 include/game/GameData/SaveDataAccessFunction.h create mode 100644 include/game/GameData/UniqueObjInfo.h create mode 100644 include/game/HakoniwaSequence/HakoniwaSequence.h create mode 100644 include/game/HakoniwaSequence/HakoniwaStateBootLoadData.h create mode 100644 include/game/HakoniwaSequence/HakoniwaStateDeleteScene.h create mode 100644 include/game/HakoniwaSequence/HakoniwaStateDemoEnding.h create mode 100644 include/game/HakoniwaSequence/HakoniwaStateDemoOpening.h create mode 100644 include/game/HakoniwaSequence/HakoniwaStateDemoWorldWarp.h create mode 100644 include/game/HakoniwaSequence/HakoniwaStateSimpleDemo.h create mode 100644 include/game/Info/QuestInfo.h create mode 100644 include/game/Info/QuestInfoHolder.h create mode 100644 include/game/Info/ShineInfo.h create mode 100644 include/game/InformationMovie/InformationMovieUpdater.h create mode 100644 include/game/Input/InputSeparator.h create mode 100644 include/game/Interfaces/IUseDimension.h create mode 100644 include/game/Interfaces/IUsePlayerCollision.h create mode 100644 include/game/Interfaces/IUsePlayerHack.h create mode 100644 include/game/Layouts/CoinCounter.h create mode 100644 include/game/Layouts/CommonVerticalList.h create mode 100644 include/game/Layouts/MapLayout.h create mode 100644 include/game/Layouts/MapMini.h create mode 100644 include/game/Layouts/SimpleLayoutMenu.h create mode 100644 include/game/Player/Actions/PlayerActionGroundMoveControl.h create mode 100644 include/game/Player/Attacks/PlayerSpinCapAttack.h create mode 100644 include/game/Player/CapFunction.h create mode 100644 include/game/Player/HackCap.h create mode 100644 include/game/Player/HackCap/CapTargetInfo.h create mode 100644 include/game/Player/HackCap/HackCapJointControlKeeper.h create mode 100644 include/game/Player/HackCapThrowParam.h create mode 100644 include/game/Player/PlayerActorBase.h create mode 100644 include/game/Player/PlayerActorHakoniwa.h create mode 100644 include/game/Player/PlayerAnimControlRun.h create mode 100644 include/game/Player/PlayerAnimFrameCtrl.h create mode 100644 include/game/Player/PlayerAnimator.h create mode 100644 include/game/Player/PlayerCameraTarget.h create mode 100644 include/game/Player/PlayerCollider.h create mode 100644 include/game/Player/PlayerColliderHakoniwa.h create mode 100644 include/game/Player/PlayerConst.h create mode 100644 include/game/Player/PlayerCostumeInfo.h create mode 100644 include/game/Player/PlayerFactory.h create mode 100644 include/game/Player/PlayerFormSensorCollisionArranger.h create mode 100644 include/game/Player/PlayerFunction.h create mode 100644 include/game/Player/PlayerHackKeeper.h create mode 100644 include/game/Player/PlayerInfo.h create mode 100644 include/game/Player/PlayerInitInfo.h create mode 100644 include/game/Player/PlayerInput.h create mode 100644 include/game/Player/PlayerJointControlPartsDynamics.h create mode 100644 include/game/Player/PlayerModelChangerHakoniwa.h create mode 100644 include/game/Player/PlayerModelHolder.h create mode 100644 include/game/Player/PlayerModelKeeper.h create mode 100644 include/game/Player/PlayerPuppet.h create mode 100644 include/game/Player/States/PlayerStateRunHakoniwa.h create mode 100644 include/game/Player/YukimaruRacePlayer.h create mode 100644 include/game/StageScene/StageScene.h create mode 100644 include/game/StageScene/StageSceneLayout.h create mode 100644 include/game/StageScene/StageSceneStateOption.h create mode 100644 include/game/StageScene/StageSceneStatePauseMenu.h create mode 100644 include/game/StageScene/StageSceneStateServerConfig.hpp create mode 100644 include/game/StaticInstances.h create mode 100644 include/game/System/Application.h create mode 100644 include/game/System/GameDrawInfo.h create mode 100644 include/game/System/GameFrameWorkNx.h create mode 100644 include/game/System/GameSystem.h create mode 100644 include/game/System/GameSystemInfo.h create mode 100644 include/game/UI/MenuSelectParts.h create mode 100644 include/game/WorldList/WorldList.h create mode 100644 include/game/WorldList/WorldResourceLoader.h create mode 100644 include/helpers.hpp create mode 100644 include/layouts/HideAndSeekIcon.h create mode 100644 include/layouts/NameTag.h create mode 100644 include/logger.hpp create mode 100644 include/main.hpp create mode 100644 include/nn.h create mode 100644 include/nn/account.h create mode 100644 include/nn/atk.h create mode 100644 include/nn/atk/SoundArchivePlayer.h create mode 100644 include/nn/atk/SoundDataManager.h create mode 100644 include/nn/atk/SoundPlayer.h create mode 100644 include/nn/atk/detail/AdvancedWaveSoundRuntime.h create mode 100644 include/nn/atk/detail/BasicSound.h create mode 100644 include/nn/atk/detail/SequenceSoundRuntime.h create mode 100644 include/nn/atk/detail/SoundArchiveManager.h create mode 100644 include/nn/atk/detail/StreamSoundRuntime.h create mode 100644 include/nn/atk/detail/WaveSoundRuntime.h create mode 100644 include/nn/audio.h create mode 100644 include/nn/crypto.h create mode 100644 include/nn/diag.h create mode 100644 include/nn/err.h create mode 100644 include/nn/friends.h create mode 100644 include/nn/fs.h create mode 100644 include/nn/g3d.h create mode 100644 include/nn/g3d/BindFuncTable.h create mode 100644 include/nn/g3d/ResFile.h create mode 100644 include/nn/g3d/ResFogAnim.h create mode 100644 include/nn/g3d/ResLightAnim.h create mode 100644 include/nn/g3d/ResMaterial.h create mode 100644 include/nn/g3d/ResMaterialAnim.h create mode 100644 include/nn/g3d/ResModel.h create mode 100644 include/nn/g3d/ResSceneAnim.h create mode 100644 include/nn/g3d/ResShapeAnim.h create mode 100644 include/nn/g3d/ResSkeletalAnim.h create mode 100644 include/nn/g3d/g3d_ResFile.h create mode 100644 include/nn/gfx/api.h create mode 100644 include/nn/gfx/buffer.h create mode 100644 include/nn/gfx/detail/bufferimpl.h create mode 100644 include/nn/gfx/detail/deviceimpl.h create mode 100644 include/nn/gfx/detail/pool.h create mode 100644 include/nn/gfx/device.h create mode 100644 include/nn/gfx/memory.h create mode 100644 include/nn/hid.h create mode 100644 include/nn/image.h create mode 100644 include/nn/init.h create mode 100644 include/nn/mem.h create mode 100644 include/nn/nex/RootObject.h create mode 100644 include/nn/nex/auth.h create mode 100644 include/nn/nex/berkeley.h create mode 100644 include/nn/nex/buffer.h create mode 100644 include/nn/nex/cache.h create mode 100644 include/nn/nex/checksum.h create mode 100644 include/nn/nex/client.h create mode 100644 include/nn/nex/data.h create mode 100644 include/nn/nex/ddl.h create mode 100644 include/nn/nex/dynamic.h create mode 100644 include/nn/nex/encryption.h create mode 100644 include/nn/nex/hash.h create mode 100644 include/nn/nex/instance.h create mode 100644 include/nn/nex/key.h create mode 100644 include/nn/nex/plugin.h create mode 100644 include/nn/nex/pseudo.h create mode 100644 include/nn/nex/reference.h create mode 100644 include/nn/nex/string.h create mode 100644 include/nn/nex/system.h create mode 100644 include/nn/nex/time.h create mode 100644 include/nn/nifm.h create mode 100644 include/nn/nn.h create mode 100644 include/nn/oe.h create mode 100644 include/nn/os.h create mode 100644 include/nn/result.h create mode 100644 include/nn/settings.h create mode 100644 include/nn/socket.h create mode 100644 include/nn/ssl.h create mode 100644 include/nn/swkbd/swkbd.h create mode 100644 include/nn/system_settings.ini create mode 100644 include/nn/time.h create mode 100644 include/nn/ui2d/Layout.h create mode 100644 include/nn/ui2d/Material.h create mode 100644 include/nn/ui2d/Pane.h create mode 100644 include/nn/ui2d/Parts.h create mode 100644 include/nn/ui2d/Texture.h create mode 100644 include/nn/ui2d/detail/TexCoordArray.h create mode 100644 include/nn/util.h create mode 100644 include/nn/util/Float2.h create mode 100644 include/nn/util/util_AccessorBase.h create mode 100644 include/nn/vfx/Config.h create mode 100644 include/nn/vfx/Heap.h create mode 100644 include/nn/vfx/System.h create mode 100644 include/nn/vi.h create mode 100644 include/packets/CaptureInf.h create mode 100644 include/packets/ChangeStagePacket.h create mode 100644 include/packets/CostumeInf.h create mode 100644 include/packets/GameInf.h create mode 100644 include/packets/HackCapInf.h create mode 100644 include/packets/InitPacket.h create mode 100644 include/packets/Packet.h create mode 100644 include/packets/PlayerConnect.h create mode 100644 include/packets/PlayerDC.h create mode 100644 include/packets/PlayerInfPacket.h create mode 100644 include/packets/ServerCommand.h create mode 100644 include/packets/ShineCollect.h create mode 100644 include/packets/TagInf.h create mode 100644 include/puppets/HackModelHolder.hpp create mode 100644 include/puppets/PuppetHolder.hpp create mode 100644 include/puppets/PuppetInfo.h create mode 100644 include/rs/util.hpp create mode 100644 include/rs/util/InputUtil.h create mode 100644 include/rs/util/ItemUtil.h create mode 100644 include/rs/util/LiveActorUtil.h create mode 100644 include/rs/util/SensorUtil.h create mode 100644 include/sead/basis/seadNew.h create mode 100644 include/sead/basis/seadRawPrint.h create mode 100644 include/sead/basis/seadTypes.h create mode 100644 include/sead/camera/PerspectiveProjection.h create mode 100644 include/sead/codec/seadBase64.h create mode 100644 include/sead/codec/seadHashCRC16.h create mode 100644 include/sead/codec/seadHashCRC32.h create mode 100644 include/sead/container/seadBuffer.h create mode 100644 include/sead/container/seadFreeList.h create mode 100644 include/sead/container/seadListImpl.h create mode 100644 include/sead/container/seadObjArray.h create mode 100644 include/sead/container/seadObjList.h create mode 100644 include/sead/container/seadOffsetList.h create mode 100644 include/sead/container/seadOrderedSet.h create mode 100644 include/sead/container/seadPtrArray.h create mode 100644 include/sead/container/seadRingBuffer.h create mode 100644 include/sead/container/seadSafeArray.h create mode 100644 include/sead/container/seadStrTreeMap.h create mode 100644 include/sead/container/seadTList.h create mode 100644 include/sead/container/seadTreeMap.h create mode 100644 include/sead/container/seadTreeNode.h create mode 100644 include/sead/controller/seadController.h create mode 100644 include/sead/controller/seadControllerBase.h create mode 100644 include/sead/controller/seadControllerMgr.h create mode 100644 include/sead/devenv/seadAssertConfig.h create mode 100644 include/sead/devenv/seadDebugFontMgrNvn.h create mode 100644 include/sead/devenv/seadEnvUtil.h create mode 100644 include/sead/devenv/seadFontBase.h create mode 100644 include/sead/devenv/seadGameConfig.h create mode 100644 include/sead/devenv/seadStackTrace.h create mode 100644 include/sead/filedevice/cafe/seadCafeFSAFileDeviceCafe.h create mode 100644 include/sead/filedevice/nin/seadNinAocFileDeviceNin.h create mode 100644 include/sead/filedevice/nin/seadNinContentFileDeviceNin.h create mode 100644 include/sead/filedevice/nin/seadNinFileDeviceBaseNin.h create mode 100644 include/sead/filedevice/nin/seadNinHostIOFileDevice.h create mode 100644 include/sead/filedevice/nin/seadNinSDFileDeviceNin.h create mode 100644 include/sead/filedevice/nin/seadNinSaveFileDeviceNin.h create mode 100644 include/sead/filedevice/seadArchiveFileDevice.h create mode 100644 include/sead/filedevice/seadFileDevice.h create mode 100644 include/sead/filedevice/seadFileDeviceMgr.h create mode 100644 include/sead/filedevice/seadMainFileDevice.h create mode 100644 include/sead/filedevice/seadPath.h create mode 100644 include/sead/framework/seadCalculateTask.h create mode 100644 include/sead/framework/seadFramework.h create mode 100644 include/sead/framework/seadGameFramework.h create mode 100644 include/sead/framework/seadHeapPolicies.h create mode 100644 include/sead/framework/seadInfLoopChecker.h create mode 100644 include/sead/framework/seadMethodTree.h create mode 100644 include/sead/framework/seadMethodTreeMgr.h create mode 100644 include/sead/framework/seadProcessMeter.h create mode 100644 include/sead/framework/seadProcessMeterBar.h create mode 100644 include/sead/framework/seadTaskBase.h create mode 100644 include/sead/framework/seadTaskID.h create mode 100644 include/sead/framework/seadTaskMgr.h create mode 100644 include/sead/framework/seadTaskParameter.h create mode 100644 include/sead/gfx/cafe/seadPrimitiveRendererCafe.h create mode 100644 include/sead/gfx/cafe/seadTextureCafeGX2.h create mode 100644 include/sead/gfx/seadCamera.h create mode 100644 include/sead/gfx/seadColor.h create mode 100644 include/sead/gfx/seadDrawContext.h create mode 100644 include/sead/gfx/seadFrameBuffer.h create mode 100644 include/sead/gfx/seadPrimitiveRenderer.h create mode 100644 include/sead/gfx/seadPrimitiveRendererUtil.h create mode 100644 include/sead/gfx/seadProjection.h create mode 100644 include/sead/gfx/seadTextWriter.h create mode 100644 include/sead/gfx/seadTexture.h create mode 100644 include/sead/gfx/seadViewport.h create mode 100644 include/sead/heap/seadArena.h create mode 100644 include/sead/heap/seadDisposer.h create mode 100644 include/sead/heap/seadExpHeap.h create mode 100644 include/sead/heap/seadFrameHeap.h create mode 100644 include/sead/heap/seadHeap.h create mode 100644 include/sead/heap/seadHeapMgr.h create mode 100644 include/sead/heap/seadMemBlock.h create mode 100644 include/sead/hostio/seadHostIOCurve.h create mode 100644 include/sead/hostio/seadHostIOEventListener.h create mode 100644 include/sead/hostio/seadHostIOMgr.h create mode 100644 include/sead/hostio/seadHostIONode.h create mode 100644 include/sead/hostio/seadHostIOReflexible.h create mode 100644 include/sead/hostio/seadHostIOThreadLock.h create mode 100644 include/sead/math/seadBoundBox.h create mode 100644 include/sead/math/seadBoundBox.hpp create mode 100644 include/sead/math/seadMathBase.h create mode 100644 include/sead/math/seadMathCalcCommon.h create mode 100644 include/sead/math/seadMathCalcCommon.hpp create mode 100644 include/sead/math/seadMathNumbers.h create mode 100644 include/sead/math/seadMathPolicies.h create mode 100644 include/sead/math/seadMatrix.h create mode 100644 include/sead/math/seadMatrix.hpp create mode 100644 include/sead/math/seadMatrixCalcCommon.h create mode 100644 include/sead/math/seadMatrixCalcCommon.hpp create mode 100644 include/sead/math/seadQuat.h create mode 100644 include/sead/math/seadQuat.hpp create mode 100644 include/sead/math/seadQuatCalcCommon.h create mode 100644 include/sead/math/seadQuatCalcCommon.hpp create mode 100644 include/sead/math/seadVector.h create mode 100644 include/sead/math/seadVector.hpp create mode 100644 include/sead/math/seadVectorCalcCommon.h create mode 100644 include/sead/math/seadVectorCalcCommon.hpp create mode 100644 include/sead/mc/seadCoreInfo.h create mode 100644 include/sead/mc/seadJob.h create mode 100644 include/sead/mc/seadJobQueue.h create mode 100644 include/sead/mc/seadWorker.h create mode 100644 include/sead/mc/seadWorkerMgr.h create mode 100644 include/sead/prim/cafe/seadMemUtilCafe.hpp create mode 100644 include/sead/prim/nin/seadMemUtilNin.hpp create mode 100644 include/sead/prim/seadBitFlag.h create mode 100644 include/sead/prim/seadBitUtil.h create mode 100644 include/sead/prim/seadContainerIterator.h create mode 100644 include/sead/prim/seadDelegate.h create mode 100644 include/sead/prim/seadDelegateEventSlot.h create mode 100644 include/sead/prim/seadEndian.h create mode 100644 include/sead/prim/seadEnum.h create mode 100644 include/sead/prim/seadFormatPrint.h create mode 100644 include/sead/prim/seadLongBitFlag.h create mode 100644 include/sead/prim/seadMemUtil.h create mode 100644 include/sead/prim/seadNamable.h create mode 100644 include/sead/prim/seadPtrUtil.h create mode 100644 include/sead/prim/seadRuntimeTypeInfo.h create mode 100644 include/sead/prim/seadSafeString.h create mode 100644 include/sead/prim/seadSafeString.hpp create mode 100644 include/sead/prim/seadScopeGuard.h create mode 100644 include/sead/prim/seadScopedLock.h create mode 100644 include/sead/prim/seadSizedEnum.h create mode 100644 include/sead/prim/seadStorageFor.h create mode 100644 include/sead/prim/seadStringBuilder.h create mode 100644 include/sead/prim/seadStringUtil.h create mode 100644 include/sead/prim/seadTypedBitFlag.h create mode 100644 include/sead/prim/seadTypedLongBitFlag.h create mode 100644 include/sead/random/seadGlobalRandom.h create mode 100644 include/sead/random/seadRandom.h create mode 100644 include/sead/resource/seadArchiveRes.h create mode 100644 include/sead/resource/seadDecompressor.h create mode 100644 include/sead/resource/seadResource.h create mode 100644 include/sead/resource/seadResourceMgr.h create mode 100644 include/sead/resource/seadSZSDecompressor.h create mode 100644 include/sead/resource/seadSharcArchiveRes.h create mode 100644 include/sead/stream/seadBufferStream.h create mode 100644 include/sead/stream/seadStream.h create mode 100644 include/sead/stream/seadStreamFormat.h create mode 100644 include/sead/stream/seadStreamSrc.h create mode 100644 include/sead/thread/nin/seadThreadLocalStorageNin.hpp create mode 100644 include/sead/thread/seadAtomic.h create mode 100644 include/sead/thread/seadCriticalSection.h create mode 100644 include/sead/thread/seadDelegateThread.h create mode 100644 include/sead/thread/seadEvent.h create mode 100644 include/sead/thread/seadMessageQueue.h create mode 100644 include/sead/thread/seadMutex.h create mode 100644 include/sead/thread/seadReadWriteLock.h create mode 100644 include/sead/thread/seadSemaphore.h create mode 100644 include/sead/thread/seadSpinLock.h create mode 100644 include/sead/thread/seadThread.h create mode 100644 include/sead/thread/seadThreadLocalStorage.h create mode 100644 include/sead/thread/seadThreadUtil.h create mode 100644 include/sead/time/seadCalendarSpan.h create mode 100644 include/sead/time/seadCalendarTime.h create mode 100644 include/sead/time/seadDateSpan.h create mode 100644 include/sead/time/seadDateTime.h create mode 100644 include/sead/time/seadDateUtil.h create mode 100644 include/sead/time/seadTickSpan.h create mode 100644 include/sead/time/seadTickTime.h create mode 100644 include/server/Client.hpp create mode 100644 include/server/DeltaTime.hpp create mode 100644 include/server/HideAndSeekConfigMenu.hpp create mode 100644 include/server/HideAndSeekMode.hpp create mode 100644 include/server/SocketClient.hpp create mode 100644 include/server/gamemode/GameModeBase.hpp create mode 100644 include/server/gamemode/GameModeConfigMenu.hpp create mode 100644 include/server/gamemode/GameModeFactory.hpp create mode 100644 include/server/gamemode/GameModeInfoBase.hpp create mode 100644 include/server/gamemode/GameModeTimer.hpp create mode 100644 include/syssocket/sockdefines.h create mode 100644 include/types.h create mode 100644 linkerscripts/application.ld create mode 100644 linkerscripts/syms100.ld create mode 100644 linkerscripts/syms310.ld create mode 100644 patches/codehook.slpatch create mode 100644 patches/configs/100.config create mode 100644 patches/maps/100/main.map create mode 100644 patches/tests.slpatch create mode 100644 romfs/DebugData/Font/nvn_font_jis1.ntx create mode 100644 romfs/DebugData/Font/nvn_font_jis1_tbl.bin create mode 100644 romfs/DebugData/Font/nvn_font_shader.bin create mode 100644 romfs/DebugData/Font/nvn_font_shader_jis1.bin create mode 100644 romfs/LayoutData/HideAndSeekIcon.szs create mode 100644 romfs/LayoutData/TitleLogoParts.szs create mode 100644 romfs/LayoutData/TitleLogoSmallParts.szs create mode 100644 scripts/calcJump.py create mode 100644 scripts/createActorTable.py create mode 100644 scripts/genLinkerScript.py create mode 100644 scripts/genPatch.py create mode 100644 scripts/sendPatch.py create mode 100644 scripts/tcpServer.py create mode 100644 source/Factory.cpp create mode 100644 source/Keyboard.cpp create mode 100644 source/cameras/CameraPoserCustom.cpp create mode 100644 source/cameras/ProjectCameraPoserFactory.cpp create mode 100644 source/crt0.s create mode 100644 source/debugMenu.cpp create mode 100644 source/helpers.cpp create mode 100644 source/hooks.cpp create mode 100644 source/layouts/HideAndSeekIcon.cpp create mode 100644 source/layouts/NameTag.cpp create mode 100644 source/main.cpp create mode 100644 source/module.cpp create mode 100644 source/puppets/PuppetActor.cpp create mode 100644 source/puppets/PuppetCapActor.cpp create mode 100644 source/puppets/PuppetHackActor.cpp create mode 100644 source/puppets/PuppetHolder.cpp create mode 100644 source/puppets/PuppetMain.cpp create mode 100644 source/sead/seadNew.cpp create mode 100644 source/sead/time/seadCalendarTime.cpp create mode 100644 source/sead/time/seadDateSpan.cpp create mode 100644 source/sead/time/seadDateTime.cpp create mode 100644 source/sead/time/seadDateTimeUtc.cpp create mode 100644 source/sead/time/seadDateUtil.cpp create mode 100644 source/sead/time/seadTickSpan.cpp create mode 100644 source/server/Client.cpp create mode 100644 source/server/DeltaTime.cpp create mode 100644 source/server/GameModeTimer.cpp create mode 100644 source/server/HackModelHolder.cpp create mode 100644 source/server/HideAndSeekConfigMenu.cpp create mode 100644 source/server/HideAndSeekMode.cpp create mode 100644 source/server/SocketBase.cpp create mode 100644 source/server/SocketClient.cpp create mode 100644 source/server/captureSync.cpp create mode 100644 source/server/logger.cpp create mode 100644 source/states/StageSceneStatePauseMenu.cpp create mode 100644 source/states/StageSceneStateServerConfig.cpp create mode 100644 switch.specs diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ef86260 --- /dev/null +++ b/.clang-format @@ -0,0 +1,74 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 100 +CommentPragmas: '^ (IWYU pragma:|NOLINT)' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ForEachMacros: [] +IncludeCategories: + - Regex: '^<[Ww]indows\.h>$' + Priority: 1 + - Regex: '^<' + Priority: 2 + - Regex: '^"' + Priority: 3 +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: c++17 +TabWidth: 4 +UseTab: Never +... diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..7058e79 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5fd286 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +starlight_patch_100/** +build100/** +romfs stuff/** +.vscode/** +Crash Reports/** +.cache/** +Custom Stage Builds/** + +compile_commands.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9c8c846 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# TODO (Khangaroo): Make this process a lot less hacky (no, export did not work) +# See MakefileNSO + +.PHONY: all clean starlight send + +SMOVER ?= 100 +BUILDVER ?= 99.37 +IP ?= 10.0.0.221 +DEBUGLOG ?= 0 # defaults to disable debug logger +SERVERIP ?= 0.0.0.0 # put debug logger server IP here + +PROJNAME ?= StarlightBase + +all: starlight + +starlight: + $(MAKE) all -f MakefileNSO SMOVER=$(SMOVER) BUILDVER=$(BUILDVER) DEBUGLOG=$(DEBUGLOG) SERVERIP=${SERVERIP} + $(MAKE) starlight_patch_$(SMOVER)/*.ips + + mkdir -p starlight_patch_$(SMOVER)/atmosphere/exefs_patches/$(PROJNAME)/ + mkdir -p starlight_patch_$(SMOVER)/atmosphere/contents/0100000000010000/exefs/ + + mv starlight_patch_$(SMOVER)/3CA12DFAAF9C82DA064D1698DF79CDA1.ips starlight_patch_$(SMOVER)/atmosphere/exefs_patches/$(PROJNAME)/3CA12DFAAF9C82DA064D1698DF79CDA1.ips + mv $(shell basename $(CURDIR))$(SMOVER).elf starlight_patch_$(SMOVER)/subsdk1.elf + mv $(shell basename $(CURDIR))$(SMOVER).nso starlight_patch_$(SMOVER)/atmosphere/contents/0100000000010000/exefs/subsdk1 + +starlight_patch_$(SMOVER)/*.ips: patches/*.slpatch patches/configs/$(SMOVER).config patches/maps/$(SMOVER)/*.map \ + build$(SMOVER)/$(shell basename $(CURDIR))$(SMOVER).map scripts/genPatch.py + @rm -f starlight_patch_$(SMOVER)/*.ips + python3 scripts/genPatch.py $(SMOVER) + +# builds project with the file structure used in the yuzu emulator +yuzu: + $(MAKE) all -f MakefileNSO SMOVER=$(SMOVER) BUILDVER=$(BUILDVER) + $(MAKE) starlight_patch_$(SMOVER)/*.ips + + mkdir -p starlight_patch_$(SMOVER)/yuzu/ + + mv starlight_patch_$(SMOVER)/3CA12DFAAF9C82DA064D1698DF79CDA1.ips starlight_patch_$(SMOVER)/yuzu/3CA12DFAAF9C82DA064D1698DF79CDA1.ips + mv $(shell basename $(CURDIR))$(SMOVER).elf starlight_patch_$(SMOVER)/subsdk1.elf + mv $(shell basename $(CURDIR))$(SMOVER).nso starlight_patch_$(SMOVER)/yuzu/subsdk1 +# builds and sends project to FTP server hosted on provided IP +send: all + python3.8 scripts/sendPatch.py $(IP) $(PROJNAME) + +log: all + python3.8 scripts/tcpServer.py $(SERVERIP) + +sendlog: all + python3.8 scripts/sendPatch.py $(IP) $(PROJNAME) $(USER) $(PASS) + python3.8 scripts/tcpServer.py $(SERVERIP) + +clean: + $(MAKE) clean -f MakefileNSO + @rm -fr starlight_patch_* \ No newline at end of file diff --git a/MakefileNSO b/MakefileNSO new file mode 100644 index 0000000..e37bcfe --- /dev/null +++ b/MakefileNSO @@ -0,0 +1,176 @@ +#--------------------------------------------------------------------------------- +# Starlight-specific +# SMOVER is the target version of Super Mario Odyssey, but without the decimal points +# This can be changed by compiling for a different version (e.g. make 100) +# (used for C defines, filenames, etc) +# (used for user-facing stuff) +# LINKERSCRIPTS is the directory where the function addresses for Super Mario Odyssey are +# stored +# Each script is stored as syms$(SMOVER).ld +# (used for mapping Super Mario Odyssey functions to the proper address) +#--------------------------------------------------------------------------------- + +LINKERSCRIPTS := linkerscripts + +#--------------------------------------------------------------------------------- +#.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET ?= $(notdir $(CURDIR))$(SMOVER) +BUILD ?= build$(SMOVER) +SOURCES := source/sead/time source/sead source/puppets source/server source/layouts source/states source/cameras source +DATA := data +INCLUDES := include include/sead + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec + +CFLAGS := -g -Wall -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ -DSMOVER=$(SMOVER) -O3 -DNNSDK -DSWITCH -DBUILDVER=$(BUILDVER) -DDEBUGLOG=$(DEBUGLOG) -DSERVERIP=$(SERVERIP) + +CXXFLAGS := $(CFLAGS) -fno-rtti -fomit-frame-pointer -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables -std=gnu++20 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=../switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -Wl,--version-script=$(TOPDIR)/exported.txt -Wl,-init=__custom_init -Wl,-fini=__custom_fini -nostdlib + +LIBS := -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR ?= $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.jpg) + ifneq (,$(findstring $(TARGET).jpg,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).jpg + else + ifneq (,$(findstring icon.jpg,$(icons))) + export APP_ICON := $(TOPDIR)/icon.jpg + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_ICON)),) + export NROFLAGS += --icon=$(APP_ICON) +endif + +ifeq ($(strip $(NO_NACP)),) + export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- + +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @cp $(LINKERSCRIPTS)/syms$(SMOVER).ld $(LINKERSCRIPTS)/symstemp.ld # This is required because you can't pass a variable to the .specs + $(MAKE) -C $(BUILD) -f $(CURDIR)/MakefileNSO + @rm -f $(LINKERSCRIPTS)/symstemp.ld + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr build* *.nso *.elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +%.nso: %.elf + @elf2nso $< $@ + @echo built ... $(notdir $@) +#--------------------------------------------------------------------------------- +all : $(OUTPUT).nso + +$(OUTPUT).elf : $(OFILES) + + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..e332489 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Super Mario Odyssey - Online Multiplayer Mod + +Welcome to the official repository for the Super Mario Odyssey Online mod! Have fun exploring kingdoms with friends, playing gamemodes, or beating the game as fast as possible! This mod is still early in development, so expect bugs and un-refined aspects as we work hard to improve it and make the mod as polished as possible. + +## Features + +* Explore Kingdoms together with up to 10 People +* Almost every capture in the game is synced between players +* Full 2D and Costume models syncing +* Moon Collection is shared between all players +* Custom Configuration Menu (Accessible by holding ZL and selecting any option in the pause/start menu) +* Support for custom gamemodes (WIP) +### Available Gamemodes +* Hide and Seek + +## SMO Version Support + +* 1.0 + +## Installation Tutorial + +Before installing, Ensure that your switch is hacked. If not, follow [This Guide](https://switch.homebrew.guide/) to get your switch setup for modding. Make sure you set up a way to block Nintendo's servers as you will need to have your switch connected to the internet for this mod to work! + +1. Download the latest mod build from either [Gamebanana](https://gamebanana.com/games/6376) or from the [Releases](https://github.com/CraftyBoss/SuperMarioOdysseyOnline/releases) tab. (Alternatively, build from source) +2. Extract the downloaded zip onto the root of your Switch's SD card. +3. If you need to host an online server, head over to the [Super Mario Odyssey Online Server](https://github.com/Sanae6/SmoOnlineServer) repository and follow the instructions there to set up the server. +4. Launch the game! Upon first time bootup, the mod should ask for a server IP to save to the games common save file. This IP address will be the server you wish to connect to every time you launch the game with the mod installed. (Note: un-installing the mod and launching the game will remove the server IP from the common save file.) + +## Gamemode Info +### Hide and Seek +* Depending on Group size, select who will start as seekers at the beginning of each round and a kingdom to hide in. +* Each player has a timer on the top right of the screen that will increase while they are hiding during a round. +* When a seeker gets close enough to a player, the player will die and respawn as a seeker. +* During the round, hiders who die by other means will also become seekers upon respawning. +* If a hider loads into a new stage (via a pipe, door, etc.) the hider will get 5 seconds of tag invincibility to prevent spawn point camping. +* The player with the most time at the end of a round (or set of rounds) is considered the winner. +* While not a concrete rule, it's generally agreed upon that hiding should not be done out of bounds, inside objects that don't sync across games yet, and inside objects that completely conceal a player from others (such as trees). + +## Gamemode Controls +### Hide and Seek +- Left D-Pad: Decrease time +- Right D-Pad: Increase Time +- L + D-Pad Down: Reset Time +- D-Pad Up: Switch from Hider/Seeker + +## Building Prerequisites + +- [devkitPro](https://devkitpro.org/) +- Python 3 +- The [Keystone-Engine](https://www.keystone-engine.org/) Python Module + +## Building + +Build has only been tested on WSL2 running Ubuntu 20.04.1. + +Just run: +``` +DEVKITPRO={path_to_devkitpro} make +``` + +On Ubuntu (and other Debian-based systems), devkitPro will be installed to `/opt/devkitpro` by default: + +``` +DEVKITPRO=/opt/devkitpro/ make +``` + +## Installing (Atmosphère) + +After a successful build, simply transfer the `atmosphere` folder located inside `starlight_patch_100` to the root of your switch's SD card. + +--- + +# Contributors + +- [Sanae](https://github.com/sanae6) Wrote the majority of the server code +- [Shadow](https://github.com/shadowninja108) original author of starlight, the tool used to make this entire mod possible +- [GRAnimated](https://github.com/GRAnimated) + +# Credits +- [OdysseyDecomp](https://github.com/shibbo/OdysseyDecomp) +- [OdysseyReversed](https://github.com/shibbo/OdysseyReversed) +- [open-ead](https://github.com/open-ead/sead) sead Headers diff --git a/exported.txt b/exported.txt new file mode 100644 index 0000000..337b29e --- /dev/null +++ b/exported.txt @@ -0,0 +1,5 @@ +{ + global: + _Z14stageSceneHookv; + local: *; +}; diff --git a/include/Keyboard.hpp b/include/Keyboard.hpp new file mode 100644 index 0000000..003d339 --- /dev/null +++ b/include/Keyboard.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include "al/async/AsyncFunctorThread.h" +#include "al/async/FunctorV0M.hpp" + +#include "nn/swkbd/swkbd.h" + +#include "logger.hpp" +#include "sead/prim/seadSafeString.h" + +class Keyboard { + public: + Keyboard(ulong strSize); + void keyboardThread(); + + void openKeyboard(const char* initialText); + + const char* getResult() { + if (mThread->isDone()) { + return mResultString.cstr(); + } + return nullptr; + }; + + bool isThreadDone() { return mThread->isDone(); } + + void setHeaderText(const char16_t* text) { mHeaderText = text; } + void setSubText(const char16_t* text) { mSubText = text; } + + private: + + al::AsyncFunctorThread* mThread; + nn::swkbd::String mResultString; + + bool mIsDoneKeyboard; + + sead::FixedSafeString<0x10> mInitialText; + + const char16_t *mHeaderText = u"Enter Server IP Here!"; + const char16_t *mSubText = u"Must be a Valid Address."; + + char* mWorkBuf; + int mWorkBufSize; + char* mTextCheckBuf; + int mTextCheckSize; + char* mCustomizeDicBuf; + int mCustomizeDicSize; +}; \ No newline at end of file diff --git a/include/SocketBase.hpp b/include/SocketBase.hpp new file mode 100644 index 0000000..622b4c0 --- /dev/null +++ b/include/SocketBase.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include "types.h" +#include "nn.h" +#include "sead/basis/seadNew.h" + +class SocketBase { + + public: + SocketBase(const char *name); + + virtual nn::Result init(const char * ip, u16 port) = 0; + + const char *getStateChar(); + u8 getLogState(); + s32 getSocket(); + + void set_sock_flags(int flags); + + bool closeSocket(); + + void setName(const char *name) {strcpy(sockName, name);}; + + protected: + s32 socket_log(const char* str); + s32 socket_read_char(char *out); + + char sockName[0x10] = {}; + const char *sock_ip; + + u16 port; + u8 socket_log_state; + s32 socket_log_socket; + + int sock_flags; +}; + + + diff --git a/include/actors/PuppetActor.h b/include/actors/PuppetActor.h new file mode 100644 index 0000000..e6ed730 --- /dev/null +++ b/include/actors/PuppetActor.h @@ -0,0 +1,85 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/async/FunctorV0M.hpp" +#include "al/async/FunctorBase.h" +#include "al/util.hpp" +#include "al/string/StringTmp.h" +#include "al/layout/BalloonMessage.h" + +#include "game/Player/PlayerFunction.h" +#include "game/Player/PlayerJointControlPartsDynamics.h" +#include "game/Player/PlayerConst.h" +#include "game/Player/PlayerModelHolder.h" + +#include "actors/PuppetCapActor.h" +#include "actors/PuppetHackActor.h" +#include "layouts/NameTag.h" +#include "sead/math/seadVector.h" +#include "server/DeltaTime.hpp" + +#include "logger.hpp" +#include "puppets/PuppetInfo.h" +#include "puppets/HackModelHolder.hpp" +#include "helpers.hpp" +#include "algorithms/CaptureTypes.h" + +class PuppetActor : public al::LiveActor { + public: + PuppetActor(const char *name); + virtual void init(al::ActorInitInfo const &) override; + virtual void initAfterPlacement(void) override; + virtual void control(void) override; + virtual void movement(void) override; + virtual void makeActorAlive(void) override; + virtual void makeActorDead(void) override; + + void initOnline(PuppetInfo *pupInfo); + + void startAction(const char *actName); + void hairControl(); + + void setBlendWeight(int index, float weight) { al::setSklAnimBlendWeight(getCurrentModel(), weight, index); }; + + bool isNeedBlending(); + + bool isInCaptureList(const char *hackName); + + PuppetInfo* getInfo() { return mInfo; } + + const char *getPuppetName() { return mInfo->puppetName; } + + bool addCapture(PuppetHackActor *capture, const char *hackType); + + al::LiveActor* getCurrentModel(); + + int getMaxCaptures() {return mCaptures->getEntryCount(); }; + + void debugTeleportCaptures(const sead::Vector3f& pos); + + void debugTeleportCapture(const sead::Vector3f& pos, int index); + + bool mIsDebug = false; + + float mClosingSpeed = 0; + + NameTag *mNameTag = nullptr; // temp public + private: + void changeModel(const char* newModel); + + bool setCapture(const char* captureName); + + void syncPose(); + + PlayerCostumeInfo *mCostumeInfo = nullptr; + PuppetInfo *mInfo = nullptr; + PuppetCapActor *mPuppetCap = nullptr; + PlayerModelHolder *mModelHolder = nullptr; + HackModelHolder* mCaptures = nullptr; + + CaptureTypes::Type mCurCapture = CaptureTypes::Type::Unknown; + + bool mIs2DModel = false; + + bool mIsCaptureModel = false; +}; diff --git a/include/actors/PuppetCapActor.h b/include/actors/PuppetCapActor.h new file mode 100644 index 0000000..3e13e89 --- /dev/null +++ b/include/actors/PuppetCapActor.h @@ -0,0 +1,28 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/util.hpp" + +#include "game/Player/PlayerFunction.h" +#include "game/Player/HackCap/HackCapJointControlKeeper.h" + +#include "logger.hpp" +#include "puppets/PuppetInfo.h" +#include "helpers.hpp" + +class PuppetCapActor : public al::LiveActor { + public: + PuppetCapActor(const char *name); + virtual void init(al::ActorInitInfo const &) override; + virtual void initAfterPlacement() override; + virtual void control(void) override; + virtual void movement(void) override; + void initOnline(PuppetInfo *info); + + void startAction(const char *actName); + void update(); + + private: + HackCapJointControlKeeper *mJointKeeper; + PuppetInfo *mInfo; +}; diff --git a/include/actors/PuppetHackActor.h b/include/actors/PuppetHackActor.h new file mode 100644 index 0000000..6e32658 --- /dev/null +++ b/include/actors/PuppetHackActor.h @@ -0,0 +1,28 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/util.hpp" + +#include "logger.hpp" +#include "puppets/PuppetInfo.h" +#include "helpers.hpp" + +// TODO: Make this actor only created once per puppet, and use SubActorKeeper to create PartsModel actors for each capture + +class PuppetHackActor : public al::LiveActor { + public: + PuppetHackActor(const char *name); + virtual void init(al::ActorInitInfo const &) override; + virtual void initAfterPlacement() override; + virtual void control(void) override; + virtual void movement(void) override; + void initOnline(PuppetInfo *info, const char *hackType); + + void startAction(const char* actName); + + void startHackAnim(bool isOn); + + private: + PuppetInfo *mInfo; + sead::FixedSafeString<0x20> mHackType; +}; diff --git a/include/agl/DisplayList.h b/include/agl/DisplayList.h new file mode 100644 index 0000000..15b693c --- /dev/null +++ b/include/agl/DisplayList.h @@ -0,0 +1,52 @@ +/** + * @file DisplayList.h + * @brief Defines a display list for the GPU. + */ + +#pragma once + +#include "types.h" +#include "agl/gpu.h" +#include "sead/heap/seadHeap.h" + +namespace agl +{ + class DisplayList + { + public: + DisplayList(); + + virtual ~DisplayList(); + + void setControlMemeory(void *, s32); + void clear(); + void setBuffer(agl::GPUMemAddr, u64); + void setValidSize(u64 size); + void copyTo(agl::DisplayList *) const; + void beginDisplayList(); + void endDisplayList(); + bool beginDisplayListBuffer(agl::GPUMemAddr, u64, bool); + void endDisplayListBuffer(sead::Heap *); + void adjustValueSize(); + void invalidateCPUCache() const; + void dump() const; + bool suspend(void **); + void resume(void *, u64); + u64 calcRemainingSize(); + + u64 _8; + u64 _10; + u64 _18; + u32 mUsedSize; // _20 + u32 _24; + u32 mSize; // _28 + u32 _2C; + u64 _30; + u64 _38; + u8 _40[0x248-0x40]; // todo; what is here? + u32 _248; // init'd to 0x200 + u32 _24C; + u64 _250; + char* mDisplayName; // _258 + }; +}; \ No newline at end of file diff --git a/include/agl/DrawContext.h b/include/agl/DrawContext.h new file mode 100644 index 0000000..de47183 --- /dev/null +++ b/include/agl/DrawContext.h @@ -0,0 +1,41 @@ +/** + * @file DrawContext.h + * @brief Defines a draw context for textures in a GPU buffer. + */ + +#pragma once + +#include "types.h" +#include "DisplayList.h" + +#include +#include + +namespace agl +{ + class DrawContext : public sead::DrawContext + { + public: + DrawContext(); + virtual ~DrawContext(); + + void setCommandBuffer(agl::DisplayList *); + void flushCommandBuffer(); + void setBoundRenderBuffer(u64 *); + void barrierTexture(u32); + void barrierShader(u32); + bool isTextureDirty(u32, s32) const; + void setTextureDirty(s32); + void setCommandBufferTemporary(); + + agl::DisplayList* mDisplayList; // _F0 + u64* _F8; // agl::RenderBuffer* + u8 _100; + u8 _101; + u8 _102; + u8 _103; + u32 _104; + u32 _10C; + sead::CriticalSection mCriticalSection; // _110 + }; +}; \ No newline at end of file diff --git a/include/agl/RenderBuffer.h b/include/agl/RenderBuffer.h new file mode 100644 index 0000000..30c6759 --- /dev/null +++ b/include/agl/RenderBuffer.h @@ -0,0 +1,48 @@ +/** + * @file RenderBuffer.h + * @brief Defines classes that implement a render buffer. + */ + +#pragma once + +#include "RenderTargetColor.h" +#include "RenderTargetDepth.h" + +#include "sead/gfx/seadFrameBuffer.h" + +namespace agl +{ + class RenderBuffer : public sead::FrameBuffer + { + public: + RenderBuffer(); + RenderBuffer(sead::Vector2 const &, sead::BoundBox2 const &); + RenderBuffer(sead::Vector2 const &, f32, f32, f32, f32); + virtual ~RenderBuffer(); + + virtual void copyToDisplayBuffer(sead::DrawContext *, sead::DisplayBuffer const *); + virtual void clear(sead::DrawContext *, u32, sead::Color4f const &, f32, u32); + virtual void bindImpl_(sead::DrawContext *) const; + + void initialize_(); + void setRenderTargetColorNullAll(); + void adjustPhysicalAreaAndVirtualSizeFromColorTarget(u32); + void invalidateGPUCache(agl::DrawContext *) const; + void bind_(agl::DrawContext *, u16) const; + + u32 _8; + u32 C; + u64 _10; + u32 _18; + u32 _1C; + agl::RenderTargetColor* _20; + agl::RenderTargetColor* _28; + agl::RenderTargetColor* _30; + agl::RenderTargetColor* _38; + agl::RenderTargetColor* _40; + agl::RenderTargetColor* _48; + agl::RenderTargetColor* _50; + agl::RenderTargetColor* _58; + agl::RenderTargetDepth* _60; + }; +}; \ No newline at end of file diff --git a/include/agl/RenderTargetColor.h b/include/agl/RenderTargetColor.h new file mode 100644 index 0000000..37079c9 --- /dev/null +++ b/include/agl/RenderTargetColor.h @@ -0,0 +1,34 @@ +/** + * @file RenderTargetColor.h + * @brief Defines classes implement a rendering target color. + */ + +#pragma once + +#include "TextureData.h" + +namespace agl +{ + class RenderTargetColor : agl::TextureData + { + public: + RenderTargetColor(); + RenderTargetColor(agl::TextureData const &, u32, u32); + + void onApplyTextureData_(); + void initRegs_(u32) const; + + s32 _128; + u32 _12C; + u32 _130; + u32 _134; + u64 _138; + u64 _140; + u64 _148; + u64 _150; + u64 _158; + u64 _160; + u64 _168; + u64 _170; + }; +}; \ No newline at end of file diff --git a/include/agl/RenderTargetDepth.h b/include/agl/RenderTargetDepth.h new file mode 100644 index 0000000..427232e --- /dev/null +++ b/include/agl/RenderTargetDepth.h @@ -0,0 +1,29 @@ +/** + * @file RenderTargetDepth.h + * @brief Defines a class to represent a render target depth. + */ + +#pragma once + +#include "TextureData.h" + +namespace agl +{ + class RenderTargetDepth : public agl::TextureData + { + public: + RenderTargetDepth(); + RenderTargetDepth(agl::TextureData const &, u32, u32); + + void onApplyTextureData_(); + void initRegs_(u32) const; + + s32 _128; + u32 _12C; + u64 _130; + u64 _138; + u64 _140; + u64 _148; + u8 _150[0x178-0x150]; + }; +}; \ No newline at end of file diff --git a/include/agl/TextureData.h b/include/agl/TextureData.h new file mode 100644 index 0000000..9ee6358 --- /dev/null +++ b/include/agl/TextureData.h @@ -0,0 +1,63 @@ +/** + * @file TextureData.h + * @brief Defines a class that implements a texture compressor, and a texture data storage container. + */ + +#pragma once + +#include "detail/Surface.h" +#include "driver/NVNtexture.h" + +#include "sead/prim/seadSafeString.h" + +namespace agl +{ + class TextureData + { + public: + class CompressToWork + { + public: + CompressToWork(agl::TextureData const &); + + u64 _0; + u64 _8; + u64 _10; + u64 _18; + u32 _20; + u64 _28; + u64 _30; + u32 _38; + u64 _40; + agl::detail::Surface mSurface; // _48 + agl::driver::NVNtexture_ mTexture; // _70 + }; + + TextureData(); + + void setMipLevelNum_(s32, bool); + u16 getMinSlice_() const; + void getTextureFormatName() const; + u32 calcMipByteSize(u32) const; + bool isCompressedFormat() const; + bool isRenderTargetCompressAvailable() const; + bool isDepthFormat() const; + bool hasStencil() const; + void invalidateCPUCache(); + void flushCPUCache() const; + void setDebugLabel(sead::SafeStringBase const &); + void getDebugLabel() const; + + u64 _0; + u32 _8; + u32 C; + u64 _10; + u64 _18; + u64 _20; + u64 _28; + agl::detail::Surface mSurface; // _30 + agl::TextureFormat mTextureFormat; // _54 + u8 _58[0x120-0x58]; + char* _120; // "agl::TextureData string" + }; +}; \ No newline at end of file diff --git a/include/agl/detail/FileIOMgr.h b/include/agl/detail/FileIOMgr.h new file mode 100644 index 0000000..9bbc6de --- /dev/null +++ b/include/agl/detail/FileIOMgr.h @@ -0,0 +1,77 @@ +/** + * @file FileIOMgr.h + * @brief Defines classes that handle shader management I/O. + */ + +#pragma once + +#include "sead/heap.h" +#include "sead/hostio.h" +#include "sead/string.h" +#include "sead/xml.h" + +namespace agl +{ + namespace detail + { + class FileIOMgr : public sead::hostio::Node + { + public: + + class SingletonDisposer + { + public: + ~SingletonDisposer(); + }; + + class CreateArg + { + public: + CreateArg(); + + u32 mArg; // _0 + }; + + class DialogArg + { + public: + DialogArg(); + + u64* _0; + u64* _8; + u64 _10; + u64 _18; + u64 _20; + char* mMsg; // _28 + u64* _30; + u64* _38; + u64 _40; + u32 _48; // set to 0x20 + u8 _4C; + u8 _4D; + u8 _4E; + u8 _4F; + u32 _50; + }; + + FileIOMgr(); + + void initialize(agl::detail::FileIOMgr::CreateArg const &, sead::Heap *); + void setCheckoutCommandPath(sead::SafeStringBase const &); + void save(sead::XmlDocument const &, agl::detail::FileIOMgr::DialogArg const &, u32); + void showDialog(sead::hostio::FileInfo *, sead::SafeStringBase const &, sead::SafeStringBase const &, sead::SafeStringBase const &, sead::SafeStringBase const &) const; + void checkout_(sead::SafeStringBase const &) const; + void showErrorDialog_(sead::SafeStringBase const &) const; + void save(void const*, u32, agl::detail::FileIOMgr::DialogArg const &); + s32 load(agl::detail::FileIOMgr::DialogArg const &); + void close(s32); + void genMessage(sead::hostio::Context *); + void listenPropertyEvent(sead::hostio::PropertyEvent const *); + + static agl::detail::FileIOMgr* createInstance(sead::Heap *); + static void deleteInstance(); + + u64* _28; // sead::NinHostIOFileDevice * + }; + }; +}; \ No newline at end of file diff --git a/include/agl/detail/MemoryPool.h b/include/agl/detail/MemoryPool.h new file mode 100644 index 0000000..223994f --- /dev/null +++ b/include/agl/detail/MemoryPool.h @@ -0,0 +1,39 @@ +/** + * @file MemoryPool.h + * @brief Defines classes for memory pools for storage. + */ + +#pragma once + +#include "types.h" + +namespace agl +{ + typedef s32 MemoryAttribute; + + namespace detail + { + class MemoryPoolType + { + public: + static s32 convert(agl::MemoryAttribute); + + static u32 cInvalidPoolType; // 0 + static u32 cValidPoolType; // 0x80000000 + }; + + class MemoryPool + { + public: + MemoryPool(); + + void initialize(void *, u64, agl::detail::MemoryPoolType const &); + void initialize(void *, u64, agl::detail::MemoryPoolType const &, agl::detail::MemoryPool const &, s32); + + void finalize(); + + u8 _0[0x104]; // todo: where do the parent 0x100 bytes come from? + + }; + }; +}; \ No newline at end of file diff --git a/include/agl/detail/MemoryPoolHeap.h b/include/agl/detail/MemoryPoolHeap.h new file mode 100644 index 0000000..a5c6a3d --- /dev/null +++ b/include/agl/detail/MemoryPoolHeap.h @@ -0,0 +1,36 @@ +/** + * @file MemoryPoolHeap.h + * @brief Defines classes that manage heaps for memory pools. + */ + +#pragma once + +#include "types.h" +#include "MemoryPool.h" + +namespace agl +{ + class GPUMemBlockBase; + + namespace detail + { + class GPUMemBlockMgrHeapEx; + + class MemoryPoolHeap + { + public: + MemoryPoolHeap(void *, u64, u64, agl::detail::MemoryPoolType const &, void *, u64, agl::detail::GPUMemBlockMgrHeapEx *); + ~MemoryPoolHeap(); + + static agl::detail::MemoryPoolHeap* create(u64, s32, u64, s32, u64, u64, agl::detail::MemoryPoolType const &, agl::detail::GPUMemBlockMgrHeapEx *); + static void destroy(agl::detail::MemoryPoolHeap *); + + void pushBack(agl::GPUMemBlockBase *); + u64* allocFromMemoryPool(u64, s32); + void freeToHeap(agl::GPUMemBlockBase *); + bool isAllocatable(agl::detail::MemoryPoolType const &, u64, s32) const; + + agl::GPUMemBlockBase* _120; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/detail/ShaderHolder.h b/include/agl/detail/ShaderHolder.h new file mode 100644 index 0000000..cbb260a --- /dev/null +++ b/include/agl/detail/ShaderHolder.h @@ -0,0 +1,32 @@ +/** + * @file ShaderHolder.h + * @brief Defines classes for shader storage. + */ + +#pragma once + +#include "sead/heap.h" + +namespace agl +{ + namespace detail + { + class ShaderHolder + { + public: + + class SingletonDisposer_ + { + public: + virtual ~SingletonDisposer_(); + + static SingletonDisposer_ sStaticDisposer; + }; + + ShaderHolder(); + + static agl::detail::ShaderHolder* createInstance(sead::Heap *); + static void deleteInstance(); + }; + }; +}; \ No newline at end of file diff --git a/include/agl/detail/Surface.h b/include/agl/detail/Surface.h new file mode 100644 index 0000000..f75c906 --- /dev/null +++ b/include/agl/detail/Surface.h @@ -0,0 +1,56 @@ +/** + * @file Surface.h + * @brief Defines classes to setup NVN textures on a surface. + */ + +#pragma once + +#include "types.h" +#include "agl/util.h" + +class NVNtextureBuilder; +class NVNtexture; + +namespace agl +{ + namespace detail + { + struct SurfaceBase; + + class Surface + { + public: + Surface(); + void initialize(agl::TextureType, agl::TextureFormat, u32, agl::TextureAttribute, agl::MultiSampleType); + void initializeSize(u32, u32, u32); + void copyFrom(agl::detail::SurfaceBase const &); + void calcSizeAndAlignment(); + void setupNVNtextureBuilder(NVNtextureBuilder *) const; + void printInfo() const; + void copyFrom(NVNtexture const &); + + u16 _0; + u16 _2; + u16 _4; + u16 _6; + u8 _8; + u8 _9; + u16 _A; + u8 C[0x1A-0xC]; + u8 _1A; + u8 _1B; + u32 _1C; + u8 _20; + u8 _21; + u8 _22; + u8 _23; + }; + + struct SurfaceBase + { + u64 _0; + u64 _8; + u64 _10; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/driver/NVNimage.h b/include/agl/driver/NVNimage.h new file mode 100644 index 0000000..fe11da4 --- /dev/null +++ b/include/agl/driver/NVNimage.h @@ -0,0 +1,26 @@ +/** + * @file NVNimage.h + * @brief Defines a class for representing a NVN image. + */ + +#pragma once + +#include "types.h" + +namespace agl +{ + namespace driver + { + class NVNimage_ + { + public: + NVNimage_(); + NVNimage_(agl::driver::NVNimage_ const &); + ~NVNimage_(); + + void updateImageId(s32 id); + + u64 mImageId; // _0 + }; + }; +}; \ No newline at end of file diff --git a/include/agl/driver/NVNsampler.h b/include/agl/driver/NVNsampler.h new file mode 100644 index 0000000..815dd2d --- /dev/null +++ b/include/agl/driver/NVNsampler.h @@ -0,0 +1,31 @@ +/** + * @file NVNsampler.h + * @brief Defines a sampler for NVN images. + */ + +#pragma once + +#include "types.h" + +class NVNsampler; + +namespace agl +{ + namespace driver + { + class NVNsampler_ + { + public: + NVNsampler_(); + NVNsampler_(agl::driver::NVNsampler_ const &); + ~NVNsampler_(); + + void releaseSampler(); + bool registerSampler(NVNsampler const &, char const *); + void updateTextureId(s32); + + u64 _0; + s16 _8; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/driver/NVNtexture.h b/include/agl/driver/NVNtexture.h new file mode 100644 index 0000000..3478f5b --- /dev/null +++ b/include/agl/driver/NVNtexture.h @@ -0,0 +1,39 @@ +/** + * @file device.h + * @brief Defines a class to represent a NVN texture. + */ + +#pragma once + +#include "types.h" + +class NVNtextureView; +class NVNtexture; + +namespace agl +{ + namespace driver + { + class NVNtexture_ + { + public: + NVNtexture_(); + NVNtexture_(agl::driver::NVNtexture_ const &); + ~NVNtexture_(); + + void releaseTexture(); + void updateTexId_(s32 newID); + //agl::driver:NVNtexture_ operator=(agl::driver::NVNtexture_ const &); + bool registerTexture(NVNtexture const *, NVNtextureView const *, char const *, bool); + + void setReference_() const; + + u8 _0[0xC0]; // NVNtexture + s32 mTextureID; // _C0 + u8 _C4; + u8 _C5; + u8 _C6; + u8 _C7; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/g3d.h b/include/agl/g3d.h new file mode 100644 index 0000000..dab120a --- /dev/null +++ b/include/agl/g3d.h @@ -0,0 +1,3 @@ +#pragma once + +#include \ No newline at end of file diff --git a/include/agl/g3d/g3d_ResFile.h b/include/agl/g3d/g3d_ResFile.h new file mode 100644 index 0000000..7e94ac1 --- /dev/null +++ b/include/agl/g3d/g3d_ResFile.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace agl +{ + namespace g3d + { + class ResFile + { + public: + static void BindTexture(nn::g3d::ResFile *, nn::g3d::ResFile *); + static void Cleanup(nn::g3d::ResFile *); + }; + }; +}; \ No newline at end of file diff --git a/include/agl/gpu.h b/include/agl/gpu.h new file mode 100644 index 0000000..884e3b2 --- /dev/null +++ b/include/agl/gpu.h @@ -0,0 +1,93 @@ +/** + * @file gpu.h + * @brief Defines classes that use the GPU, such as memory blocks, and address blocks. + */ + +#pragma once + +#include "types.h" +#include "agl/detail/MemoryPool.h" +#include "agl/detail/MemoryPoolHeap.h" +#include "nn/gfx/api.h" +#include "nn/gfx/memory.h" +#include "sead/heap/seadHeap.h" + +namespace agl +{ + typedef u64 GPUMemVoidAddr; + + class GPUMemBlockBase + { + public: + GPUMemBlockBase(); + + virtual ~GPUMemBlockBase(); + + void clear(); + void freeBuffer(); + void free(); + void allocBuffer_(u64, sead::Heap *, s32, agl::MemoryAttribute); + bool tryAllocBuffer_(u64, sead::Heap *, s32, agl::MemoryAttribute); + void setBuffer_(u64, void *, void *, agl::MemoryAttribute); + void setVirtual_(u64, sead::Heap *, agl::MemoryAttribute, agl::GPUMemVoidAddr, s32); + void initializeGfxMemoryPool(nn::gfx::TMemoryPool, nn::gfx::ApiVersion<8>>> *) const; + void addList(agl::GPUMemBlockBase*); + void setMemoryPool(void *, u64, agl::detail::MemoryPool *); + void setMemoryPoolHeap(void *, u64, agl::detail::MemoryPoolHeap *); + u64 getByteOffset() const; + u64 getMemoryPoolType() const; + + void* mMemBlockBuffer; // _8 + u64 mMemBlockBufferSize; // _10 + agl::detail::MemoryPool* mMemoryPool; // _18 + agl::detail::MemoryPoolHeap* mMemoryPoolHeap; // _20 + u8 _28; // this is some sort of bitflag + u8 _29; + u8 _2A; + u8 _2B; + u32 _2C; + u64 _30; + }; + + class GPUMemAddrBase + { + public: + GPUMemAddrBase(agl::GPUMemBlockBase const &, u64); + + u32 verify_() const; + void deleteGPUMemBlock() const; + void invalidate(); + u32 getAlignmentAddress() const; + void setByteOffsetByPtr(void *); + void roundUp(s32); + void flushCPUCache(u64); + void invalidateCPUCache(u64); + + agl::detail::MemoryPool* mMemoryPool; // _0 + u32 mAlignmentAddr; // _8 + u32 GPUMemAddrBase_C; // most likely padding bytes + agl::GPUMemBlockBase* mMemoryBlock; // _10 + }; + + template + class GPUMemBlock : public agl::GPUMemBlockBase + { + public: + virtual ~GPUMemBlock(); + }; + + template + class GPUMemBlockT : public agl::GPUMemBlockBase + { + public: + virtual ~GPUMemBlockT(); + }; + + template + class GPUMemAddr + { + public: + u64* mPtr; // _0 + u64 _8; + }; +}; \ No newline at end of file diff --git a/include/agl/shader/Shader.h b/include/agl/shader/Shader.h new file mode 100644 index 0000000..5ee6079 --- /dev/null +++ b/include/agl/shader/Shader.h @@ -0,0 +1,69 @@ +/** + * @file Shader.h + * @brief Defines a basic shader. + */ + +#pragma once + +#include "types.h" + +namespace agl +{ + enum ShaderType + { + ShaderType_Vertex = 0, + ShaderType_Fragment = 1, + ShaderType_Geometry = 2, + ShaderType_Compute = 3 + }; + + class Shader + { + public: + Shader(); + + virtual ~Shader(); + + virtual s32 getShaderType() const = 0; + virtual s32 getShaderMode() const; + virtual s32 getRingItemSize() const; + + void setBinary(void const *shaderBinary); + + void* mShaderBinary; // _8 + u64 _10; + u64 _18; + }; + + class VertexShader : public agl::Shader + { + public: + virtual ~VertexShader(); + + virtual s32 getShaderType() const; + }; + + class FragmentShader : public agl::Shader + { + public: + virtual ~FragmentShader(); + + virtual s32 getShaderType() const; + }; + + class GeometryShader : public agl::Shader + { + public: + virtual ~GeometryShader(); + + virtual s32 getShaderType() const; + }; + + class ComputeShader : public agl::Shader + { + public: + virtual ~ComputeShader(); + + virtual s32 getShaderType() const; + }; +}; \ No newline at end of file diff --git a/include/agl/shader/ShaderCompileInfo.h b/include/agl/shader/ShaderCompileInfo.h new file mode 100644 index 0000000..eb4ce8b --- /dev/null +++ b/include/agl/shader/ShaderCompileInfo.h @@ -0,0 +1,42 @@ +/** + * @file ShaderCompileInfo.h + * @brief Defines file devices and extensions. + */ + +#pragma once + +#include "sead/heap.h" +#include "sead/hostio.h" +#include "sead/array.h" +#include "Shader.h" +#include "types.h" + +namespace agl +{ + class ShaderCompileInfo : public sead::hostio::Node + { + // this value is used as an index to a table of version lists + // on 1.2.0, located at 0x7101E80B30 + typedef s32 Target; + + ShaderCompileInfo(); + + virtual ~ShaderCompileInfo(); + + void destroy(); + void create(s32, s32 bufferSize, sead::Heap *); + void clearVariation(); + void pushBackVariation(char const *, char const *); + void calcCompileSource(agl::ShaderType, sead::BufferedSafeStringBase *, agl::ShaderCompileInfo::Target, bool); + sead::SafeStringBase* getRegitserUniformBlockName(); // "RegisterUBO" + + u64 _8; + char* mName; // _10 + u64 _18; + u64 _20; + sead::PtrArrayImpl _28; + sead::PtrArrayImpl _38; + sead::PtrArrayImpl _48; + sead::PtrArrayImpl _58; + }; +}; \ No newline at end of file diff --git a/include/agl/shader/ShaderProgram.h b/include/agl/shader/ShaderProgram.h new file mode 100644 index 0000000..518db92 --- /dev/null +++ b/include/agl/shader/ShaderProgram.h @@ -0,0 +1,67 @@ +/** + * @file ShaderProgram.h + * @brief Defines a general shader program to compile raw shaders with macros. + */ + +#pragma once + +#include "agl/DisplayList.h" +#include "sead/heap.h" +#include "sead/string.h" +#include "Shader.h" + +namespace agl +{ + class ShaderProgram + { + public: + + class VariationBuffer + { + public: + void initialize(s32, sead::Heap *); + void createMacro(s32, sead::SafeStringBase const &, sead::SafeStringBase const &, s32, sead::Heap *); + void setMacroValue(s32, s32, sead::SafeStringBase const &); + void create(sead::Heap *); + }; + + ShaderProgram(); + + virtual ~ShaderProgram(); + + void cleanUp(); + void destroyLocationBuffers(); + void initializeVariation(sead::SafeStringBase const &, s32, sead::Heap *); + void createVariationMacro(s32, sead::SafeStringBase const &, sead::SafeStringBase const &, s32, sead::Heap *); + void setVariationMacroValue(s32, s32, sead::SafeStringBase const &); + void createVariation(sead::Heap *); + + u64* _8; + agl::DisplayList mShaderDisplayList; // _10 + u32 _278; + u32 _27C; + u64 _280; + u32 _288; + u32 _28C; + u64 _290; + u32 _298; + u32 _29C; + u64 _2A0; + u32 _2A8; + u32 _2AC; + u64 _2B0; + u32 _2B8; + u32 _2BC; + u64 _2C0; + u32 _2C8; + u32 _2CC; + u64 _2D0; + agl::VertexShader mVertexShader; // _2D8 + agl::FragmentShader mFragmentShader; // _2F8 + agl::GeometryShader mGeometryShader; // _318 + agl::ComputeShader mComputeShader; // _338 + u8 _358[0x418-0x358]; // todo; what is here? + u64 _418; + u32 _420; + }; +}; \ No newline at end of file diff --git a/include/agl/shader/ShaderProgramArchive.h b/include/agl/shader/ShaderProgramArchive.h new file mode 100644 index 0000000..6ddc40c --- /dev/null +++ b/include/agl/shader/ShaderProgramArchive.h @@ -0,0 +1,38 @@ +/** + * @file ShaderProgramArchive.h + * @brief Defines a class wrapper for SHARCB (Shader Binary) archives. + */ + +#pragma once + +#include "sead/disposer.h" +#include "sead/heap.h" +#include "sead/hostio.h" + +namespace agl +{ + class ResBinaryShaderArchive; + class ResShaderArchive; + class ResShaderProgram; + class ResShaderSource; + + class ShaderProgramArchive : public sead::IDisposer, public sead::hostio::Node + { + public: + ShaderProgramArchive(); + + virtual ~ShaderProgramArchive(); + + void destroy(); + void createWithOption(agl::ResBinaryShaderArchive, agl::ResShaderArchive, u32, sead::Heap *); + void destroyResFile_(); + void initialize(agl::ShaderProgramArchive *, s32, agl::ResShaderProgram, sead::Heap *); + void updateCompileInfo(); + void setUp(); + void setUp_(bool); + + u64 _20; + agl::ResBinaryShaderArchive* mBinaryShaderArchive; // _28 + agl::ResShaderArchive* mResShaderArchive; // _30 + }; +}; \ No newline at end of file diff --git a/include/agl/shtxt/Clause.h b/include/agl/shtxt/Clause.h new file mode 100644 index 0000000..40bcf42 --- /dev/null +++ b/include/agl/shtxt/Clause.h @@ -0,0 +1,56 @@ +/** + * @file Clause.h + * @brief Defines a clause for shader processing. + */ + +#pragma once + +#include "sead/string.h" +#include "types.h" + +namespace agl +{ + namespace shtxt + { + class Clause + { + public: + class TableChecker + { + public: + TableChecker(); + }; + + typedef u8 Type; + + Clause(); + Clause(agl::shtxt::Clause::Type, char const *, char const *); + ~Clause(); + + u64 findNumberBlock(agl::shtxt::Clause::Type *, char const *); + void appendTo(sead::BufferedSafeStringBase *) const; + void appendTo(sead::BufferedSafeStringBase *, u32) const; + void copyTo(sead::BufferedSafeStringBase *) const; + f64 toNumber() const; + f64 forceNumber() const; + u32 calcLineFeedCount() const; + bool compareImpl(agl::shtxt::Clause const &, u32, agl::shtxt::Clause const *, agl::shtxt::Clause const *) const; + bool compare(sead::SafeStringBase const &, u32) const; + bool compare(agl::shtxt::Clause const &, u32) const; + s32 calcHash(void const *, u32, u32) const; + + agl::shtxt::Clause* _0; + agl::shtxt::Clause* _8; + u8 mClauseType; // _10 + u8 _11; + u8 _12; + u8 _13; + u32 _14; + char* _18; + char* _20; + + static bool cTableChecked; + static u32* cHashTable; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/shtxt/DefineLinker.h b/include/agl/shtxt/DefineLinker.h new file mode 100644 index 0000000..7645d99 --- /dev/null +++ b/include/agl/shtxt/DefineLinker.h @@ -0,0 +1,45 @@ +/** + * @file DefineLinker.h + * @brief Defines a linker for shader source compilation. + */ + +#pragma once + +#include "sead/heap.h" +#include "types.h" + +namespace agl +{ + namespace shtxt + { + class Clause; + + class DefineLinker + { + public: + DefineLinker(); + ~DefineLinker(); + + void clear(); + bool set(sead::Heap *, agl::shtxt::Clause const *, agl::shtxt::Clause const *); + bool setImpl(sead::Heap *, agl::shtxt::Clause const *, agl::shtxt::Clause const *, bool); + void setDirect(agl::shtxt::Clause *, u32, bool); + void updateHash(); + void replace(sead::Heap *, agl::shtxt::Clause const *, agl::shtxt::Clause const *, bool); + agl::shtxt::DefineLinker* clone(sead::Heap *, sead::Heap *) const; + agl::shtxt::DefineLinker* cloneAll(sead::Heap *, sead::Heap *) const; + + agl::shtxt::DefineLinker* _0; // seems to copy itself twice + agl::shtxt::DefineLinker* _8; + agl::shtxt::Clause* _10; + agl::shtxt::Clause* _18; // hash clause? + u64 _20; + agl::shtxt::Clause* _28; + u64 _30; + u64 _38; + s32 mHash; // _40 + s16 _44; // -1 + u16 _46; // 0x101 + }; + }; +}; \ No newline at end of file diff --git a/include/agl/shtxt/ExpressionEvaluator.h b/include/agl/shtxt/ExpressionEvaluator.h new file mode 100644 index 0000000..e92d5a1 --- /dev/null +++ b/include/agl/shtxt/ExpressionEvaluator.h @@ -0,0 +1,53 @@ +/** + * @file ExpressionEvaluator.h + * @brief Defines an evaluator for shader expressions. + */ + +#pragma once + +#include "sead/array.h" +#include "sead/delegate.h" +#include "sead/heap.h" + +namespace agl +{ + namespace shtxt + { + class Clause; + class SyntaxLeash; + + class ExpressionEvaluator + { + public: + ExpressionEvaluator(); + ExpressionEvaluator(sead::Heap *, sead::Heap *, sead::AnyDelegate1Const const *); + + void initialize(sead::Heap *, sead::Heap *, sead::AnyDelegate1Const const *); + u64 findSyntaxLeash(sead::ObjArray *, agl::shtxt::Clause const *) const; + u64* createBinaryOperatorSyntaxTree(sead::ObjArray *, agl::shtxt::Clause *); + u64* createTernaryOperatorSyntaxTree(sead::ObjArray *, agl::shtxt::Clause *); + u64* createTokenOperatorSyntaxTree(sead::ObjArray *, agl::shtxt::Clause *); + void resolveOperatorTokenConnect(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorUnary(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorMathHigh(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorMathLow(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorShift(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorCompareHigh(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorCompareLow(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorBitOpAnd(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorBitOpXor(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorBitOpOr(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorLogicalAnd(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorLogicalOr(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorTernary(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperatorAssignment(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveOperator(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolveParenthesis(sead::ObjArray *, agl::shtxt::Clause *, agl::shtxt::Clause *); + void resolve(agl::shtxt::Clause *, agl::shtxt::Clause *, bool); + + sead::Heap* _0; + sead::Heap* _8; + sead::AnyDelegate1Const* _10; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/shtxt/Lexer.h b/include/agl/shtxt/Lexer.h new file mode 100644 index 0000000..5225409 --- /dev/null +++ b/include/agl/shtxt/Lexer.h @@ -0,0 +1,36 @@ +/** + * @file Lexer.h + * @brief Defines a lexer for parsing shader clauses. + */ + +#pragma once + +#include "sead/heap.h" + +namespace agl +{ + namespace shtxt + { + class Clause; + + class Lexer + { + public: + Lexer(); + ~Lexer(); + + void initialize(sead::Heap *, char const *, agl::shtxt::Clause *); + void setupCurrentRange(u64 range); + u32 findNumberBlock() const; + agl::shtxt::Clause* createClause(u32) const; + bool execute(bool); + + sead::Heap* _0; + agl::shtxt::Clause* _8; + char* mRefName; // _10 + u64 _18; + u64 _20; + u64 _28; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/shtxt/Preprocessor.h b/include/agl/shtxt/Preprocessor.h new file mode 100644 index 0000000..3c8ca56 --- /dev/null +++ b/include/agl/shtxt/Preprocessor.h @@ -0,0 +1,63 @@ +/** + * @file Preprocessor.h + * @brief Defines a preprocessor for a collection of clauses and macros in a shader program. + */ + +#pragma once + +#include "sead/heap.h" +#include "sead/string.h" + +namespace agl +{ + namespace shtxt + { + class Clause; + class DefineLinker; + class MacroDeployInfo; + class MacroReplaceInfo; + + class Preprocessor + { + public: + Preprocessor(sead::Heap *, sead::Heap *); + ~Preprocessor(); + + void finalize(); + void initialize(char const *); + void removeClause(agl::shtxt::Clause *) const; + void removeClause(agl::shtxt::Clause *, agl::shtxt::Clause *, bool) const; + void removeClauseAll(); + void removeDefineLinkerAll(); + void setReplacedMacro(char const **, char const **, u32); + void setDeployMacro(char const**, u32); + void appendMacro(agl::shtxt::DefineLinker const *); + bool preprocess(u32, u64, u64); + void removeComment(); + void forceLF(); + void reduceSpace(); + void reduceLF(); + void format(bool); + bool construct(sead::BufferedSafeStringBase *dest) const; + u64 calcConstructLength() const; + + + u64 _0; + u64 _8; // some sort of size + u64 _10; + sead::ExpHeap* _18; + u64 _20; + agl::shtxt::Clause* _28; + agl::shtxt::DefineLinker* _30; + char* mRefName; // _38 + agl::shtxt::MacroReplaceInfo* _40; + u32 _48; // related to replace info + u32 _4C; + agl::shtxt::MacroDeployInfo* _50; + u32 _58; // related to deploy info + u32 _5C; + agl::shtxt::DefineLinker* _60; + u64 _68; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/shtxt/SyntaxTree.h b/include/agl/shtxt/SyntaxTree.h new file mode 100644 index 0000000..16c076b --- /dev/null +++ b/include/agl/shtxt/SyntaxTree.h @@ -0,0 +1,39 @@ +/** + * @file SyntaxTree.h + * @brief Defines a syntax tree for shader compilation. + */ + +#pragma once + +#include "sead/delegate.h" +#include "types.h" + +namespace agl +{ + namespace shtxt + { + class Clause; + + class SyntaxTree + { + public: + SyntaxTree(agl::shtxt::Clause *); + ~SyntaxTree(); + + void removeClauseRecursive(sead::AnyDelegate1Const const *); + f64 checkAndGetValue(); + void checkAndEvaluate(agl::shtxt::SyntaxTree const *) const; + f64 evaluate(); + void constructRecursive(sead::Heap *,sead::Heap *); + u64* construct(sead::Heap *, sead::Heap *) const; + + agl::shtxt::SyntaxTree* _0; + agl::shtxt::SyntaxTree* _8; + agl::shtxt::SyntaxTree* _10; + u64 _18; + agl::shtxt::Clause* _20; + u64 _28; + u32 _30; + }; + }; +}; \ No newline at end of file diff --git a/include/agl/util.h b/include/agl/util.h new file mode 100644 index 0000000..fdc58b3 --- /dev/null +++ b/include/agl/util.h @@ -0,0 +1,109 @@ +/** + * @file util.h + * @brief Defines enumerators for texture types and formats. + */ + +#pragma once + +namespace agl +{ + enum TextureType + { + TYPELESS = 0, + UNORM = 1, + SNORM = 2, + UFLOAT = 3, + FLOAT = 4, + SINT = 5, + UINT = 6, + SRGB = 7, + }; + enum TextureFormat + { + INVALID, + R32_G32_B32_A32, + R32_G32_B32, + R16_G16_B16_A16, + R5_G5_B5_A1, + R32_G32, + R32_G8_X24, + D32_S8_X24, + R32_X8_X24, + X32_G8_X24, + R10_G10_B10_A2, + R11_G11_B10, + R8_G8_B8_A8, + R9_G9B9E5_SHAREDEXP, + R8_G8_B8_G8, + R8_G8_B8_X8, + R4_G4_B4_A4, + R16_G16, + R32, + D32, + R24_G8, + D24_S8, + R24_X8, + X24_G8, + R8G8, + R4_G4, + R16, + G8_R8_G8_B8, + D16, + D24, + D24S8, + D32S8, + R8, + A8, + R1, + B5_G6_R5, + A1_B5_G5_R5, + BC1, + BC2, + BC3, + BC4, + BC5, + BC6, + BC7, + AYUV, + Y410, + Y416, + NV12, + P010, + P016, + YUY2, + Y210, + Y216, + NV11, + AI44, + IA44, + P8, + A8P8, + P208, + V208, + V408, + ASTC4x4, + ASTC5x4, + ASTC5x5, + ASTC6x5, + ASTC6x6, + ASTC8x5, + ASTC8x6, + ASTC8x8, + ASTC10x5, + ASTC10x6, + ASTC10x8, + ASTC10x10, + ASTC12x10, + ASTC12x12 + }; + + enum TextureAttribute + { + + }; + + enum MultiSampleType + { + + }; +}; \ No newline at end of file diff --git a/include/agl/utl.h b/include/agl/utl.h new file mode 100644 index 0000000..8400d18 --- /dev/null +++ b/include/agl/utl.h @@ -0,0 +1,23 @@ +/** + * @file GameDataFile.h + * @brief Utilitis for agl namespace. + */ + +#pragma once + +#include "../types.h" +#include "DrawContext.h" +#include "sead/math/seadVector.h" +#include "sead/math/seadMatrix.h" +#include "sead/gfx/seadColor.h" + +namespace agl { + namespace utl { + class DevTools{ + public: + void static beginDrawImm(agl::DrawContext *,sead::Matrix34 const&,sead::Matrix44 const&); + void static drawTriangleImm(agl::DrawContext*, sead::Vector3 const&, sead::Vector3 const&, sead::Vector3 const&, sead::Color4f const&); + void static drawLineImm(agl::DrawContext*, sead::Vector3 const&, sead::Vector3 const&, sead::Color4f const&, float); + }; + }; +}; \ No newline at end of file diff --git a/include/al/HtmlViewer.h b/include/al/HtmlViewer.h new file mode 100644 index 0000000..5ea00ee --- /dev/null +++ b/include/al/HtmlViewer.h @@ -0,0 +1,12 @@ +#pragma once + +#include "sead/prim/seadSafeString.hpp" + +namespace al +{ + class HtmlViewer + { + public: + void call(const char *, sead::BufferedSafeStringBase *) const; + }; +}; \ No newline at end of file diff --git a/include/al/LiveActor/LiveActor.h b/include/al/LiveActor/LiveActor.h new file mode 100644 index 0000000..b27b0ca --- /dev/null +++ b/include/al/LiveActor/LiveActor.h @@ -0,0 +1,124 @@ +#pragma once + +#include "al/actor/ActorInitInfo.h" +#include "al/actor/ActorSceneInfo.h" +#include "al/actor/ActorActionKeeper.h" +#include "al/area/AreaObjDirector.h" +#include "al/audio/AudioKeeper.h" +#include "al/camera/CameraDirector.h" +#include "al/collision/CollisionDirector.h" +#include "al/effect/EffectKeeper.h" +#include "al/hio/HioNode.h" +#include "al/nerve/Nerve.h" +#include "al/nerve/NerveStateCtrl.h" +#include "al/pose/ActorPoseKeeper.h" +#include "al/rail/RailKeeper.h" +#include "al/rail/RailRider.h" +#include "al/scene/SceneObjHolder.h" +#include "al/screen/ScreenPointKeeper.h" +#include "al/sensor/HitSensorKeeper.h" +#include "al/switch/StageSwitchKeeper.h" +#include "al/actor/SubActorKeeper.h" + +// vtable for LiveActor: 1C4EB58 + +namespace al +{ + + class ActorPoseKeeperBase; + class ActorExecuteInfo; + class ActorItemKeeper; + class ActorScoreKeeper; + class Collider; + class CollisionParts; + class ModelKeeper; + class ShadowKeeper; + class ActorPrePassLightKeeper; + class ActorOcclusionKeeper; + class LiveActorFlag; + + class ActorInitInfo; + class HitSensor; + class SensorMsg; + class ScreenPointer; + class ScreenPointTarget; + + class LiveActor : public al::IUseNerve, public al::IUseEffectKeeper, public al::IUseAudioKeeper, public al::IUseStageSwitch, public al::IUseSceneObjHolder, public al::IUseAreaObj, public al::IUseCamera, public al::IUseCollision, public al::IUseRail, public al::IUseHioNode + { + public: + LiveActor(const char *); + + virtual al::NerveKeeper* getNerveKeeper() const; + + virtual void init(const ActorInitInfo &); + virtual void initAfterPlacement(); + virtual void appear(); + virtual void makeActorAlive(); + virtual void kill(); + virtual void makeActorDead(); + virtual void movement(); + virtual void calcAnim(); + virtual void draw() const; + virtual void startClipped(); + virtual void endClipped(); + virtual void attackSensor(HitSensor *, HitSensor *); + virtual bool receiveMsg(const SensorMsg *, HitSensor *, HitSensor *); + virtual bool receiveMsgScreenPoint(const SensorMsg *, ScreenPointer *, ScreenPointTarget *); + + virtual const char *getName() const { return this->mActorName; }; + + virtual sead::Matrix34f *getBaseMtx() const; + + virtual al::EffectKeeper *getEffectKeeper() const { return this->mEffectKeeper; }; + + virtual al::AudioKeeper *getAudioKeeper() const { return this->mAudioKeeper; }; + + virtual al::StageSwitchKeeper *getStageSwitchKeeper() const { return this->mStageSwitchKeeper; }; + + virtual al::RailRider *getRailRider() const + { + if (this->mRailKeeper) + return this->mRailKeeper->getRailRider(); + return nullptr; + }; + + virtual al::SceneObjHolder *getSceneObjHolder() const { return this->mSceneInfo->mSceneObjHolder; }; + + virtual al::CollisionDirector *getCollisionDirector() const { return this->mSceneInfo->mCollisionDirector; }; + + virtual al::AreaObjDirector *getAreaObjDirector() const { return this->mSceneInfo->mAreaObjDirector; }; + + virtual al::CameraDirector *getCameraDirector() const { return this->mSceneInfo->mCameraDirector; }; + + virtual void initStageSwitchKeeper() { this->mStageSwitchKeeper = new StageSwitchKeeper(); }; + + virtual void control(); + + virtual void updateCollider(); + + const char *mActorName; // 0x48 + al::ActorPoseKeeperBase *mPoseKeeper; // 0x50 + al::ActorExecuteInfo *mLayoutExecuteInfo; // 0x58 + al::ActorActionKeeper *mActorActionKeeper; // 0x60 + al::ActorItemKeeper *mActorItemKeeper; // 0x68 + al::ActorScoreKeeper *mActorScoreKeeper; // 0x70 + al::Collider *mCollider; // 0x78 + al::CollisionParts *mCollisionParts; // 0x80 + al::ModelKeeper *mModelKeeper; // 0x88 + al::NerveKeeper *mNerveKeeper; // 0x90 + al::HitSensorKeeper *mHitSensorKeeper; // 0x98 + al::ScreenPointKeeper *mScreenPointKeeper; // 0xA0 + al::EffectKeeper *mEffectKeeper; // 0xA8 + al::AudioKeeper *mAudioKeeper; // 0xB0 + void *gap_4; // 0xB8 + al::StageSwitchKeeper *mStageSwitchKeeper; // 0xC0 + al::RailKeeper *mRailKeeper; // 0xC8 + al::ShadowKeeper *mShadowKeeper; // 0xD0 + al::ActorPrePassLightKeeper *mActorPrePassLightKeeper; // 0xD8 + al::ActorOcclusionKeeper *mActorOcclusionKeeper; // 0xE0 + al::SubActorKeeper *mSubActorKeeper; // 0xE8 + void *gap_6; // 0xF0 + al::ActorSceneInfo *mSceneInfo; // 0xF8 + al::LiveActorFlag *mLiveActorFlag; // 0x100 + }; +}; \ No newline at end of file diff --git a/include/al/LiveActor/LiveActorGroup.h b/include/al/LiveActor/LiveActorGroup.h new file mode 100644 index 0000000..26be814 --- /dev/null +++ b/include/al/LiveActor/LiveActorGroup.h @@ -0,0 +1,21 @@ +#pragma once + +#include "al/hio/HioNode.h" + +namespace al +{ + class LiveActor; + + class LiveActorGroup : public al::HioNode + { + public: + LiveActorGroup(const char *, int); + + virtual void registerActor(al::LiveActor *); + + const char* mGroupName; // _8 + int mMaxActorCount; // _10 + int mActorCount; // _14 + al::LiveActor** mActors; // _18 + }; +}; \ No newline at end of file diff --git a/include/al/PlayerHolder/PlayerHolder.h b/include/al/PlayerHolder/PlayerHolder.h new file mode 100644 index 0000000..068bb93 --- /dev/null +++ b/include/al/PlayerHolder/PlayerHolder.h @@ -0,0 +1,30 @@ +#pragma once + +#include "sead/math/seadVector.h" +#include "al/scene/Scene.h" +#include "al/LiveActor/LiveActor.h" +#include "game/Player/PlayerActorHakoniwa.h" +#include "types.h" + +namespace al { + + class PadRumbleKeeper; + + class PlayerHolder { + public: + PlayerHolder(int bufSize); + void clear(void); + void registerPlayer(al::LiveActor *, al::PadRumbleKeeper *); + PlayerActorHakoniwa *getPlayer(int) const; + PlayerActorHakoniwa *tryGetPlayer(int) const; + int getPlayerNum() const; + int getBufferSize() const {return bufferSize;}; + bool isFull(void) const; + bool isExistPadRumbleKeeper(int) const; + al::PadRumbleKeeper *getPadRumbleKeeper(int) const; + + undefined unkPointer[0x8]; + int bufferSize; + }; + +} \ No newline at end of file diff --git a/include/al/Scene.hpp b/include/al/Scene.hpp new file mode 100644 index 0000000..aef5d7f --- /dev/null +++ b/include/al/Scene.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class Scene + { + + }; +} diff --git a/include/al/action/ActionEffectCtrl.h b/include/al/action/ActionEffectCtrl.h new file mode 100644 index 0000000..247d881 --- /dev/null +++ b/include/al/action/ActionEffectCtrl.h @@ -0,0 +1,11 @@ +#pragma once + +#include "al/effect/EffectKeeper.h" + +namespace al { + class ActionEffectCtrl { + public: + void startAction(char const *); + IUseEffectKeeper *mEffectKeeper; + }; +} \ No newline at end of file diff --git a/include/al/actor/ActorActionKeeper.h b/include/al/actor/ActorActionKeeper.h new file mode 100644 index 0000000..a8cbea5 --- /dev/null +++ b/include/al/actor/ActorActionKeeper.h @@ -0,0 +1,47 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/action/ActionEffectCtrl.h" + +#include "sead/math/seadVector.h" + +namespace al +{ + + class ActorResource; + class ActionAnimCtrl; + class NerveActionCtrl; + class ActionFlagCtrl; + class ActionSeCtrl; + class ActionBgmCtrl; + class ActionPadAndCameraCtrl { + public: + ActionPadAndCameraCtrl(LiveActor const *, al::ActorResource const *, sead::Vector3f const *, char const *); + unsigned char padding[0x18]; + int mRumbleCount; // 0x18 + }; + class ActionScreenEffectCtrl; + class ActorActionKeeper + { + public: + inline ActorActionKeeper(LiveActor *, char const*, ActionAnimCtrl *, NerveActionCtrl *, ActionFlagCtrl *, ActionEffectCtrl *, ActionSeCtrl *, ActionBgmCtrl *, ActionPadAndCameraCtrl *, ActionScreenEffectCtrl *); + void tryCreate(al::LiveActor *,al::ActorResource const*,char const*,char const*); + void startAction(char const*); + void tryStartActionNoAnim(char const*); + void updatePrev(void); + void updatePost(void); + void init(void); + + LiveActor *mParentActor; // 0x0 + const char *mActorName; // 0x8 + bool unkBool; // 0x10 + ActionAnimCtrl *mAnimCtrl; // 0x18 + NerveActionCtrl *mNrvActionCtrl; // 0x20 + ActionFlagCtrl *mFlagCtrl; // 0x28 + ActionEffectCtrl *mEffectCtrl; // 0x30 + ActionSeCtrl *mSeCtrl; // 0x38 + ActionScreenEffectCtrl *mScreenEffectCtrl; // 0x40 + ActionPadAndCameraCtrl *mPadAndCamCtrl; // 0x48 + ActionBgmCtrl *mBgmCtrl; // 0x50 + }; +} // namespace al diff --git a/include/al/actor/ActorCameraTarget.h b/include/al/actor/ActorCameraTarget.h new file mode 100644 index 0000000..b4e1322 --- /dev/null +++ b/include/al/actor/ActorCameraTarget.h @@ -0,0 +1,25 @@ +#pragma once + +#include "sead/math/seadVector.h" +#include "al/LiveActor/LiveActor.h" +#include "al/camera/CameraTargetBase.h" + +namespace al +{ + class ActorCameraTarget : public al::CameraTargetBase + { + public: + ActorCameraTarget(al::LiveActor const *, float, sead::Vector3f const *); + + void calcTrans(sead::Vector3f *) const; + void calcSide(sead::Vector3f *) const; + void calcUp(sead::Vector3f *) const; + void calcFront(sead::Vector3f *) const; + void calcGravity(sead::Vector3f *) const; + void calcVelocity(sead::Vector3f *) const; + + al::LiveActor *actor; // 0x10 + sead::Vector3f *pos; // 0x18 + float distance; // 0x20 + }; +}; \ No newline at end of file diff --git a/include/al/actor/ActorDimensionKeeper.h b/include/al/actor/ActorDimensionKeeper.h new file mode 100644 index 0000000..5fb34a2 --- /dev/null +++ b/include/al/actor/ActorDimensionKeeper.h @@ -0,0 +1,21 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +class ActorDimensionKeeper { + public: + ActorDimensionKeeper(al::LiveActor const *); + void validate(void); + void invalidate(void); + void forceChange2DKeep(void); + void forceEndChange2DKeep(void); + void update(void); + + al::LiveActor *curActor; // 0x0 + bool is2D; // 0x8 + bool is2DModel; // 0x9 + bool unk2; // 0x10 + bool unk3; // 0xA + bool unk4; // 0xB + struct In2DAreaMoveControl *mMoveControl; // 0x10 +}; \ No newline at end of file diff --git a/include/al/actor/ActorExecuteInfo.h b/include/al/actor/ActorExecuteInfo.h new file mode 100644 index 0000000..bf28726 --- /dev/null +++ b/include/al/actor/ActorExecuteInfo.h @@ -0,0 +1,12 @@ +#pragma once + +namespace al +{ + class ExecutorActorExecuteBase; + + class ActorExecuteInfo + { + public: + void addUpdater(al::ExecutorActorExecuteBase *); + }; +}; \ No newline at end of file diff --git a/include/al/actor/ActorInitInfo.h b/include/al/actor/ActorInitInfo.h new file mode 100644 index 0000000..75523f9 --- /dev/null +++ b/include/al/actor/ActorInitInfo.h @@ -0,0 +1,77 @@ +#pragma once + +#include "al/actor/Placement.h" +#include "al/actor/ActorSceneInfo.h" +#include "al/LiveActor/LiveActorGroup.h" +#include "al/execute/ExecuteDirector.h" +#include "al/audio/AudioDirector.h" +#include "al/effect/EffectSystemInfo.h" +#include "al/gamepad/util.h" +#include "al/rumble/PadRumbleDirector.h" +#include "al/scene/SceneObjHolder.h" +#include "game/GameData/GameDataHolderBase.h" + +namespace al +{ + + class ModelDrawBufferCounter; + class ActorResourceHolder; + class HitSensorDirector; + class ScreenPointDirector; + class StageSwitchDirector; + class ViewIdHolder; + class ActorFactory; + class ClippingDirector; + class DemoDirector; + class GravityHolder; + class ItemDirectorBase; + class NatureDirector; + class SceneMsgCtrl; + class SceneStopCtrl; + class ScreenCoverCtrl; + class ShadowDirector; + class ModelGroup; + class GraphicsSystemInfo; + class PlayerHolder; + + class ActorInitInfo + { + public: + ActorInitInfo(); + void initViewIdSelf(al::PlacementInfo const*,al::ActorInitInfo const&); + void initNew(al::PlacementInfo const* placementInfo, al::LayoutInitInfo const* lytInfo, + al::LiveActorGroup* actorGroup, al::ActorFactory const* factory, + al::ActorResourceHolder* resourceHolder, al::AreaObjDirector* areaDir, + al::AudioDirector* audioDir, al::CameraDirector* camDir, + al::ClippingDirector* clippingDir, al::CollisionDirector* collDir, + al::DemoDirector* demoDir, al::EffectSystemInfo* effectSys, + al::ExecuteDirector* executeDir, al::GameDataHolderBase* dataHolder, + al::GravityHolder* gravityHolder, al::HitSensorDirector* hitSensorDir, + al::ItemDirectorBase* itemDir, al::NatureDirector* natureDir, + al::GamePadSystem const* gamepad, al::PadRumbleDirector* padRumbleDir, + al::PlayerHolder* playerHolder, al::SceneObjHolder* sceneObjHolder, + al::SceneMsgCtrl* sceneMsgCtrl, al::SceneStopCtrl* sceneStopCtrl, + al::ScreenCoverCtrl* screenCoverCtrl, al::ScreenPointDirector* screenPointDir, + al::ShadowDirector* shadowDir, al::StageSwitchDirector* stageSwitchDir, + al::ModelGroup* modelGroup, al::GraphicsSystemInfo* gfxSysInfo, + al::ModelDrawBufferCounter* mdlDrawBuffCtr, + al::LiveActorGroup *otherActorGroup); + LiveActorGroup *mLiveActorGroup; // 0x0 + const al::PlacementInfo& mPlacementInfo; // 0x8 + LayoutInitInfo *mLayoutInitInfo; // 0x10 + ActorSceneInfo mActorSceneInfo; // 0x18-0xB0 + LiveActorGroup *mLiveActorGroup2; // 0xB8 + ActorFactory *mActorFactory; // 0xC0 + ActorResourceHolder *mResourceHolder; // 0xC8 + AudioDirector *mAudioDirector; // 0xD0 + EffectSystemInfo *mEffectSysInfo; // 0xD8 + ExecuteDirector *mExecuteDirector; // 0xE0 + HitSensorDirector *mHitSensorDirector; // 0xE8 + ScreenPointDirector *mScreenPointDirector; // 0xF0 + StageSwitchDirector *mStageSwitchDirector; // 0xF8 + ViewIdHolder *mViewIdHolder; // 0x100 + }; +}; + +// size not entirely known, guessing based off of ActorInitInfo::initNew +static_assert(sizeof(al::ActorInitInfo) == 0x108); \ No newline at end of file diff --git a/include/al/actor/ActorSceneInfo.h b/include/al/actor/ActorSceneInfo.h new file mode 100644 index 0000000..3ca0213 --- /dev/null +++ b/include/al/actor/ActorSceneInfo.h @@ -0,0 +1,53 @@ +#pragma once + +#include "al/area/AreaObjDirector.h" +#include "al/camera/CameraDirector.h" +#include "al/collision/CollisionDirector.h" +#include "al/scene/SceneObjHolder.h" +#include "al/layout/LayoutInitInfo.h" +#include "game/GameData/GameDataHolderBase.h" + +namespace al { + + struct ClippingDirector; + struct DemoDirector; + struct GravityHolder; + struct ItemDirectorBase; + struct NatureDirector; + struct SceneMsgCtrl; + struct SceneStopCtrl; + struct ScreenCoverCtrl; + struct ShadowDirector; + struct ModelGroup; + struct GraphicsSystemInfo; + struct PlayerHolder; + struct ModelDrawBufferCounter; + + class ActorSceneInfo + { + public: + ActorSceneInfo() = default; + AreaObjDirector *mAreaObjDirector; // 0x0 + CameraDirector *mCameraDirector; // 0x8 + ClippingDirector *mClippingDirector; // 0x10 + CollisionDirector *mCollisionDirector; // 0x18 + DemoDirector *mDemoDirector; // 0x20 + GameDataHolderBase *mGameDataHolder; // 0x28 + GravityHolder *mGravityHolder; // 0x30 + ItemDirectorBase *mItemDirector; // 0x38 + NatureDirector *mNatureDirector; // 0x40 + GamePadSystem *mGamePadSys; // 0x48 + PadRumbleDirector *mPadRumbleDirector; // 0x50 + PlayerHolder *mPlayerHolder; // 0x58 + SceneObjHolder *mSceneObjHolder; // 0x60 + SceneStopCtrl *mSceneStopCtrl; // 0x68 + SceneMsgCtrl *mSceneMsgCtrl; // 0x70 + ScreenCoverCtrl *mScreenCoverCtrl; // 0x78 + ShadowDirector *mShadowDirector; // 0x80 + ModelGroup *mModelGroup; // 0x88 + GraphicsSystemInfo *mGfxSysInfo; // 0x90 + ModelDrawBufferCounter *mDrawBuffCount; // 0x98 + }; +} // namespace al + +static_assert(sizeof(al::ActorSceneInfo) == 0xA0, "Actor Scene Info Size"); \ No newline at end of file diff --git a/include/al/actor/DemoActor.h b/include/al/actor/DemoActor.h new file mode 100644 index 0000000..d068312 --- /dev/null +++ b/include/al/actor/DemoActor.h @@ -0,0 +1,43 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +namespace al +{ + class DemoActor : public LiveActor { + public: + DemoActor(const char* name); + virtual void paddingFunc(); + virtual void init(ActorInitInfo const &); + virtual void control(void); + virtual void initDemoActor(al::ActorInitInfo const&, al::ActorInitInfo const&, + sead::Matrix34f const*, bool); + virtual void initDemoActorSerial(al::ActorInitInfo const&, al::ActorInitInfo const&, + sead::Matrix34f const*); + virtual void initAfterCreateFromFactory(al::ActorInitInfo const&, al::ActorInitInfo const&, + sead::Matrix34f const*, bool); + virtual void startAction(int); + virtual void resetDynamics(void); + + bool isExistAction(int) const; + const char* getDemoActionName(int) const; + void startActionByName(const char*); + void hideModelBySwitch(void); + void showModelBySwitch(void); + void endDemo(void); + + void exeDelay(void); + void exeAction(void); + + struct DemoActionList *mActList = nullptr; // 0x108 + sead::Matrix34f mPoseMtx = sead::Matrix34f::ident; // 0x110 (size 0x30) + int mActDelay = 0; // 0x140 + int mActionIndex = 0; // 0x144 + int mSubActorNum = 0xFFFFFFFF; // 0x148 + int mShowModelStartFrame = 0xFFFFFFFF; // 0x14C + int mShowModelEndFrame = 0; // 0x150 + bool unkBool1 = false; + bool unkBool2 = false; + struct JointSpringControllerHolder *mJointControllerHolder = nullptr; // 0x158 + }; +} // namespace al diff --git a/include/al/actor/IUseName.h b/include/al/actor/IUseName.h new file mode 100644 index 0000000..655d917 --- /dev/null +++ b/include/al/actor/IUseName.h @@ -0,0 +1,10 @@ +#pragma once + +namespace al +{ + class IUseName + { + public: + virtual const char* getName() const = 0; + }; +}; \ No newline at end of file diff --git a/include/al/actor/LiveActorKit.h b/include/al/actor/LiveActorKit.h new file mode 100644 index 0000000..00609e1 --- /dev/null +++ b/include/al/actor/LiveActorKit.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class LiveActorKit { + public: + + }; +} // namespace al diff --git a/include/al/actor/Placement.h b/include/al/actor/Placement.h new file mode 100644 index 0000000..537185b --- /dev/null +++ b/include/al/actor/Placement.h @@ -0,0 +1,36 @@ +#pragma once + +#include "al/byaml/ByamlIter.h" +#include "sead/prim/seadSafeString.hpp" + +namespace al +{ + class PlacementInfo + { + public: + PlacementInfo(); + + void set(const al::ByamlIter &, const al::ByamlIter &); + + al::ByamlIter _0; + al::ByamlIter mZoneIter; // _10 + }; + + class PlacementId + { + public: + PlacementId(); + PlacementId(const char *unitConifgName, const char *objId, const char *commonId); + + bool init(const al::PlacementInfo &); + bool isEqual(const al::PlacementId &) const; + static bool isEqual(const al::PlacementId &, const al::PlacementId &); + bool isValid() const; + bool makeString(sead::BufferedSafeStringBase *) const; + + const char* _0; + const char* mUnitConfigName; // _8 + const char* mID; // _10 + const char* mCommonID; // _18 + }; +} \ No newline at end of file diff --git a/include/al/actor/SubActorKeeper.h b/include/al/actor/SubActorKeeper.h new file mode 100644 index 0000000..c1e0a80 --- /dev/null +++ b/include/al/actor/SubActorKeeper.h @@ -0,0 +1,16 @@ +#pragma once + + +#include "container/seadPtrArray.h" +namespace al { + struct SubActorInfo { + struct LiveActor* mActor; + void* unkPtr; + int unkInt; + }; + + class SubActorKeeper { + al::LiveActor* mRootActor; + sead::PtrArray mArray; + }; +} \ No newline at end of file diff --git a/include/al/actor/alPlacementFunction.h b/include/al/actor/alPlacementFunction.h new file mode 100644 index 0000000..3e34cf8 --- /dev/null +++ b/include/al/actor/alPlacementFunction.h @@ -0,0 +1,11 @@ +#pragma once + +#include "al/actor/ActorInitInfo.h" + +namespace alPlacementFunction { + void getModelName(char const**,al::ActorInitInfo const&); + void getModelName(char const**,al::PlacementInfo const&); + bool tryGetModelName(char const**,al::PlacementInfo const&); + bool tryGetModelName(char const**, al::ActorInitInfo const&); + +} \ No newline at end of file diff --git a/include/al/actor/misc.h b/include/al/actor/misc.h new file mode 100644 index 0000000..ec0b7fb --- /dev/null +++ b/include/al/actor/misc.h @@ -0,0 +1,11 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +namespace al +{ + template + al::LiveActor *createActorFunction(const char *actorName); + +} // namespace al + diff --git a/include/al/area/AreaObjDirector.h b/include/al/area/AreaObjDirector.h new file mode 100644 index 0000000..233b5e5 --- /dev/null +++ b/include/al/area/AreaObjDirector.h @@ -0,0 +1,12 @@ +#pragma once + +namespace al +{ + class AreaObjDirector; + + class IUseAreaObj + { + public: + virtual al::AreaObjDirector* getAreaObjDirector() const = 0; + }; +}; \ No newline at end of file diff --git a/include/al/area/AreaObjGroup.h b/include/al/area/AreaObjGroup.h new file mode 100644 index 0000000..5070cdb --- /dev/null +++ b/include/al/area/AreaObjGroup.h @@ -0,0 +1,10 @@ +#pragma once + +namespace al +{ + class AreaObjGroup + { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/area/ChangeStageInfo.h b/include/al/area/ChangeStageInfo.h new file mode 100644 index 0000000..a3d7628 --- /dev/null +++ b/include/al/area/ChangeStageInfo.h @@ -0,0 +1,41 @@ +#pragma once + +#include "al/actor/Placement.h" + +#include "game/GameData/GameDataHolder.h" + +#include +#include + +class ChangeStageInfo { + public: + enum SubScenarioType : unsigned int { + UNK, + UNK1, + UNK2, + UNK3, + UNK4 + }; + + ChangeStageInfo(const GameDataHolder *mHolder, const al::PlacementInfo &placementInfo); + ChangeStageInfo(const GameDataHolder *mHolder, const al::PlacementInfo &placementInfo, const char *unk1, const char *unk2, bool unk3, int unk4, SubScenarioType type); + ChangeStageInfo(const GameDataHolder *mHolder, const char *changeId, const char *stageName, bool isReturn, int scenarioNo, SubScenarioType type); + void init(const al::PlacementInfo &placement, const GameDataHolder *mHolder); + void init(void); + void copy(const ChangeStageInfo &other); + inline void findScenarioNoByList(const GameDataHolder *holder); + bool isSubScenarioTypeLifeRecover(void) const; + bool isSubScenarioTypeResetMiniGame(void) const; + void setWipeType(const char *type); + void calcTrans(sead::Vector3f *result, const al::PlacementInfo &info); + + sead::FixedSafeString<0x80> changeStageId; // 0x0 (Size: 0x98) + sead::FixedSafeString<0x80> changeStageName; // 0xA0 + sead::FixedSafeString<0x80> placementString; // 0x138 + bool isReturn; // 0x1C8 + int scenarioNo; // 0x1CC or 0x134 + SubScenarioType subType; // 0x1D0 + sead::FixedSafeString<0x80> wipeType; // 0x1D8 + int hintPriority; // 0x270 + +}; \ No newline at end of file diff --git a/include/al/async/AsyncFunctorThread.h b/include/al/async/AsyncFunctorThread.h new file mode 100644 index 0000000..29eac62 --- /dev/null +++ b/include/al/async/AsyncFunctorThread.h @@ -0,0 +1,28 @@ +#pragma once + +#include "types.h" +#include "sead/prim/seadSafeString.hpp" +#include "FunctorBase.h" +#include "sead/thread/seadDelegateThread.h" +#include "sead/thread/seadMessageQueue.h" +#include "sead/mc/seadCoreInfo.h" + +namespace al +{ + class AsyncFunctorThread { + public: + AsyncFunctorThread(sead::SafeStringBase const &functorName, al::FunctorBase const &functor, int blockType, int stackSize, sead::CoreId id); + // this function is whats passed into the delegate thread as the function to call when the thread becomes unblocked + void threadFunction(sead::Thread *, s64); // unused args + + bool isDone() const {return this->mIsDone;}; + void start(); + // private: + unsigned char padding_08[0x8]; + sead::DelegateThread *mDelegateThread; + al::FunctorBase functor; + bool mIsDone; + }; +} // namespace al + +static_assert(sizeof(al::AsyncFunctorThread) == 0x20); diff --git a/include/al/async/FunctorBase.h b/include/al/async/FunctorBase.h new file mode 100644 index 0000000..8f835a7 --- /dev/null +++ b/include/al/async/FunctorBase.h @@ -0,0 +1,13 @@ +#pragma once + +namespace al +{ + + class FunctorBase + { + public: + virtual void operator()(void) const {return;}; + virtual FunctorBase *clone(void) const {return {0};}; + virtual ~FunctorBase() {return;}; + }; +} // namespace al diff --git a/include/al/async/FunctorV0M.hpp b/include/al/async/FunctorV0M.hpp new file mode 100644 index 0000000..ad6a9bb --- /dev/null +++ b/include/al/async/FunctorV0M.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "FunctorBase.h" +#include "sead/basis/seadNew.h" +#include "types.h" +#include "nn.h" + +namespace sead +{ + namespace system + { + void DeleteImpl(void* ptr); + } // namespace system +} // namespace sead + +namespace al +{ + template + class FunctorV0M : public al::FunctorBase { + + public: + inline FunctorV0M(T objPointer, F functPointer) : mFunctor(functPointer), mObjPointer(objPointer) { }; + + void operator()(void) const override { + (mObjPointer->*mFunctor)(); + }; + + FunctorV0M *clone(void) const override { + return new FunctorV0M(mObjPointer, mFunctor); + }; + + ~FunctorV0M() override { + sead::system::DeleteImpl(this); + }; + + protected: + // 0x0 = vtable + T mObjPointer; // 0x8 = object pointer + F mFunctor; // 0x10 = member function pointer + }; +} // namespace al diff --git a/include/al/audio/AudioDirector.h b/include/al/audio/AudioDirector.h new file mode 100644 index 0000000..4e93365 --- /dev/null +++ b/include/al/audio/AudioDirector.h @@ -0,0 +1,7 @@ +#pragma once + +namespace al +{ + class AudioDirector; + +}; \ No newline at end of file diff --git a/include/al/audio/AudioKeeper.h b/include/al/audio/AudioKeeper.h new file mode 100644 index 0000000..fec6f51 --- /dev/null +++ b/include/al/audio/AudioKeeper.h @@ -0,0 +1,18 @@ +#pragma once + +namespace al +{ + class AudioKeeper; + + class IUseAudioKeeper + { + public: + virtual al::AudioKeeper* getAudioKeeper() const = 0; + }; + + class AudioKeeper + { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/byaml/ByamlContainerHeader.h b/include/al/byaml/ByamlContainerHeader.h new file mode 100644 index 0000000..959e115 --- /dev/null +++ b/include/al/byaml/ByamlContainerHeader.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace al { +class ByamlContainerHeader { +public: + int getType() const; + int getCount(bool) const; + + u32 mType; // _0 +}; +}; // namespace al \ No newline at end of file diff --git a/include/al/byaml/ByamlData.h b/include/al/byaml/ByamlData.h new file mode 100644 index 0000000..ae5c8b4 --- /dev/null +++ b/include/al/byaml/ByamlData.h @@ -0,0 +1,34 @@ +#pragma once + +#include "al/byaml/ByamlHashPair.h" + +namespace al { +enum DataType : unsigned char { + TYPE_STRING = 0xA0, + TYPE_BINARY = 0xA1, + TYPE_ARRAY = 0xC0, + TYPE_HASH = 0xC1, + TYPE_STRING_TABLE = 0xC2, + TYPE_BOOL = 0xD0, + TYPE_INT = 0xD1, + TYPE_FLOAT = 0xD2, + TYPE_UINT = 0xD3, + TYPE_LONG = 0xD4, + TYPE_ULONG = 0xD5, + TYPE_DOUBLE = 0xD6, + TYPE_NULL = 0xFF +}; + +class ByamlData { +public: + ByamlData(); + + void set(const ByamlHashPair*, bool); + void set(unsigned char, unsigned int, bool); + unsigned char getType() const; + unsigned int getValue() const; + + unsigned int mValue; // _0 + unsigned char mType; // _4 +}; +}; // namespace al \ No newline at end of file diff --git a/include/al/byaml/ByamlHashPair.h b/include/al/byaml/ByamlHashPair.h new file mode 100644 index 0000000..32144dd --- /dev/null +++ b/include/al/byaml/ByamlHashPair.h @@ -0,0 +1,19 @@ +#pragma once + +namespace al { +class ByamlHashPair { +public: + int getKey(bool) const; + char getType() const; + int getValue(bool) const; + + union { + const int mData; + struct { + const char _0, _1, _2, mType; + }; + }; + + const int mValue; +}; +}; // namespace al \ No newline at end of file diff --git a/include/al/byaml/ByamlHeader.h b/include/al/byaml/ByamlHeader.h new file mode 100644 index 0000000..609b53b --- /dev/null +++ b/include/al/byaml/ByamlHeader.h @@ -0,0 +1,22 @@ +#pragma once + +namespace al { +class ByamlHeader { +public: + short getTag() const; + bool isInvertOrder() const; + short getVersion() const; + int getHashKeyTableOffset() const; + int getStringTableOffset() const; + int getDataOffset() const; + + union { + int _0; + unsigned short mTag, mVersion; + }; + + int mHashKeyOffset; // _4 + int mStringTableOffset; // _8 + int mDataOffset; // _C +}; +}; // namespace al \ No newline at end of file diff --git a/include/al/byaml/ByamlIter.h b/include/al/byaml/ByamlIter.h new file mode 100644 index 0000000..c53da78 --- /dev/null +++ b/include/al/byaml/ByamlIter.h @@ -0,0 +1,63 @@ +#pragma once + +#include "al/byaml/ByamlData.h" + +namespace al { +class ByamlIter { +public: + ByamlIter(); + ByamlIter(const char*); + ByamlIter(const char*, const char*); + + bool isValid() const; + bool isTypeHash() const; + bool isTypeArray() const; + bool isTypeContainer() const; + bool isExistKey(char const* key) const; + int getKeyIndex(char const* key) const; + bool isInvertOrder() const; + unsigned int getSize() const; + al::ByamlIter* getIterByIndex(int index) const; + bool getByamlDataByIndex(al::ByamlData*, int index) const; + al::ByamlIter* getIterByKey(char const*) const; + bool getByamlDataByKey(al::ByamlData*, char const*) const; + bool getByamlDataByKeyIndex(al::ByamlData*, int) const; + bool getByamlDataAndKeyName(al::ByamlData*, char const**, int) const; + bool getKeyName(char const**, int) const; + bool tryGetIterByIndex(al::ByamlIter*, int) const; + bool tryGetIterAndKeyNameByIndex(al::ByamlIter*, char const**, int) const; + bool tryGetIterByKey(al::ByamlIter*, char const*) const; + bool tryGetStringByKey(char const**, char const*) const; + bool tryConvertString(char const**, al::ByamlData const*) const; + bool tryGetBinaryByKey(char const**, int*, char const*) const; + bool tryGetBinary(char const**, int*, al::ByamlData const*) const; + bool tryGetBoolByKey(bool*, char const*) const; + bool tryConvertBool(bool*, al::ByamlData const*) const; + bool tryGetIntByKey(int*, char const*) const; + bool tryConvertInt32(int*, al::ByamlData const*) const; + bool tryGetUInt32ByKey(unsigned int*, char const*) const; + bool tryConvertUInt32(unsigned int*, al::ByamlData const*) const; + bool tryGetFloatByKey(float*, char const*) const; + bool tryConvertFloat(float*, al::ByamlData const*) const; + bool tryGetInt64ByKey(long*, char const*) const; + bool tryConvertInt64(long*, al::ByamlData const*) const; + bool tryGetUInt64ByKey(unsigned long*, char const*) const; + bool tryConvertUInt64(unsigned long*, al::ByamlData const*) const; + bool tryGetDoubleByKey(double*, char const*) const; + bool tryConvertDouble(double*, al::ByamlData const*) const; + bool tryGetStringByIndex(char const**, int) const; + bool tryGetBinaryByIndex(char const**, int*, int) const; + bool tryGetBoolByIndex(bool*, int) const; + bool tryGetInt32ByIndex(int*, int) const; + bool tryGetUInt32ByIndex(unsigned int*, int) const; + bool tryGetFloatByindex(float*, int) const; + bool tryGetInt64ByIndex(long*, int) const; + bool tryGetUInt64ByIndex(unsigned long*, int) const; + bool tryGetDoubleByIndex(double*, int) const; + bool tryConvertIter(al::ByamlIter*, al::ByamlData const*) const; + bool isEqualData(al::ByamlIter const&) const; + + unsigned char* mData; // _0 + unsigned long mDataOffset; // _8 +}; +}; // namespace al \ No newline at end of file diff --git a/include/al/byaml/ByamlStringTableIter.h b/include/al/byaml/ByamlStringTableIter.h new file mode 100644 index 0000000..d1472db --- /dev/null +++ b/include/al/byaml/ByamlStringTableIter.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace al { +class ByamlStringTableIter { +public: + ByamlStringTableIter(); + ByamlStringTableIter(const unsigned char*, bool); + + int getSize() const; + const u32* getAddressTable() const; + int getStringAddress(int) const; + int getEndAddress() const; + const char* getString(int) const; + int getStringSize(int) const; + int findStringIndex(const char*) const; + bool isValidate() const; + + const u8* mData; // _0 + bool mReversed; +}; +}; // namespace al \ No newline at end of file diff --git a/include/al/byaml/writer/ByamlWriter.h b/include/al/byaml/writer/ByamlWriter.h new file mode 100644 index 0000000..1dec688 --- /dev/null +++ b/include/al/byaml/writer/ByamlWriter.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +namespace sead { +class Heap; +class WriteStream; +} // namespace sead + +namespace al { + +class ByamlWriterStringTable; +class ByamlWriterContainer; +class ByamlWriterBigDataList; + +class ByamlWriterArray; +class ByamlWriterHash; + +class ByamlIter; + +class ByamlWriter { +public: + ByamlWriter(sead::Heap*, bool); + virtual ~ByamlWriter(); + + void addBool(bool); + void addInt(int); + void addUInt(u32); + void addFloat(float); + void addInt64(long); + void addUInt64(u64); + void addDouble(double); + void addString(const char*); + void addNull(); + void addBool(const char*, bool); + void addInt(const char*, int); + void addUInt(const char*, u32); + void addFloat(const char*, float); + void addInt64(const char*, long); + void addUInt64(const char*, u64); + void addDouble(const char*, double); + void addString(const char*, char const*); + void addNull(const char*); + + ByamlWriterArray* getArrayCurrentContainer(); + ByamlWriterHash* getHashCurrentContainer(); + ByamlWriterContainer* getCurrentContainer(); + void pushHash(); + void pushContainer(ByamlWriterContainer*); + void pushArray(); + void pushArray(char const*); + void pushHash(char const*); + void pushIter(const ByamlIter&); + void pushIter(const char*, const ByamlIter&); + void pushLocalIter(const ByamlIter&, const char*); + void pop(); + u32 calcHeaderSize() const; + u32 calcPackSize() const; + void write(sead::WriteStream*); + void print() const; + +private: + sead::Heap* mHeap; + ByamlWriterStringTable* mStringTable1 = nullptr; + ByamlWriterStringTable* mStringTable2 = nullptr; + sead::TList mContainerList; + ByamlWriterBigDataList* mBigDataList = nullptr; + ByamlWriterContainer** mContainerStack = nullptr; + int mContainerStackSize = 64; + int mCurrentContainerIndex = -1; + bool _mAlwaysFalse; +}; + +} // namespace al diff --git a/include/al/byaml/writer/ByamlWriterBigDataList.h b/include/al/byaml/writer/ByamlWriterBigDataList.h new file mode 100644 index 0000000..72af3e4 --- /dev/null +++ b/include/al/byaml/writer/ByamlWriterBigDataList.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace sead { +class WriteStream; +} + +namespace al { + +class ByamlWriterBigData; + +class ByamlWriterBigDataList { +public: + ByamlWriterBigDataList(); + virtual ~ByamlWriterBigDataList(); + u32 calcPackSize() const; + void addData(al::ByamlWriterBigData*); + int setOffset(int); + void write(sead::WriteStream*); + +private: + sead::TList mList; +}; + +} // namespace al diff --git a/include/al/byaml/writer/ByamlWriterData.h b/include/al/byaml/writer/ByamlWriterData.h new file mode 100644 index 0000000..b49dd82 --- /dev/null +++ b/include/al/byaml/writer/ByamlWriterData.h @@ -0,0 +1,268 @@ +#pragma once + +#include +#include + +namespace sead { +class WriteStream; +} + +namespace al { + +class ByamlWriterData { +public: + virtual ~ByamlWriterData(); + virtual void makeIndex(); + virtual u32 calcPackSize() const; + virtual u8 getTypeCode() const; + virtual bool isContainer() const; + virtual void write(sead::WriteStream*) const; + virtual void print(int) const; + void printIndent(int) const; +}; + +class ByamlWriterBool : public ByamlWriterData { +public: + ByamlWriterBool(bool); + u8 getTypeCode() const override; + void write(sead::WriteStream*) const override; + void print(int) const override; + +private: + bool mValue; +}; + +class ByamlWriterInt : public ByamlWriterData { +public: + ByamlWriterInt(int); + u8 getTypeCode() const override; + void write(sead::WriteStream*) const override; + void print(int) const override; + +private: + int mValue; +}; + +class ByamlWriterFloat : public ByamlWriterData { +public: + ByamlWriterFloat(float); + u8 getTypeCode() const override; + void write(sead::WriteStream*) const override; + void print(int) const override; + +private: + float mValue; +}; + +class ByamlWriterUInt : public ByamlWriterData { +public: + ByamlWriterUInt(u32); + u8 getTypeCode() const override; + void write(sead::WriteStream*) const override; + void print(int) const override; + +private: + u32 mValue; +}; + +class ByamlWriterNull : public ByamlWriterData { +public: + ByamlWriterNull(); + u8 getTypeCode() const override; + void write(sead::WriteStream*) const override; + void print(int) const override; +}; + +class ByamlWriterStringTable; +class ByamlWriterString : public ByamlWriterData { +public: + ByamlWriterString(const char*, ByamlWriterStringTable*); + u8 getTypeCode() const override; + void write(sead::WriteStream*) const override; + void print(int) const override; + +private: + const char* mString; + ByamlWriterStringTable* mStringTable; +}; + +class ByamlWriterBigDataList; + +class ByamlWriterBigData : public ByamlWriterData { +public: + ByamlWriterBigData(al::ByamlWriterBigDataList*); + ~ByamlWriterBigData(); + void write(sead::WriteStream*) const override; + virtual u32 calcBigDataSize() const; + virtual void writeBigData(sead::WriteStream*) const; + + void setOffset(int offset) { mOffset = offset; } + +private: + al::ByamlWriterBigDataList* mList; + int mOffset = 0; +}; + +class ByamlWriterInt64 : public ByamlWriterBigData { +public: + ByamlWriterInt64(long, ByamlWriterBigDataList*); + ~ByamlWriterInt64(); + u8 getTypeCode() const override; + void writeBigData(sead::WriteStream*) const override; + void print(int) const override; + +private: + long mValue; +}; + +class ByamlWriterUInt64 : public ByamlWriterBigData { +public: + ByamlWriterUInt64(u64, ByamlWriterBigDataList*); + ~ByamlWriterUInt64(); + u8 getTypeCode() const override; + void writeBigData(sead::WriteStream*) const override; + void print(int) const override; + +private: + u64 mValue; +}; + +class ByamlWriterDouble : public ByamlWriterBigData { +public: + ByamlWriterDouble(double, ByamlWriterBigDataList*); + ~ByamlWriterDouble(); + u8 getTypeCode() const override; + void writeBigData(sead::WriteStream*) const override; + void print(int) const override; + +private: + double mValue; +}; + +class ByamlWriterHash; +class ByamlWriterArray; +class ByamlWriterStringTable; + +class ByamlWriterContainer : public ByamlWriterData { +public: + bool isContainer() const override; + + virtual void addBool(const char*, bool); + virtual void addInt(const char*, s32); + virtual void addUInt(const char*, u32); + virtual void addFloat(const char*, float); + virtual void addInt64(const char*, s64, ByamlWriterBigDataList*); + virtual void addUInt64(const char*, u64, ByamlWriterBigDataList*); + virtual void addDouble(const char*, double, ByamlWriterBigDataList*); + virtual void addString(const char*, const char*); + virtual void addHash(const char*, ByamlWriterHash*); + virtual void addArray(const char*, ByamlWriterArray*); + virtual void addNull(const char*); + + virtual void addBool(bool); + virtual void addInt(s32); + virtual void addUInt(u32); + virtual void addFloat(float); + virtual void addInt64(s64, ByamlWriterBigDataList*); + virtual void addUInt64(u64, ByamlWriterBigDataList*); + virtual void addDouble(double, ByamlWriterBigDataList*); + virtual void addString(const char*); + virtual void addHash(ByamlWriterHash*); + virtual void addArray(ByamlWriterArray*); + virtual void addNull(); + + virtual void writeContainer(sead::WriteStream*) const; + virtual bool isHash() const; + virtual bool isArray() const; + virtual void deleteData(); + + int getOffset() const { return mOffset; } + void setOffset(int offset) { mOffset = offset; } + +private: + int mOffset = 0; // FIXME shouldn't be public +}; + +class ByamlWriterArray : public ByamlWriterContainer { +public: + ByamlWriterArray(ByamlWriterStringTable*); + ~ByamlWriterArray(); + + void deleteData() override; + u32 calcPackSize() const override; + + void addData(al::ByamlWriterData*); + void addBool(bool) override; + void addInt(s32) override; + void addUInt(u32) override; + void addFloat(float) override; + void addInt64(s64, ByamlWriterBigDataList*) override; + void addUInt64(u64, ByamlWriterBigDataList*) override; + void addDouble(double, ByamlWriterBigDataList*) override; + void addString(const char*) override; + void addHash(ByamlWriterHash*) override; + void addArray(ByamlWriterArray*) override; + void addNull() override; + + u8 getTypeCode() const override; + void writeContainer(sead::WriteStream*) const override; + void write(sead::WriteStream*) const override; + void print(int) const override; + bool isArray() const override; + +private: + sead::TList mList; + al::ByamlWriterStringTable* mStringTable; +}; +static_assert(sizeof(ByamlWriterArray) == 0x30); + +class ByamlWriterHashPair : public sead::ListNode { +public: + ByamlWriterHashPair(const char*, ByamlWriterData*); + + const char* getKey() { return mKey; } + al::ByamlWriterData* getValue() { return mValue; } + +private: + void* selfReference = this; + void* test2 = nullptr; + const char* mKey; + al::ByamlWriterData* mValue; +}; +static_assert(sizeof(ByamlWriterHashPair) == 0x30); + +class ByamlWriterHash : public ByamlWriterContainer { +public: + ByamlWriterHash(ByamlWriterStringTable*, ByamlWriterStringTable*); + ~ByamlWriterHash(); + + void deleteData() override; // TODO implementation missing + u32 calcPackSize() const override; + + void addData(const char*, al::ByamlWriterData*); // TODO implementation missing + void addBool(const char*, bool) override; + void addInt(const char*, s32) override; + void addUInt(const char*, u32) override; + void addFloat(const char*, float) override; + void addInt64(const char*, s64, ByamlWriterBigDataList*) override; + void addUInt64(const char*, u64, ByamlWriterBigDataList*) override; + void addDouble(const char*, double, ByamlWriterBigDataList*) override; + void addString(const char*, const char*) override; + void addHash(const char*, ByamlWriterHash*) override; + void addArray(const char*, ByamlWriterArray*) override; + void addNull(const char*) override; + + u8 getTypeCode() const override; + void writeContainer(sead::WriteStream*) const override; // TODO implementation missing + void write(sead::WriteStream*) const override; + void print(int) const override; // TODO implementation missing + bool isHash() const override; + +private: + sead::TList mList; // TODO not really... it's something different here. + al::ByamlWriterStringTable* mStringTable1; + al::ByamlWriterStringTable* mStringTable2; +}; +static_assert(sizeof(ByamlWriterHash) == 0x38); + +} // namespace al diff --git a/include/al/byaml/writer/ByamlWriterStringTable.h b/include/al/byaml/writer/ByamlWriterStringTable.h new file mode 100644 index 0000000..bdf8c9d --- /dev/null +++ b/include/al/byaml/writer/ByamlWriterStringTable.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace sead { +class WriteStream; +} + +namespace al { + +class ByamlWriterStringTable { +public: + ByamlWriterStringTable(); + virtual ~ByamlWriterStringTable(); + const char* tryAdd(const char*); + u32 calcHeaderSize() const; + u32 calcContentSize() const; + u32 calcPackSize() const; + bool isEmpty() const; + u32 calcIndex(const char*) const; + void write(sead::WriteStream*) const; + void print() const; + +private: + sead::TList mList; +}; + +} // namespace al diff --git a/include/al/camera/CameraAngleCtrlInfo.h b/include/al/camera/CameraAngleCtrlInfo.h new file mode 100644 index 0000000..1b96e5f --- /dev/null +++ b/include/al/camera/CameraAngleCtrlInfo.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class CameraAngleCtrlInfo { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/camera/CameraAngleSwingInfo.h b/include/al/camera/CameraAngleSwingInfo.h new file mode 100644 index 0000000..a636f35 --- /dev/null +++ b/include/al/camera/CameraAngleSwingInfo.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class CameraAngleSwingInfo { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/camera/CameraArrowCollider.h b/include/al/camera/CameraArrowCollider.h new file mode 100644 index 0000000..8281a70 --- /dev/null +++ b/include/al/camera/CameraArrowCollider.h @@ -0,0 +1,11 @@ +#pragma once + +#include "sead/math/seadVector.h" + +namespace al +{ + class CameraArrowCollider { + public: + void update(sead::Vector3f const &,sead::Vector3f const &,sead::Vector3f const &); + }; +}; \ No newline at end of file diff --git a/include/al/camera/CameraDirector.h b/include/al/camera/CameraDirector.h new file mode 100644 index 0000000..c899f85 --- /dev/null +++ b/include/al/camera/CameraDirector.h @@ -0,0 +1,66 @@ +#pragma once + +#include "CameraPoseUpdater.h" +#include "CameraPoserSceneInfo.h" +#include "al/camera/CameraPoser.h" +#include "al/camera/CameraTicket.h" +#include "al/hio/HioNode.h" +#include "al/execute/IUseExecutor.h" + +namespace al { + + class CameraResourceHolder; + class CameraRailHolder; + class CameraPoserFactory; + class PlayerHolder; + class ICameraInput; + class NameToCameraParamTransferFunc; + + class CameraDirector : public al::HioNode, public al::IUseExecutor { + public: + + void init(al::CameraPoserSceneInfo *,al::CameraPoserFactory const*); + al::CameraPoseUpdater *getPoseUpdater(int) const; + void endInit(al::PlayerHolder const*); + al::CameraTicket *createCameraFromFactory(char const *creatorName, al::PlacementId const*,char const*, int priority, sead::Matrix34f const&); + void createCamera(al::CameraPoser *,al::PlacementId const*,char const*,int,sead::Matrix34f const&,bool); + void execute(void) override; + void update(void); + void createObjectCamera(al::PlacementId const*,char const*,char const*,int,sead::Matrix34f const&); + void createObjectEntranceCamera(al::PlacementId const*,char const*,sead::Matrix34f const&); + void createMirrorObjectCamera(al::PlacementId const*,char const*,int,sead::Matrix34f const&); + void initAreaCameraSwitcherMultiForPrototype(al::AreaObjDirector *); + al::ICameraInput *getCameraInput(int); + void setCameraInput(al::ICameraInput const*); + void setViewCameraInput(al::ICameraInput const*,int); + void initAreaCameraSwitcherSingle(void); + void initResourceHolder(al::CameraResourceHolder const*); + void registerCameraRailHolder(al::CameraRailHolder *); + void initSceneFovyDegree(float); + void setCameraParamTransferFuncTable(al::NameToCameraParamTransferFunc const*,int); + void initSettingCloudSea(float); + void initSnapShotCameraAudioKeeper(al::IUseAudioKeeper *); + void initAndCreatePauseCameraCtrl(float); + float getSceneFovyDegree(void) const; + void validateCameraArea2D(void); + void invalidateCameraArea2D(void); + void stopByDeathPlayer(void); + void restartByDeathPlayer(void); + void startInvalidStopJudgeByDemo(void); + void endInvalidStopJudgeByDemo(void); + void startSnapShotMode(bool); + void endSnapShotMode(void); + + unsigned char padding[0x20]; + al::CameraPoserFactory *mFactory; // 0x28 + al::CameraPoserSceneInfo *mSceneInfo; // 0x30 + // 0xBC float farClipDistance + // 0xB8 float nearClipDistance + }; + + class IUseCamera + { + public: + virtual al::CameraDirector* getCameraDirector() const = 0; + }; +}; \ No newline at end of file diff --git a/include/al/camera/CameraObjectRequestInfo.h b/include/al/camera/CameraObjectRequestInfo.h new file mode 100644 index 0000000..106f510 --- /dev/null +++ b/include/al/camera/CameraObjectRequestInfo.h @@ -0,0 +1,7 @@ +#pragma once + +namespace al { + class CameraObjectRequestInfo { + + }; +} \ No newline at end of file diff --git a/include/al/camera/CameraPoseUpdater.h b/include/al/camera/CameraPoseUpdater.h new file mode 100644 index 0000000..ec5d652 --- /dev/null +++ b/include/al/camera/CameraPoseUpdater.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class CameraPoseUpdater { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/camera/CameraPoser.h b/include/al/camera/CameraPoser.h new file mode 100644 index 0000000..1985c74 --- /dev/null +++ b/include/al/camera/CameraPoser.h @@ -0,0 +1,132 @@ +#pragma once + +#include "al/actor/Placement.h" +#include "al/byaml/ByamlIter.h" +#include "al/hio/HioNode.h" +#include "al/area/AreaObjDirector.h" +#include "al/audio/AudioKeeper.h" +#include "al/collision/CollisionDirector.h" +#include "al/actor/IUseName.h" +#include "al/nerve/Nerve.h" +#include "al/nerve/NerveKeeper.h" +#include "al/rail/RailKeeper.h" + +#include "CameraPoserFlag.h" +#include "CameraStartInfo.h" +#include "CameraTurnInfo.h" +#include "CameraObjectRequestInfo.h" +#include "al/rail/RailRider.h" + +#include "sead/math/seadQuat.h" +#include "sead/gfx/seadCamera.h" +#include "sead/math/seadVector.h" +#include "sead/math/seadMatrix.h" + +// size is 0x140/320 bytes +namespace al { + + class CameraVerticalAbsorber; + class CameraAngleCtrlInfo; + class CameraAngleSwingInfo; + class CameraArrowCollider; + class CameraOffsetCtrlPreset; + class CameraParamMoveLimit; + class GyroCameraCtrl; + class SnapShotCameraCtrl; + class CameraViewInfo; + + class CameraPoser : public al::HioNode, public al::IUseAreaObj, public al::IUseAudioKeeper, public al::IUseCollision, public al::IUseName, public al::IUseNerve, public al::IUseRail { + public: + CameraPoser(char const* poserName); + + virtual AreaObjDirector* getAreaObjDirector() const override; + virtual void init(); + virtual void initByPlacementObj(al::PlacementInfo const&); + virtual void endInit(); + virtual void start(CameraStartInfo const &); + virtual void update(); + virtual void end(); + virtual void loadParam(ByamlIter const &); + virtual void makeLookAtCamera(sead::LookAtCamera*) const; + virtual void receiveRequestFromObject(CameraObjectRequestInfo const&); + virtual bool isZooming(void) const; + virtual bool isEnableRotateByPad(void) const; + virtual void startSnapShotMode(void); + virtual void endSnapShotMode(void); + + virtual const char *getName(void) const override; + virtual CollisionDirector *getCollisionDirector(void) const override; + virtual NerveKeeper *getNerveKeeper(void) const override; + virtual AudioKeeper *getAudioKeeper(void) const override; + virtual RailRider* getRailRider(void) const override; + + virtual void load(ByamlIter const&); + virtual void movement(void); + virtual void calcCameraPose(sead::LookAtCamera *) const; + virtual void requestTurnToDirection(al::CameraTurnInfo const *); + + bool isInterpoleByCameraDistance(void) const; + bool isInterpoleEaseOut(void) const; + bool isEndInterpoleByStep(void) const; + bool isFirstCalc(void) const; + + void initNerve(al::Nerve const*,int); + void initArrowCollider(al::CameraArrowCollider *); + void initAudioKeeper(char const*); + void initRail(al::PlacementInfo const&); + void initLocalInterpole(void); + void initLookAtInterpole(float); + void initOrthoProjectionParam(void); + void tryInitAreaLimitter(al::PlacementInfo const&); + + void makeLookAtCameraPrev(sead::LookAtCamera *) const; + void makeLookAtCameraPost(sead::LookAtCamera *) const; + void makeLookAtCameraLast(sead::LookAtCamera *) const; + void makeLookAtCameraCollide(sead::LookAtCamera*) const; + + void getInterpoleStep(void); + void setInterpoleStep(int); + void resetInterpoleStep(void); + void setInterpoleEaseOut(void); + void getEndInterpoleStep(void); + + void appear(al::CameraStartInfo const&); + void calcCameraPose(sead::LookAtCamera *); + void receiveRequestFromObjectCore(al::CameraObjectRequestInfo const&); + + void startSnapShotModeCore(void); + void endSnapShotModeCore(void); + + const char *mPoserName; // 0x30 + float unkFloat1; // 0x38 + sead::Vector3f mPosition; // 0x3C + sead::Vector3f mTargetTrans = sead::Vector3f::ex; // 0x48 + sead::Vector3f mCameraUp = sead::Vector3f::ey; // 0x54 + float mFovyDegree = 35.0f; // 0x60 + float unkFloat; // 0x64 + sead::Matrix34f mViewMtx = sead::Matrix34f::ident; // 0x68 + bool unkBool1 = false; // 0x98 + CameraViewInfo *mViewInfo; // 0xA0 + al::AreaObjDirector *mAreaDirector; // 0xA8 + CameraPoserFlag *mPoserFlags; // 0xB0 + CameraVerticalAbsorber *mVerticalAbsorber; // 0xB8 + CameraAngleCtrlInfo *mAngleCtrlInfo; // 0xC0 + CameraAngleSwingInfo *mAngleSwingInfo; // 0xC8 + CameraArrowCollider *mArrowCollider; // 0xD0 + CameraOffsetCtrlPreset *mOffsetCtrlPreset; // 0xD8 + float *mLocalInterpole; // 0xE0 (size = 0x20) + float *mLookAtInterpole; // 0xE8 (size = 0x10) + CameraParamMoveLimit *mParamMoveLimit; // 0xF0 + void *unkPtr4; // 0xF8 + GyroCameraCtrl *mGyroCtrl; // 0x100 + SnapShotCameraCtrl *mSnapshotCtrl; // 0x108 + AudioKeeper *mAudioKeeper; // 0x110 + NerveKeeper *mNerveKeeper; // 0x118 + RailKeeper *mRailKeeper; // 0x120 + int *unkPtr5; // 0x128 (size = 0xC) interpolesteptype? + int *unkPtr6; // 0x130 (size - 0x8) + sead::Vector3f *mOrthoProjectionParam; // 0x138 (gets init'd with new of size 0xC) + }; + + static_assert(sizeof(CameraPoser) == 0x140, "Camera Poser Size"); +}; diff --git a/include/al/camera/CameraPoserFlag.h b/include/al/camera/CameraPoserFlag.h new file mode 100644 index 0000000..47e9214 --- /dev/null +++ b/include/al/camera/CameraPoserFlag.h @@ -0,0 +1,25 @@ +#pragma once + +namespace al +{ + class CameraPoserFlag { + public: + CameraPoserFlag(void); + bool mFirstCalc = true; + bool mOffVerticalAbsorb = true; + bool mInvalidCollider = false; + bool unkBool4 = false; + bool mValidKeepPreSelfPoseNextCameraByParam = false; + bool unkBool6 = false; + bool mInvalidKeepPreSelfPoseNextCameraOverWriteProgram = false; + bool mInvalidKeepDistanceNextCamera = false; + bool unkBool9 = false; + bool unkBool10 = false; + bool mInvalidChangeSubjective = false; + bool unkBool12 = false; + bool unkBool13 = false; + bool mInvalidPreCameraEndAfterInterpole = false; + bool mStopUpdateGyro = false; // 0xE + // might be better to use a bool array? + }; +}; \ No newline at end of file diff --git a/include/al/camera/CameraPoserFollowLimit.h b/include/al/camera/CameraPoserFollowLimit.h new file mode 100644 index 0000000..e0f9d5e --- /dev/null +++ b/include/al/camera/CameraPoserFollowLimit.h @@ -0,0 +1,9 @@ +#pragma once + + +class CameraPoserFollowLimit { + public: + unsigned char massive[0x200]; + + //sead::Vector3f lookAtPos; // 0x1F4 +}; \ No newline at end of file diff --git a/include/al/camera/CameraPoserSceneInfo.h b/include/al/camera/CameraPoserSceneInfo.h new file mode 100644 index 0000000..0def809 --- /dev/null +++ b/include/al/camera/CameraPoserSceneInfo.h @@ -0,0 +1,7 @@ +#pragma once + +namespace al { +struct CameraPoserSceneInfo { + float mSceneFovyDegree; +}; +} \ No newline at end of file diff --git a/include/al/camera/CameraStartInfo.h b/include/al/camera/CameraStartInfo.h new file mode 100644 index 0000000..24e89f6 --- /dev/null +++ b/include/al/camera/CameraStartInfo.h @@ -0,0 +1,7 @@ +#pragma once + +namespace al { + class CameraStartInfo { + + }; +} \ No newline at end of file diff --git a/include/al/camera/CameraTargetBase.h b/include/al/camera/CameraTargetBase.h new file mode 100644 index 0000000..46357c3 --- /dev/null +++ b/include/al/camera/CameraTargetBase.h @@ -0,0 +1,31 @@ +#pragma once + +#include "sead/math/seadVector.h" + +namespace al { + + struct CameraSubTargetBase; + + class CameraTargetBase { + public: + CameraTargetBase(); + + virtual void calcSide(sead::Vector3f *) const {return;}; + virtual void calcUp(sead::Vector3f *) const {return;}; + virtual void calcFront(sead::Vector3f *) const {return;}; + virtual void calcGravity(sead::Vector3f *input) const {input->x = -1.f; input->y = 0.f; input->z = 0.f; return;}; + virtual void calcVelocity(sead::Vector3f *) const {return;}; + virtual bool isCollideGround(void) const {return false;}; + virtual bool isInWater(void) const {return false;}; + virtual bool isInMooonGravity(void) const {return false;}; + virtual bool isClimbPole(void) const {return false;}; + virtual bool isGrabCeil(void) const {return false;}; + virtual bool isWallWatch(void) const {return false;}; + virtual bool isInvalidMoveByInput(void) const {return false;}; + virtual bool isEnableEndAfterInterpole(void) const {return false;}; + virtual void update(void) const {return;}; + virtual float getRequestDistance(void) const {return -1.f;}; + + bool isActiveTarget; // 0x8 + }; +} \ No newline at end of file diff --git a/include/al/camera/CameraTargetHolder.h b/include/al/camera/CameraTargetHolder.h new file mode 100644 index 0000000..ad1a5ca --- /dev/null +++ b/include/al/camera/CameraTargetHolder.h @@ -0,0 +1,10 @@ +#pragma once + +#include "al/actor/ActorCameraTarget.h" + +namespace al { + class CameraTargetHolder { + public: + al::ActorCameraTarget *tryGetViewTarget(int) const; + }; +} \ No newline at end of file diff --git a/include/al/camera/CameraTicket.h b/include/al/camera/CameraTicket.h new file mode 100644 index 0000000..c9def40 --- /dev/null +++ b/include/al/camera/CameraTicket.h @@ -0,0 +1,32 @@ +#pragma once + +#include "al/actor/Placement.h" +#include "al/camera/CameraPoser.h" + +namespace al { + + class CameraTicketId { + public: + CameraTicketId(al::PlacementId const*,char const*); + void isEqual(al::CameraTicketId const&); + void isEqual(al::CameraTicketId const&,al::CameraTicketId const&); + void isEqual(al::ByamlIter const&); + void tryGetObjId(void); + void getObjId(void); + + al::PlacementId *mPlacement; + const char *mTicketName; + + }; + + class CameraTicket { + public: + CameraTicket(CameraPoser *poser, CameraTicketId const *ticketID, int priority); + void setPriority(int); + + CameraPoser *mPoser; + CameraTicketId *mTicketID; + int mPriority; + bool mIsActive; + }; +} \ No newline at end of file diff --git a/include/al/camera/CameraTurnInfo.h b/include/al/camera/CameraTurnInfo.h new file mode 100644 index 0000000..0d7ca14 --- /dev/null +++ b/include/al/camera/CameraTurnInfo.h @@ -0,0 +1,7 @@ +#pragma once + +namespace al { + class CameraTurnInfo { + + }; +} \ No newline at end of file diff --git a/include/al/camera/CameraVerticalAbsorber.h b/include/al/camera/CameraVerticalAbsorber.h new file mode 100644 index 0000000..d30c586 --- /dev/null +++ b/include/al/camera/CameraVerticalAbsorber.h @@ -0,0 +1,10 @@ +#pragma once + +namespace al +{ + class CameraVerticalAbsorber { + public: + bool isValid(void); + + }; +}; \ No newline at end of file diff --git a/include/al/camera/GyroCameraCtrl.h b/include/al/camera/GyroCameraCtrl.h new file mode 100644 index 0000000..fc47a82 --- /dev/null +++ b/include/al/camera/GyroCameraCtrl.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class GyroCameraCtrl { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/camera/Projection.h b/include/al/camera/Projection.h new file mode 100644 index 0000000..9851e65 --- /dev/null +++ b/include/al/camera/Projection.h @@ -0,0 +1,10 @@ +#pragma once + +namespace al +{ + class Projection { + public: + float getFovy(void) const; + void setFovy(float); + }; +}; \ No newline at end of file diff --git a/include/al/camera/SnapshotCameraCtrl.h b/include/al/camera/SnapshotCameraCtrl.h new file mode 100644 index 0000000..5c3d312 --- /dev/null +++ b/include/al/camera/SnapshotCameraCtrl.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class SnapShotCameraCtrl { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/camera/alCameraPoserFunction.h b/include/al/camera/alCameraPoserFunction.h new file mode 100644 index 0000000..062882d --- /dev/null +++ b/include/al/camera/alCameraPoserFunction.h @@ -0,0 +1,228 @@ +#pragma once + +#include "CameraPoser.h" +#include "al/actor/Placement.h" +#include "sead/math/seadVector.h" + +namespace alCameraPoserFunction { + + struct CameraCollisionHitResult; + + void initCameraArrowCollider(al::CameraPoser *); + void calcCameraPose(sead::Quat *,al::CameraPoser const*); + void calcLookDir(sead::Vector3 *,al::CameraPoser const*); + void calcCameraDir(sead::Vector3 *,al::CameraPoser const*); + void calcCameraDirH(sead::Vector3 *,al::CameraPoser const*); + void calcLookDirH(sead::Vector3 *,al::CameraPoser const*); + void calcSideDir(sead::Vector3 *,al::CameraPoser const*); + void calcPreCameraDir(sead::Vector3 *,al::CameraPoser const*); + void calcPreCameraDirH(sead::Vector3 *,al::CameraPoser const*); + void calcPreLookDir(sead::Vector3 *,al::CameraPoser const*); + void calcPreLookDirH(sead::Vector3 *,al::CameraPoser const*); + float calcPreCameraAngleH(al::CameraPoser const*); + float calcPreCameraAngleV(al::CameraPoser const*); + void setLookAtPosToTarget(al::CameraPoser *); + void calcTargetTrans(sead::Vector3 *,al::CameraPoser const*); + void setLookAtPosToTargetAddOffset(al::CameraPoser *,sead::Vector3 const&); + void setCameraPosToTarget(al::CameraPoser *); + void setCameraPosToTargetAddOffset(al::CameraPoser *,sead::Vector3 const&); + void calcTargetTransWithOffset(sead::Vector3 *,al::CameraPoser const*); + void calcTargetVelocity(sead::Vector3 *,al::CameraPoser const*); + void calcTargetVelocityH(sead::Vector3 *,al::CameraPoser const*); + void calcTargetUp(sead::Vector3 *,al::CameraPoser const*); + void calcTargetSpeedV(al::CameraPoser const*); + void calcTargetPose(sead::Quat *,al::CameraPoser const*); + void calcTargetFront(sead::Vector3 *,al::CameraPoser const*); + void calcTargetSide(sead::Vector3 *,al::CameraPoser const*); + void calcTargetGravity(sead::Vector3 *,al::CameraPoser const*); + void calcTargetSpeedH(al::CameraPoser const*); + void calcTargetJumpSpeed(al::CameraPoser const*); + void calcTargetFallSpeed(al::CameraPoser const*); + void tryGetTargetRequestDistance(float *,al::CameraPoser const*); + void tryGetBossDistanceCurve(al::CameraPoser const*); + void tryGetEquipmentDistanceCurve(al::CameraPoser const*); + void tryCalcSlopeCollisionDownFrontDirH(sead::Vector3 *,al::CameraPoser const*); + void checkValidTurnToSubTarget(al::CameraPoser const*); + void calcSubTargetBack(sead::Vector3 *,al::CameraPoser const*); + void calcSubTargetTrans(sead::Vector3 *,al::CameraPoser const*); + void calcSubTargetFront(sead::Vector3 *,al::CameraPoser const*); + void tryCalcSubTargetTurnBrakeDistanceRate(float *,al::CameraPoser const*); + void clampAngleSubTargetTurnRangeV(float *,al::CameraPoser const*); + void initCameraVerticalAbsorber(al::CameraPoser *); + void initCameraVerticalAbsorberNoCameraPosAbsorb(al::CameraPoser *); + void liberateVerticalAbsorb(al::CameraPoser *); + void stopUpdateVerticalAbsorb(al::CameraPoser *); + void stopUpdateVerticalAbsorbForSnapShotMode(al::CameraPoser *,sead::Vector3 const&); + void restartUpdateVerticalAbsorb(al::CameraPoser *); + void validateVerticalAbsorbKeepInFrame(al::CameraPoser *); + void invalidateVerticalAbsorbKeepInFrame(al::CameraPoser *); + void setVerticalAbsorbKeepInFrameScreenOffsetUp(al::CameraPoser *,float); + void setVerticalAbsorbKeepInFrameScreenOffsetDown(al::CameraPoser *,float); + void initCameraArrowCollider(al::CameraPoser *); + void initCameraArrowColliderWithoutThroughPassCollision(al::CameraPoser *); + void initCameraMoveLimit(al::CameraPoser *); + void initCameraAngleCtrl(al::CameraPoser *); + void initCameraAngleCtrlWithRelativeH(al::CameraPoser *); + void initCameraDefaultAngleRangeV(al::CameraPoser *,float,float); + void setCameraStartAngleV(al::CameraPoser *,float); + void setCameraAngleV(al::CameraPoser *,float); + void initAngleSwing(al::CameraPoser *); + void initCameraOffsetCtrlPreset(al::CameraPoser *); + void initGyroCameraCtrl(al::CameraPoser *); + void resetGyro(al::CameraPoser *); + void calcCameraGyroPose(al::CameraPoser const*,sead::Vector3 *,sead::Vector3 *,sead::Vector3 *); + void setGyroLimitAngleV(al::CameraPoser *,float,float); + void setGyroSensitivity(al::CameraPoser *,float,float); + void reduceGyroSencitivity(al::CameraPoser *); + void stopUpdateGyro(al::CameraPoser *); + void restartUpdateGyro(al::CameraPoser *); + void initSnapShotCameraCtrl(al::CameraPoser *); + void initSnapShotCameraCtrlZoomAutoReset(al::CameraPoser *); + void initSnapShotCameraCtrlZoomRollMove(al::CameraPoser *); + void validateSnapShotCameraLookAtOffset(al::CameraPoser *); + void validateSnapShotCameraZoomFovy(al::CameraPoser *); + void validateSnapShotCameraRoll(al::CameraPoser *); + void updateSnapShotCameraCtrl(al::CameraPoser *); + void startResetSnapShotCameraCtrl(al::CameraPoser *,int); + void setSnapShotMaxZoomOutFovyDegree(al::CameraPoser *,float); + void onVerticalAbsorb(al::CameraPoser *); + void offVerticalAbsorb(al::CameraPoser *); + void invalidateCameraBlur(al::CameraPoser *); + void validateCollider(al::CameraPoser *); + void invalidateCollider(al::CameraPoser *); + void validateCtrlSubjective(al::CameraPoser *); + void invalidateChangeSubjective(al::CameraPoser *); + void invalidateKeepDistanceNextCamera(al::CameraPoser *); + void invalidateKeepDistanceNextCameraIfNoCollide(al::CameraPoser *); + void invalidatePreCameraEndAfterInterpole(al::CameraPoser *); + void checkFirstCameraCollisionArrow(sead::Vector3 *,sead::Vector3 *,al::IUseCollision const*,sead::Vector3 const&,sead::Vector3 const&); + void checkFirstCameraCollisionArrow(alCameraPoserFunction::CameraCollisionHitResult *,al::IUseCollision const*,sead::Vector3 const&,sead::Vector3 const&); + void checkFirstCameraCollisionArrowOnlyCeiling(sead::Vector3 *,sead::Vector3 *,al::IUseCollision const*,sead::Vector3 const&,sead::Vector3 const&); + void checkCameraCollisionMoveSphere(sead::Vector3 *,al::IUseCollision const*,sead::Vector3 const&,sead::Vector3 const&,float); + void calcZoneRotateAngleH(float,al::CameraPoser const*); + void calcZoneRotateAngleH(float,sead::Matrix34 const&); + void calcZoneInvRotateAngleH(float,sead::Matrix34 const&); + void multVecZone(sead::Vector3 *,sead::Vector3 const&,al::CameraPoser const*); + void multVecInvZone(sead::Vector3 *,sead::Vector3 const&,al::CameraPoser const*); + void rotateVecZone(sead::Vector3 *,sead::Vector3 const&,al::CameraPoser const*); + void calcOffsetCameraKeepInFrameV(sead::Vector3 *,sead::LookAtCamera *,sead::Vector3 const&,al::CameraPoser const*,float,float); + void makeCameraKeepInFrameV(sead::LookAtCamera *,sead::Vector3 const&,al::CameraPoser const*,float,float); + void initCameraRail(al::CameraPoser *,al::PlacementInfo const&,char const*); + void tryGetCameraRailArg(float *,al::PlacementInfo const&,char const*,char const*); + void tryFindNearestLimitRailKeeper(al::CameraPoser const*,sead::Vector3 const&); + void calcCameraRotateStick(sead::Vector2 *,al::CameraPoser const*); + float calcCameraRotateStickH(al::CameraPoser const*); + float calcCameraRotateStickV(al::CameraPoser const*); + float calcCameraRotateStickPower(al::CameraPoser const*); + void tryCalcCameraSnapShotMoveStick(sead::Vector2*, al::CameraPoser const*); + + void getViewIndex(al::CameraPoser const*); + sead::LookAtCamera* getLookAtCamera(al::CameraPoser const*); + void getProjectionSead(al::CameraPoser const*); + void getProjection(al::CameraPoser const*); + void getProjectionMtx(al::CameraPoser const*); + void getNear(al::CameraPoser const*); + void getFar(al::CameraPoser const*); + void getAspect(al::CameraPoser const*); + void getPreCameraPos(al::CameraPoser const*); + sead::Vector3f *getPreLookAtPos(al::CameraPoser const*); + void getPreUpDir(al::CameraPoser const*); + void getPreFovyDegree(al::CameraPoser const*); + void getPreFovyRadian(al::CameraPoser const*); + void getPreCameraSwingAngleH(al::CameraStartInfo const&); + void getPreCameraSwingAngleV(al::CameraStartInfo const&); + void getPreCameraMaxSwingAngleH(al::CameraStartInfo const&); + void getPreCameraMaxSwingAngleV(al::CameraStartInfo const&); + void getAreaAngleH(al::CameraStartInfo const&); + void getAreaAngleV(al::CameraStartInfo const&); + void getNextAngleHByPreCamera(al::CameraStartInfo const&); + void getNextAngleVByPreCamera(al::CameraStartInfo const&); + void getUnderTargetCollisionPos(al::CameraPoser const*); + void getUnderTargetCollisionNormal(al::CameraPoser const*); + void getSlopeCollisionUpSpeed(al::CameraPoser const*); + void getSlopeCollisionDownSpeed(al::CameraPoser const*); + void getSubTargetRequestDistance(al::CameraPoser const*); + void getSubTargetTurnSpeedRate1(al::CameraPoser const*); + void getSubTargetTurnSpeedRate2(al::CameraPoser const*); + void getSubTargetTurnRestartStep(al::CameraPoser const*); + void getCameraVerticalAbsorbPosUp(al::CameraPoser const*); + void getCameraVerticalAbsorbPosDown(al::CameraPoser const*); + float getCameraAngleH(al::CameraPoser const*); + float getCameraAngleV(al::CameraPoser const*); + float getOffset(al::CameraPoser const*); + void getGyroFront(al::CameraPoser *); + float getGyroAngleV(al::CameraPoser *); + float getGyroAngleH(al::CameraPoser *); + void getSnapShotRollDegree(al::CameraPoser const*); + void getSnapShotLookAtOffset(al::CameraPoser const*); + void getRequestTargetAngleV(al::CameraObjectRequestInfo const&); + float getRequestAngleSpeed(al::CameraObjectRequestInfo const&); + float getRequestAngleV(al::CameraObjectRequestInfo const&); + void getCameraRailPointObjId(al::CameraPoser const*,int); + float getStickSensitivityLevel(al::CameraPoser const*); + float getStickSensitivityScale(al::CameraPoser const*); + void getGyroSensitivityLevel(al::CameraPoser const*); + void getGyroSensitivityScale(al::CameraPoser const*); + + bool isPrePriorityDemo(al::CameraStartInfo const&); + bool isPrePriorityDemo2(al::CameraStartInfo const&); + bool isPrePriorityDemoTalk(al::CameraStartInfo const&); + bool isPrePriorityDemoAll(al::CameraStartInfo const&); + bool isPrePriorityEntranceAll(al::CameraStartInfo const&); + bool isPrePriorityPlayer(al::CameraStartInfo const&); + bool isEqualPreCameraName(al::CameraStartInfo const&,char const*); + bool isPreCameraFixAbsolute(al::CameraStartInfo const&); + bool isInvalidCollidePreCamera(al::CameraStartInfo const&); + bool isInvalidKeepPreCameraDistance(al::CameraStartInfo const&); + bool isInvalidKeepPreCameraDistanceIfNoCollide(al::CameraStartInfo const&); + bool isValidResetPreCameraPose(al::CameraStartInfo const&); + bool isValidKeepPreSelfCameraPose(al::CameraStartInfo const&); + bool isExistAreaAngleH(al::CameraStartInfo const&); + bool isExistAreaAngleV(al::CameraStartInfo const&); + bool isExistNextPoseByPreCamera(al::CameraStartInfo const&); + bool isChangeTarget(al::CameraPoser const*); + bool isExistCollisionUnderTarget(al::CameraPoser const*); + bool isExistSlopeCollisionUnderTarget(al::CameraPoser const*); + bool isExistWallCollisionUnderTarget(al::CameraPoser const*); + bool isExistSubTarget(al::CameraPoser const*); + bool isChangeSubTarget(al::CameraPoser const*); + bool isValidSubTargetTurnV(al::CameraPoser const*); + bool isValidSubTargetResetAfterTurnV(al::CameraPoser const*); + bool isValidAngleSwing(al::CameraPoser const*); + bool isStopUpdateGyro(al::CameraPoser const*); + bool isTargetCollideGround(al::CameraPoser const*); + bool isTargetInWater(al::CameraPoser const*); + bool isTargetInMoonGravity(al::CameraPoser const*); + bool isTargetClimbPole(al::CameraPoser const*); + bool isTargetGrabCeil(al::CameraPoser const*); + bool isTargetInvalidMoveByInput(al::CameraPoser const*); + bool isTargetEnableEndAfterInterpole(al::CameraPoser const*); + bool isTargetWallCatch(al::CameraPoser const*); + bool isSnapShotMode(al::CameraPoser const*); + bool isOffVerticalAbsorb(al::CameraPoser const*); + bool isRequestStopVerticalAbsorb(al::CameraObjectRequestInfo const&); + bool isRequestResetPosition(al::CameraObjectRequestInfo const&); + bool isRequestResetAngleV(al::CameraObjectRequestInfo const&); + bool isRequestDownToDefaultAngleBySpeed(al::CameraObjectRequestInfo const&); + bool isRequestUpToTargetAngleBySpeed(al::CameraObjectRequestInfo const&); + bool isRequestMoveDownAngleV(al::CameraObjectRequestInfo const&); + bool isRequestSetAngleV(al::CameraObjectRequestInfo const&); + bool isInvalidCollider(al::CameraPoser const*); + bool isInvalidPreCameraEndAfterInterpole(al::CameraPoser const*); + bool isSceneCameraFirstCalc(al::CameraPoser const*); + bool isActiveInterpole(al::CameraPoser const*); + bool isInvalidEndEntranceCamera(al::CameraPoser const*); + bool isPause(al::CameraPoser const*); + bool isValidGyro(al::CameraPoser const*); + bool isTriggerCameraResetRotate(al::CameraPoser const*); + bool isHoldCameraZoom(al::CameraPoser const*); + bool isHoldCameraSnapShotZoomIn(al::CameraPoser const*); + bool isHoldCameraSnapShotZoomOut(al::CameraPoser const*); + bool isHoldCameraSnapShotRollLeft(al::CameraPoser const*); + bool isHoldCameraSnapShotRollRight(al::CameraPoser const*); + bool isPlayerTypeFlyer(al::CameraPoser const*); + bool isPlayerTypeHighSpeedMove(al::CameraPoser const*); + bool isPlayerTypeHighJump(al::CameraPoser const*); + bool isPlayerTypeNotTouchGround(al::CameraPoser const*); + bool isOnRideObj(al::CameraPoser const*); +} \ No newline at end of file diff --git a/include/al/collision/Collider.h b/include/al/collision/Collider.h new file mode 100644 index 0000000..d52f80f --- /dev/null +++ b/include/al/collision/Collider.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include "al/hio/HioNode.h" +#include "CollisionDirector.h" + +typedef unsigned int uint; + +namespace al +{ +struct SphereInterpolator; +struct SphereHitInfo; +struct CollisionPartsFilterBase; +struct TriangleFilterBase; + +class Collider : public al::HioNode, public IUseCollision { +public: + Collider(al::CollisionDirector*, sead::Matrix34f const*, sead::Vector3f const*, + sead::Vector3f const*, float, float, uint); + void calcCheckPos(sead::Vector3f*); + void calcMovePowerByContact(sead::Vector3f*, sead::Vector3f const&); + void clear(); + void clearContactPlane(); + void clearStoredPlaneNum(); + sead::Vector3f collide(sead::Vector3f const&); + void findCollidePos(int*, al::SphereInterpolator*, al::SphereHitInfo*, uint); + void getCollisionDirector(); + void getPlane(int); + void getRecentOnGroundNormal(uint); + void obtainMomentFixReaction(al::SphereHitInfo*, sead::Vector3f*, + sead::Vector3f*, bool, uint); + void onInvalidate(); + void preCollide(al::SphereInterpolator*, sead::Vector3f*, float*, sead::Vector3f const&, + al::SphereHitInfo*, uint); + void setCollisionPartsFilter(al::CollisionPartsFilterBase const*); + void setTriangleFilter(al::TriangleFilterBase const *); + void storeContactPlane(al::SphereHitInfo *); + void storeCurrentHitInfo(al::SphereHitInfo *, uint); + void updateRecentOnGroundInfo(); +}; +} // namespace al diff --git a/include/al/collision/CollisionDirector.h b/include/al/collision/CollisionDirector.h new file mode 100644 index 0000000..ae8474d --- /dev/null +++ b/include/al/collision/CollisionDirector.h @@ -0,0 +1,12 @@ +#pragma once + +namespace al +{ + class CollisionDirector; + + class IUseCollision + { + public: + virtual al::CollisionDirector* getCollisionDirector() const = 0; + }; +}; \ No newline at end of file diff --git a/include/al/debug/GpuPerf.h b/include/al/debug/GpuPerf.h new file mode 100644 index 0000000..503ca86 --- /dev/null +++ b/include/al/debug/GpuPerf.h @@ -0,0 +1,19 @@ +#pragma once + +#include "agl/DrawContext.h" +#include "sead/gfx/seadFrameBuffer.h" + +// seems to be a static class for managing agl::fctr::GPUStressChecker +namespace al +{ + class GpuPerf { + public: + GpuPerf(void); + void beginPerf(agl::DrawContext *); + void endPerf(agl::DrawContext *); + void update(void); + void drawResult(agl::DrawContext *,sead::FrameBuffer const*) const; + + // this class has no members + }; +} // namespace al diff --git a/include/al/effect/Effect.h b/include/al/effect/Effect.h new file mode 100644 index 0000000..88a3657 --- /dev/null +++ b/include/al/effect/Effect.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al +{ + class Effect + { + + }; +}; \ No newline at end of file diff --git a/include/al/effect/EffectInfo.h b/include/al/effect/EffectInfo.h new file mode 100644 index 0000000..f126bca --- /dev/null +++ b/include/al/effect/EffectInfo.h @@ -0,0 +1,10 @@ +#pragma once + +namespace al +{ + class EffectInfo + { + public: + + }; +} \ No newline at end of file diff --git a/include/al/effect/EffectKeeper.h b/include/al/effect/EffectKeeper.h new file mode 100644 index 0000000..638a630 --- /dev/null +++ b/include/al/effect/EffectKeeper.h @@ -0,0 +1,17 @@ +#pragma once + +namespace al +{ + class EffectKeeper; + + class IUseEffectKeeper + { + public: + virtual al::EffectKeeper* getEffectKeeper() const = 0; + }; + + class EffectKeeper + { + + }; +} \ No newline at end of file diff --git a/include/al/effect/EffectSystemInfo.h b/include/al/effect/EffectSystemInfo.h new file mode 100644 index 0000000..b6243cf --- /dev/null +++ b/include/al/effect/EffectSystemInfo.h @@ -0,0 +1,8 @@ +#pragma once + +namespace al { + class EffectSystemInfo { + public: + + }; +} \ No newline at end of file diff --git a/include/al/effect/EffectUserInfo.h b/include/al/effect/EffectUserInfo.h new file mode 100644 index 0000000..f4735f2 --- /dev/null +++ b/include/al/effect/EffectUserInfo.h @@ -0,0 +1,19 @@ +#pragma once + +namespace al +{ + class EffectUserInfo + { + public: + EffectUserInfo(); + + unsigned long _0; + int _8; + int _C; + unsigned long _10; + int _18; + int _1C; + unsigned long _20; + unsigned long _28; + }; +}; \ No newline at end of file diff --git a/include/al/event/IEventFlowEventReceiver.h b/include/al/event/IEventFlowEventReceiver.h new file mode 100644 index 0000000..a374c13 --- /dev/null +++ b/include/al/event/IEventFlowEventReceiver.h @@ -0,0 +1,8 @@ +#pragma once + +namespace al +{ + class IEventFlowEventReceiver { + + }; +} // namespace al diff --git a/include/al/execute/ExecuteDirector.h b/include/al/execute/ExecuteDirector.h new file mode 100644 index 0000000..90ca68a --- /dev/null +++ b/include/al/execute/ExecuteDirector.h @@ -0,0 +1,8 @@ +#pragma once + +namespace al +{ + class ExecuteDirector { + public: + }; +} // namespace al diff --git a/include/al/execute/IUseExecutor.h b/include/al/execute/IUseExecutor.h new file mode 100644 index 0000000..a60ae00 --- /dev/null +++ b/include/al/execute/IUseExecutor.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al { +class IUseExecutor { +public: + virtual void execute() = 0; + virtual void draw() = 0; +}; +} \ No newline at end of file diff --git a/include/al/factory/ActorFactory.h b/include/al/factory/ActorFactory.h new file mode 100644 index 0000000..3e6e22d --- /dev/null +++ b/include/al/factory/ActorFactory.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Factory.h" +#include "logger.hpp" + +namespace al { + + template + LiveActor* createActorFunction(const char *name); + + template + LiveActor *createCustomActor(const char *name) + { + return new T(name); + }; + + class LiveActor; + + typedef LiveActor* (*createActor)(const char* name); + + class ActorFactory : public Factory { + public: + ActorFactory(const char *fName) { + this->factoryName = fName; + this->actorTable = nullptr; + this->factoryCount = 0; + }; + }; +} \ No newline at end of file diff --git a/include/al/factory/ActorFactoryEntries100.h b/include/al/factory/ActorFactoryEntries100.h new file mode 100644 index 0000000..99e0685 --- /dev/null +++ b/include/al/factory/ActorFactoryEntries100.h @@ -0,0 +1,645 @@ +#pragma once + +#include "ActorFactory.h" + +#include "actors/PuppetActor.h" +#include "actors/PuppetHackActor.h" + +namespace al +{ + class AllDeadWatcher; + class BgmPlayObj; + class CameraRailHolder; + class CameraWatchPoint; + class RippleFixMapParts; + class EffectObj; + class EffectObj; + class EffectObjCameraEmit; + class EffectObjFollowCamera; + class EffectObjFollowCameraLimit; + class EffectObjInterval; + class EntranceCameraStartObj; + class FogRequester; + class GraphicsObjShadowMaskSphere; + class KeyMoveCameraObj; + class LightningController; + class OccludedEffectRequester; + class OneMeshFixMapParts; + class PrePassLineLight; + class PrePassPointLight; + class PrePassProjLight; + class PrePassProjOrthoLight; + class PrePassSpotLight; + class RippleGeneratePoint; + class RippleGeneratePoint; + class SeBarrierObj; + class SePlayObj; + class SePlayRail; + class Sky; + class SwitchKeyMoveMapParts; + class ThunderRenderRequester; + class WaterAreaMoveModel; + class AtmosScatterRequester; + class BackHideParts; + class ClockMapParts; + class ConveyerMapParts; + class FallMapParts; + class FixMapParts; + class FloaterMapParts; + class FlowMapParts; + class GateMapParts; + class KeyMoveMapParts; + class KeyMoveMapPartsGenerator; + class RailMoveMapParts; + class RollingCubeMapParts; + class RippleFixMapParts; + class RotateMapParts; + class SeesawMapParts; + class SlideMapParts; + class SubActorLodMapParts; + class SurfMapParts; + class SwingMapParts; + class SwitchDitherMapParts; + class SwitchKeepOnWatcher; + class SwitchOpenMapParts; + class VisibleSwitchMapParts; + class WheelMapParts; + class WobbleMapParts; +} // namespace al + +__attribute((used)) static al::NameToCreator actorEntries[] = { + // CUSTOM ACTOR ENTRIES HERE + {"PuppetActor", &al::createCustomActor}, + {"PuppetHackActor", &al::createCustomActor}, + // VANILLA ACTOR ENTRIES + {"AchievementNpc", &al::createActorFunction}, + {"AirBubble", &al::createActorFunction}, + {"AirBubbleGenerator", &al::createActorFunction}, + {"AirCurrent", &al::createActorFunction}, + {"AllDeadWatcher", &al::createActorFunction}, + {"AllDeadWatcherWithShine", &al::createActorFunction}, + {"AmiiboHelpNpc", &al::createActorFunction}, + {"AmiiboNpc", &al::createActorFunction}, + {"AnagramAlphabet", &al::createActorFunction}, + {"Barrel2D", &al::createActorFunction}, + {"BarrelGenerator2D", &al::createActorFunction}, + {"BarrierField", &al::createActorFunction}, + {"BazookaElectric", &al::createActorFunction}, + {"BendLeafTree", &al::createActorFunction}, + {"BgmPlayObj", &al::createActorFunction}, + {"Bird", &al::createActorFunction}, + {"BirdCarryMeat", &al::createActorFunction}, + {"BirdPlayerGlideCtrl", &al::createActorFunction}, + {"BlockBrick", &al::createActorFunction}, + {"BlockBrick2D", &al::createActorFunction}, + {"BlockBrickBig2D", &al::createActorFunction}, + {"BlockEmpty", &al::createActorFunction}, + {"BlockEmpty2D", &al::createActorFunction}, + {"BlockHard", &al::createActorFunction}, + {"ClashWorldBlockHard", &al::createActorFunction}, + {"BlockQuestion", &al::createActorFunction}, + {"CityBlockQuestion", &al::createActorFunction}, + {"BlockQuestion2D", &al::createActorFunction}, + {"BlockTransparent", &al::createActorFunction}, + {"BlockTransparent2D", &al::createActorFunction}, + {"BlowObjBeans", &al::createActorFunction}, + {"BlowObjCan", &al::createActorFunction}, + {"BlowObjGarbageBag", &al::createActorFunction}, + {"BlowObjMushroom", &al::createActorFunction}, + {"BlowObj", &al::createActorFunction}, + {"BombTail", &al::createActorFunction}, + {"BossForest", &al::createActorFunction}, + {"BossForestBlock", &al::createActorFunction}, + {"BossForestWander", &al::createActorFunction}, + {"BossKnuckle", &al::createActorFunction}, + {"BossKnuckleCounterGround", &al::createActorFunction}, + {"BossKnuckleFix", &al::createActorFunction}, + {"BossMagma", &al::createActorFunction}, + {"BossRaid", &al::createActorFunction}, + {"BossRaidNpc", &al::createActorFunction}, + {"BossRaidRivet", &al::createActorFunction}, + {"BreakablePole", &al::createActorFunction}, + {"Breeda", &al::createActorFunction}, + {"Bubble", &al::createActorFunction}, + {"Bubble2D", &al::createActorFunction}, + {"BubbleLauncher", &al::createActorFunction}, + {"Bull", &al::createActorFunction}, + {"Byugo", &al::createActorFunction}, + {"Cactus", &al::createActorFunction}, + {"CactusMini", &al::createActorFunction}, + {"CageShine", &al::createActorFunction}, + {"CageSaveSwitch", &al::createActorFunction}, + {"CageStageSwitch", &al::createActorFunction}, + {"CageBreakable", &al::createActorFunction}, + {"CameraDemoGateMapParts", &al::createActorFunction}, + {"CameraDemoKeyMoveMapParts", &al::createActorFunction}, + {"CameraRailHolder", &al::createActorFunction}, + {"CameraSub", &al::createActorFunction}, + {"CameraWatchPoint", &al::createActorFunction}, + {"Candlestand", &al::createActorFunction}, + {"CandlestandFire", &al::createActorFunction}, + {"CandlestandInitializer", &al::createActorFunction}, + {"CandlestandBgmDirector", &al::createActorFunction}, + {"CandlestandSaveWatcher", &al::createActorFunction}, + {"CandlestandWatcher", &al::createActorFunction}, + {"CapAccelerator", &al::createActorFunction}, + {"CapAcceleratorKeyMoveMapParts", &al::createActorFunction}, + {"CapAppearMapParts", &al::createActorFunction}, + {"CapBeamer", &al::createActorFunction}, + {"CapBomb", &al::createActorFunction}, + {"CapCatapult", &al::createActorFunction}, + {"CapFlower", &al::createActorFunction}, + {"CapFlowerGroup", &al::createActorFunction}, + {"CapHanger", &al::createActorFunction}, + {"CapMessageAfterInformation", &al::createActorFunction}, + {"CapRack", &al::createActorFunction}, + {"CapRackTimer", &al::createActorFunction}, + {"CapRailMover", &al::createActorFunction}, + {"CapSlotBase", &al::createActorFunction}, + {"CapSwitch", &al::createActorFunction}, + {"CapSwitchSave", &al::createActorFunction}, + {"CapSwitchTimer", &al::createActorFunction}, + {"CapThrower", &al::createActorFunction}, + {"CapTrampoline", &al::createActorFunction}, + {"Car", &al::createActorFunction}, + {"CarSandWorld", &al::createActorFunction}, + {"CarWatcher", &al::createActorFunction}, + {"CardboardBox", &al::createActorFunction}, + {"CatchBomb", &al::createActorFunction}, + {"Chair", &al::createActorFunction}, + {"CheckpointFlag", &al::createActorFunction}, + {"ChorobonHolder", &al::createActorFunction}, + {"ChurchDoor", &al::createActorFunction}, + {"CityBuilding", &al::createActorFunction}, + {"CityStreetlight", &al::createActorFunction}, + {"CityWorldSign", &al::createActorFunction}, + {"CityWorldUndergroundMachine", &al::createActorFunction}, + {"CitySign", &al::createActorFunction}, + {"CitySignal", &al::createActorFunction}, + {"CityWorldTable", &al::createActorFunction}, + {"Closet", &al::createActorFunction}, + {"CloudStep", &al::createActorFunction}, + {"CollapseSandHill", &al::createActorFunction}, + {"CollectAnimalWatcher", &al::createActorFunction}, + {"CollectBgmSpeaker", &al::createActorFunction}, + {"CollectionList", &al::createActorFunction}, + {"Coin", &al::createActorFunction}, + {"Coin2D", &al::createActorFunction}, + {"Coin2DCityDirector", &al::createActorFunction}, + {"CoinBlow", &al::createActorFunction}, + {"CoinChameleon", &al::createActorFunction}, + {"CoinCirclePlacement", &al::createActorFunction}, + {"CoinCollect", &al::createActorFunction}, + {"CoinCollectHintObj", &al::createActorFunction}, + {"CoinCollect2D", &al::createActorFunction}, + {"CoinLead", &al::createActorFunction}, + {"CoinRail", &al::createActorFunction}, + {"CoinRing", &al::createActorFunction}, + {"CoinStackGroup", &al::createActorFunction}, + {"CrystalBreakable", &al::createActorFunction}, + {"DamageBallGenerator", &al::createActorFunction}, + {"DelaySwitch", &al::createActorFunction}, + {"DemoActorCapManHero", &al::createActorFunction}, + {"DemoActorCapManHeroine", &al::createActorFunction}, + {"DemoActorKoopaShip", &al::createActorFunction}, + {"DemoActorHack", &al::createActorFunction}, + {"DemoActorPeach", &al::createActorFunction}, + {"DemoActorShineTower", &al::createActorFunction}, + {"DemoPeachWorldHomeWater001", &al::createActorFunction}, + {"DemoChangeEffectObj", &al::createActorFunction}, + {"DemoWorldMoveHomeBackGround", &al::createActorFunction}, + {"DemoPeachWedding", &al::createActorFunction}, + {"DemoPlayer", &al::createActorFunction}, + {"DemoPlayerCap", &al::createActorFunction}, + {"DigPoint", &al::createActorFunction}, + {"DigPointHintPhoto", &al::createActorFunction}, + {"DigPointWater", &al::createActorFunction}, + {"DirectionFixedBillboard", &al::createActorFunction}, + {"Dokan", &al::createActorFunction}, + {"DokanKoopa", &al::createActorFunction}, + {"DokanMaze", &al::createActorFunction}, + {"DokanMazeDirector", &al::createActorFunction}, + {"DokanStageChange", &al::createActorFunction}, + {"DonkeyKong2D", &al::createActorFunction}, + {"Donsuke", &al::createActorFunction}, + {"Doshi", &al::createActorFunction}, + {"DoorAreaChange", &al::createActorFunction}, + {"DoorAreaChangeCap", &al::createActorFunction}, + {"DoorCity", &al::createActorFunction}, + {"DoorSnow", &al::createActorFunction}, + {"DoorWarp", &al::createActorFunction}, + {"DoorWarpStageChange", &al::createActorFunction}, + {"EchoBlockMapParts", &al::createActorFunction}, + {"EffectObj", &al::createActorFunction}, + {"EffectObjScale", &al::createActorFunction}, + {"EffectObjAlpha", &al::createActorFunction}, + {"EffectObjCameraEmit", &al::createActorFunction}, + {"EffectObjFollowCamera", &al::createActorFunction}, + {"EffectObjFollowCameraLimit", &al::createActorFunction}, + {"EffectObjInterval", &al::createActorFunction}, + {"EffectObjNpcManFar", &al::createActorFunction}, + {"EffectObjQualityChange", &al::createActorFunction}, + {"ElectricWire", &al::createActorFunction}, + {"ElectricWireKoopa", &al::createActorFunction}, + {"EntranceCameraStartObj", &al::createActorFunction}, + {"EventKeyMoveCameraObjNoDemo", &al::createActorFunction}, + {"EventKeyMoveCameraObjWithDemo", &al::createActorFunction}, + {"FigureWalkingNpc", &al::createActorFunction}, + {"FireBlower", &al::createActorFunction}, + {"FireBrosPossessed", &al::createActorFunction}, + {"FireSwitch", &al::createActorFunction}, + {"FireHydrant", &al::createActorFunction}, + {"FireDrum2D", &al::createActorFunction}, + {"FishingFish", &al::createActorFunction}, + {"FixMapParts2D", &al::createActorFunction}, + {"FixMapPartsAppearKillAsync", &al::createActorFunction}, + {"FixMapPartsBgmChangeAction", &al::createActorFunction}, + {"FixMapPartsCapHanger", &al::createActorFunction}, + {"FixMapPartsDitherAppear", &al::createActorFunction}, + {"FixMapPartsForceSafetyPoint", &al::createActorFunction}, + {"FixMapPartsFukankunZoomCapMessage", &al::createActorFunction}, + {"FixMapPartsScenarioAction", &al::createActorFunction}, + {"FlyObject", &al::createActorFunction}, + {"ForestManSeed", &al::createActorFunction}, + {"ForestWorldHomeBreakParts000", &al::createActorFunction}, + {"FogRequester", &al::createActorFunction}, + {"FrailBox", &al::createActorFunction}, + {"Frog", &al::createActorFunction}, + {"Fukankun", &al::createActorFunction}, + {"FukankunZoomCapMessageSun", &al::createActorFunction}, + {"FukuwaraiWatcher", &al::createActorFunction}, + {"ForestWorldEnergyStand", &al::createActorFunction}, + {"ForestWorldFlowerCtrl", &al::createActorFunction}, + {"GabuZou", &al::createActorFunction}, + {"GabuZouGroup", &al::createActorFunction}, + {"Gamane", &al::createActorFunction}, + {"GiantWanderBoss", &al::createActorFunction}, + {"GoalMark", &al::createActorFunction}, + {"GolemClimb", &al::createActorFunction}, + {"Gotogoton", &al::createActorFunction}, + {"GotogotonGoal", &al::createActorFunction}, + {"GraphicsObjShadowMaskCube", &al::createActorFunction}, + {"GraphicsObjShadowMaskSphere", &al::createActorFunction}, + {"GrowerBug", &al::createActorFunction}, + {"GrowerWorm", &al::createActorFunction}, + {"GrowFlowerCoin", &al::createActorFunction}, + {"GrowFlowerWatcher", &al::createActorFunction}, + {"GrowPlantGrowPlace", &al::createActorFunction}, + {"GrowPlantSeed", &al::createActorFunction}, + {"GrowPlantStartStage", &al::createActorFunction}, + {"GrowPlantWatcher", &al::createActorFunction}, + {"Gunetter", &al::createActorFunction}, + {"GunetterMove", &al::createActorFunction}, + {"HackCar", &al::createActorFunction}, + {"HackFork", &al::createActorFunction}, + {"HammerBrosPossessed", &al::createActorFunction}, + {"HammerBros2D", &al::createActorFunction}, + {"HelpNpc", &al::createActorFunction}, + {"HintNpc", &al::createActorFunction}, + {"HintPhoto", &al::createActorFunction}, + {"HintRouteGuidePoint", &al::createActorFunction}, + {"HipDropSwitch", &al::createActorFunction}, + {"HipDropSwitchSave", &al::createActorFunction}, + {"HipDropSwitchTimer", &al::createActorFunction}, + {"HipDropTile", &al::createActorFunction}, + {"HipDropMoveLift", &al::createActorFunction}, + {"HipDropRepairParts", &al::createActorFunction}, + {"HipDropTransformPartsWatcher", &al::createActorFunction}, + {"HomeBed", &al::createActorFunction}, + {"HomeChair", &al::createActorFunction}, + {"HomeInside", &al::createActorFunction}, + {"HomeShip", &al::createActorFunction}, + {"Hosui", &al::createActorFunction}, + {"IcicleFall", &al::createActorFunction}, + {"Imomu", &al::createActorFunction}, + {"IndicatorDirector", &al::createActorFunction}, + {"Jango", &al::createActorFunction}, + {"Joku", &al::createActorFunction}, + {"JugemFishing", &al::createActorFunction}, + {"JumpingRopeNpc", &al::createActorFunction}, + {"Kakku", &al::createActorFunction}, + {"KaronWing", &al::createActorFunction}, + {"KeyMoveCameraFix", &al::createActorFunction}, + {"KickStone", &al::createActorFunction}, + {"KillerLauncher", &al::createActorFunction}, + {"KillerLauncherDot", &al::createActorFunction}, + {"KinokoUfo", &al::createActorFunction}, + {"Koopa", &al::createActorFunction}, + {"KoopaCapPlayer", &al::createActorFunction}, + {"KoopaChurch", &al::createActorFunction}, + {"KoopaLv1", &al::createActorFunction}, + {"KoopaLv2", &al::createActorFunction}, + {"KoopaLv3", &al::createActorFunction}, + {"KoopaShip", &al::createActorFunction}, + {"Kuribo2D3D", &al::createActorFunction}, + {"KuriboGenerator2D3D", &al::createActorFunction}, + {"KuriboGirl", &al::createActorFunction}, + {"KuriboPossessed", &al::createActorFunction}, + {"KuriboMini", &al::createActorFunction}, + {"KuriboTowerSwitch", &al::createActorFunction}, + {"KuriboWing", &al::createActorFunction}, + {"LavaFryingPan", &al::createActorFunction}, + {"LavaStewVeget", &al::createActorFunction}, + {"LavaPan", &al::createActorFunction}, + {"LavaWave", &al::createActorFunction}, + {"LifeMaxUpItem", &al::createActorFunction}, + {"LifeMaxUpItem2D", &al::createActorFunction}, + {"LifeUpItem", &al::createActorFunction}, + {"LifeUpItem2D", &al::createActorFunction}, + {"LightningController", &al::createActorFunction}, + {"LongGenerator", &al::createActorFunction}, + {"MarchingCubeBlock", &al::createActorFunction}, + {"MapPartsRoulette", &al::createActorFunction}, + {"Megane", &al::createActorFunction}, + {"MeganeLiftExLift", &al::createActorFunction}, + {"MeganeKeyMoveMapParts", &al::createActorFunction}, + {"MeganeMapParts", &al::createActorFunction}, + {"Mirror", &al::createActorFunction}, + {"MoonBasementBreakParts", &al::createActorFunction}, + {"MoonBasementClimaxWatcher", &al::createActorFunction}, + {"MoonBasementFallObj", &al::createActorFunction}, + {"MoonBasementFinalGate", &al::createActorFunction}, + {"MoonBasementFallObjDecoration", &al::createActorFunction}, + {"MoonBasementFloor", &al::createActorFunction}, + {"MoonBasementGate", &al::createActorFunction}, + {"MoonBasementMeteorAreaObj", &al::createActorFunction}, + {"MoonBasementPillar", &al::createActorFunction}, + {"MoonBasementRock", &al::createActorFunction}, + {"MoonBasementSlideObj", &al::createActorFunction}, + {"MoonRock", &al::createActorFunction}, + {"MoonWorldBell", &al::createActorFunction}, + {"MoonWorldCaptureParadeLift", &al::createActorFunction}, + {"Mofumofu", &al::createActorFunction}, + {"MofumofuLv2", &al::createActorFunction}, + {"MofumofuScrap", &al::createActorFunction}, + {"Motorcycle", &al::createActorFunction}, + {"MotorcycleParkingLot", &al::createActorFunction}, + {"MoveHomeNpc", &al::createActorFunction}, + {"MoviePlayerMapParts", &al::createActorFunction}, + {"MultiGateKeeperBonfire", &al::createActorFunction}, + {"MultiGateKeeperWatcher", &al::createActorFunction}, + {"Mummy", &al::createActorFunction}, + {"MummyGenerator", &al::createActorFunction}, + {"NeedleTrap", &al::createActorFunction}, + {"Nokonoko2D", &al::createActorFunction}, + {"NoteObjFirst", &al::createActorFunction}, + {"NoteObjFirst2D", &al::createActorFunction}, + {"NoteObjDirector", &al::createActorFunction}, + {"Objex", &al::createActorFunction}, + {"OccludedEffectRequester", &al::createActorFunction}, + {"OceanWave", &al::createActorFunction}, + {"CloudOcean", &al::createActorFunction}, + {"DemoCloudOcean", &al::createActorFunction}, + {"OneMeshFixMapParts", &al::createActorFunction}, + {"OpeningStageStartDemo", &al::createActorFunction}, + {"PackunFire", &al::createActorFunction}, + {"PadRumblePoint", &al::createActorFunction}, + {"PaintObj", &al::createActorFunction}, + {"PaulineAtCeremony", &al::createActorFunction}, + {"PaulineAudience", &al::createActorFunction}, + {"PeachWorldHomeCastleCap", &al::createActorFunction}, + {"PeachWorldGate", &al::createActorFunction}, + {"PeachWorldMoatWater", &al::createActorFunction}, + {"PeachWorldTree", &al::createActorFunction}, + {"Pecho", &al::createActorFunction}, + {"Pen", &al::createActorFunction}, + {"PictureStageChange", &al::createActorFunction}, + {"PillarKeyMoveParts", &al::createActorFunction}, + {"PillarSwitchOpenMapParts", &al::createActorFunction}, + {"PlayerMotionObserver", &al::createActorFunction}, + {"PlayerStartObj", &al::createActorFunction}, + {"PlayerSubjectiveWatchCheckObj", &al::createActorFunction}, + {"PlayGuideBoard", &al::createActorFunction}, + {"PlayRecorder", &al::createActorFunction}, + {"PlayerStartObjNoLink", &al::createActorFunction}, + {"PochiHintPhoto", &al::createActorFunction}, + {"Poetter", &al::createActorFunction}, + {"PoleClimbParts", &al::createActorFunction}, + {"PoleClimbPartsBreak", &al::createActorFunction}, + {"PoleGrabCeil", &al::createActorFunction}, + {"PoleGrabCeilKeyMoveParts", &al::createActorFunction}, + {"PopnGenerator", &al::createActorFunction}, + {"LavaWorldPoster", &al::createActorFunction}, + {"PosterCeremony", &al::createActorFunction}, + {"PosterWedding", &al::createActorFunction}, + {"ReactionObjectSkyRhythm", &al::createActorFunction}, + {"PosterWatcher", &al::createActorFunction}, + {"PrePassCausticsLight", &al::createActorFunction}, + {"PrePassLineLight", &al::createActorFunction}, + {"PrePassPointLight", &al::createActorFunction}, + {"PrePassProjLight", &al::createActorFunction}, + {"PrePassProjOrthoLight", &al::createActorFunction}, + {"PrePassSpotLight", &al::createActorFunction}, + {"ProjectRaceCheckPoint", &al::createActorFunction}, + {"Pyramid", &al::createActorFunction}, + {"QuestObj", &al::createActorFunction}, + {"RabbitGraph", &al::createActorFunction}, + {"RaceAudienceNpc", &al::createActorFunction}, + {"RaceManGoal", &al::createActorFunction}, + {"RaceManRace", &al::createActorFunction}, + {"RaceManStart", &al::createActorFunction}, + {"RaceWatcher", &al::createActorFunction}, + {"RadiConRaceWatcher", &al::createActorFunction}, + {"RadioCassette", &al::createActorFunction}, + {"RadiconNpc", &al::createActorFunction}, + {"Radish", &al::createActorFunction}, + {"RadishGold", &al::createActorFunction}, + {"RailDrawer", &al::createActorFunction}, + {"RankingNpc", &al::createActorFunction}, + {"ReactionObject", &al::createActorFunction}, + {"CarBreakable", &al::createActorFunction}, + {"ReactionObjectDotCharacter", &al::createActorFunction}, + {"ReflectBombGenerator", &al::createActorFunction}, + {"RhythmSpotlight", &al::createActorFunction}, + {"RippleGeneratePoint", &al::createActorFunction}, + {"RippleGenerateSquare", &al::createActorFunction}, + {"RotateTarget", &al::createActorFunction}, + {"RouletteSwitch", &al::createActorFunction}, + {"RouteGuideArrow", &al::createActorFunction}, + {"RouteGuideRail", &al::createActorFunction}, + {"RunAwayNpc", &al::createActorFunction}, + {"SandGeyser", &al::createActorFunction}, + {"SandWorldHomeLift", &al::createActorFunction}, + {"SaucePan", &al::createActorFunction}, + {"SaveFlagCheckObj", &al::createActorFunction}, + {"ScenarioStartCameraAnim", &al::createActorFunction}, + {"ScenarioStartCameraSimpleZoom", &al::createActorFunction}, + {"ScenarioStartCameraRailMove", &al::createActorFunction}, + {"Senobi", &al::createActorFunction}, + {"SenobiGeneratePoint", &al::createActorFunction}, + {"SenobiMoveMapParts", &al::createActorFunction}, + {"SenobiMoveMapPartsConnector", &al::createActorFunction}, + {"SeBarrierObj", &al::createActorFunction}, + {"SePlayObj", &al::createActorFunction}, + {"SePlayObjWithSave", &al::createActorFunction}, + {"SePlayRail", &al::createActorFunction}, + {"SequentialSwitch", &al::createActorFunction}, + {"SessionBgmCtrlObj", &al::createActorFunction}, + {"SessionMayorNpc", &al::createActorFunction}, + {"SessionMusicianNpc", &al::createActorFunction}, + {"Shibaken", &al::createActorFunction}, + {"ShibakenHomeShipInside", &al::createActorFunction}, + {"Shine", &al::createActorFunction}, + {"ShineWithAppearCamera", &al::createActorFunction}, + {"ShineChipWatcher", &al::createActorFunction}, + {"ShineDot", &al::createActorFunction}, + {"ShineFukankunWatchObj", &al::createActorFunction}, + {"ShineTowerRocket", &al::createActorFunction}, + {"ShopBgmPlayer", &al::createActorFunction}, + {"ShopMark", &al::createActorFunction}, + {"ShoppingWatcher", &al::createActorFunction}, + {"SignBoardDanger", &al::createActorFunction}, + {"SignBoardLayoutTexture", &al::createActorFunction}, + {"SkyFukankunZoomCapMessage", &al::createActorFunction}, + {"SkyWorldCloud", &al::createActorFunction}, + {"SkyWorldKoopaFire", &al::createActorFunction}, + {"SkyWorldKoopaFrame", &al::createActorFunction}, + {"SkyWorldMiddleViewCloud", &al::createActorFunction}, + {"SignBoard", &al::createActorFunction}, + {"SnowWorldBigIcicle", &al::createActorFunction}, + {"SnowWorldSequenceFlagCheckObj", &al::createActorFunction}, + {"Sky", &al::createActorFunction}, + {"SmallWanderBoss", &al::createActorFunction}, + {"SneakingMan", &al::createActorFunction}, + {"SnowManRaceNpc", &al::createActorFunction}, + {"SnowVolume", &al::createActorFunction}, + {"SnowVolumeEraser", &al::createActorFunction}, + {"Souvenir", &al::createActorFunction}, + {"SouvenirDirector", &al::createActorFunction}, + {"Special2KeyMoveLift", &al::createActorFunction}, + {"Special2KeyMoveParts", &al::createActorFunction}, + {"SphinxQuiz", &al::createActorFunction}, + {"SphinxRide", &al::createActorFunction}, + {"SphinxTaxiWatcher", &al::createActorFunction}, + {"Squirrel", &al::createActorFunction}, + {"Stacker", &al::createActorFunction}, + {"StackerCapWorldCtrl", &al::createActorFunction}, + {"StageEventDemo", &al::createActorFunction}, + {"StageSwitchSelector", &al::createActorFunction}, + {"StageTalkDemoNpcCap", &al::createActorFunction}, + {"StageTalkDemoNpcCapMoonRock", &al::createActorFunction}, + {"Stake", &al::createActorFunction}, + {"Statue", &al::createActorFunction}, + {"StatueSnapMark", &al::createActorFunction}, + {"SubActorLodFixPartsScenarioAction", &al::createActorFunction}, + {"SwitchAnd", &al::createActorFunction}, + {"SwitchKeyMoveMapParts", &al::createActorFunction}, + {"TalkMessageInfoPoint", &al::createActorFunction}, + {"TalkMessageInfoPointSaveObj", &al::createActorFunction}, + {"TalkNpc", &al::createActorFunction}, + {"TalkNpcFreeze", &al::createActorFunction}, + {"TalkNpcCapMan", &al::createActorFunction}, + {"TalkNpcCapManHero", &al::createActorFunction}, + {"TalkNpcCityMan", &al::createActorFunction}, + {"TalkNpcCityManLow", &al::createActorFunction}, + {"TalkNpcCityManSit", &al::createActorFunction}, + {"TalkNpcCityMayor", &al::createActorFunction}, + {"TalkNpcCollectBgm", &al::createActorFunction}, + {"TalkNpcDesertMan", &al::createActorFunction}, + {"TalkNpcForestMan", &al::createActorFunction}, + {"TalkNpcForestManScrap", &al::createActorFunction}, + {"TalkNpcKinopio", &al::createActorFunction}, + {"TalkNpcKinopioBrigade", &al::createActorFunction}, + {"TalkNpcKinopioMember", &al::createActorFunction}, + {"TalkNpcLakeMan", &al::createActorFunction}, + {"TalkNpcLavaMan", &al::createActorFunction}, + {"TalkNpcLavaManCook", &al::createActorFunction}, + {"TalkNpcLifeUpItemSeller", &al::createActorFunction}, + {"TalkNpcRabbit", &al::createActorFunction}, + {"TalkNpcSeaMan", &al::createActorFunction}, + {"TalkNpcSnowMan", &al::createActorFunction}, + {"TalkNpcSnowManLeader", &al::createActorFunction}, + {"TalkNpcSnowManRacer", &al::createActorFunction}, + {"TalkPoint", &al::createActorFunction}, + {"Tank", &al::createActorFunction}, + {"TankReviveCtrl", &al::createActorFunction}, + {"TaxiStop", &al::createActorFunction}, + {"TextureReplaceScreen", &al::createActorFunction}, + {"ThunderRenderRequester", &al::createActorFunction}, + {"Togezo", &al::createActorFunction}, + {"Togezo2D", &al::createActorFunction}, + {"TokimekiMayorNpc", &al::createActorFunction}, + {"TrampleBush", &al::createActorFunction}, + {"TrampleSwitch", &al::createActorFunction}, + {"TrampleSwitchSave", &al::createActorFunction}, + {"TrampleSwitchTimer", &al::createActorFunction}, + {"TransparentWall", &al::createActorFunction}, + {"TreasureBox", &al::createActorFunction}, + {"TreasureBoxKey", &al::createActorFunction}, + {"TreasureBoxSequentialDirector", &al::createActorFunction}, + {"TRex", &al::createActorFunction}, + {"TRexForceScroll", &al::createActorFunction}, + {"TRexPatrol", &al::createActorFunction}, + {"TRexSleep", &al::createActorFunction}, + {"TRexScrollBreakMapParts", &al::createActorFunction}, + {"Tsukkun", &al::createActorFunction}, + {"TsukkunHole", &al::createActorFunction}, + {"TwistChainList", &al::createActorFunction}, + {"Utsubo", &al::createActorFunction}, + {"UtsuboWatcher", &al::createActorFunction}, + {"VocalMike", &al::createActorFunction}, + {"VolleyballBase", &al::createActorFunction}, + {"VolleyballNet", &al::createActorFunction}, + {"VolleyballNpc", &al::createActorFunction}, + {"Wanwan", &al::createActorFunction}, + {"WanwanHole", &al::createActorFunction}, + {"WaterAreaMoveModel", &al::createActorFunction}, + {"WaterfallWorldBigBreakableWall", &al::createActorFunction}, + {"WaterfallWorldFallDownBridge", &al::createActorFunction}, + {"WaterfallWorldHomeCage", &al::createActorFunction}, + {"WaterfallWorldWaterfall", &al::createActorFunction}, + {"WaterRoad", &al::createActorFunction}, + {"WeightSwitch", &al::createActorFunction}, + {"WheelWaveSurfParts", &al::createActorFunction}, + {"WindBlowPuzzle", &al::createActorFunction}, + {"WorldMapEarth", &al::createActorFunction}, + {"WorldTravelingNpc", &al::createActorFunction}, + {"WorldTravelingPeach", &al::createActorFunction}, + {"WorldWarpHole", &al::createActorFunction}, + {"Fastener", &al::createActorFunction}, + {"FastenerObj", &al::createActorFunction}, + {"AtmosScatterRequester", &al::createActorFunction}, + {"BackHideParts", &al::createActorFunction}, + {"BreakMapParts", &al::createActorFunction}, + {"CapRotateMapParts", &al::createActorFunction}, + {"ClockMapParts", &al::createActorFunction}, + {"ConveyerMapParts", &al::createActorFunction}, + {"FallMapParts", &al::createActorFunction}, + {"FixMapParts", &al::createActorFunction}, + {"FloaterMapParts", &al::createActorFunction}, + {"FlowMapParts", &al::createActorFunction}, + {"GateMapParts", &al::createActorFunction}, + {"KeyMoveMapParts", &al::createActorFunction}, + {"KeyMoveMapPartsGenerator", &al::createActorFunction}, + {"PossessedMapParts", &al::createActorFunction}, + {"Pukupuku", &al::createActorFunction}, + {"PulseSwitch", &al::createActorFunction}, + {"RailCollision", &al::createActorFunction}, + {"RailMoveMapParts", &al::createActorFunction}, + {"RiseMapParts", &al::createActorFunction}, + {"ReactionMapParts", &al::createActorFunction}, + {"RiseMapPartsHolder", &al::createActorFunction}, + {"RocketFlower", &al::createActorFunction}, + {"RollingCubeMapParts", &al::createActorFunction}, + {"RippleFixMapParts", &al::createActorFunction}, + {"RotateMapParts", &al::createActorFunction}, + {"SeesawMapParts", &al::createActorFunction}, + {"SlideMapParts", &al::createActorFunction}, + {"SubActorLodMapParts", &al::createActorFunction}, + {"SurfMapParts", &al::createActorFunction}, + {"SwingMapParts", &al::createActorFunction}, + {"SwitchDitherMapParts", &al::createActorFunction}, + {"SwitchKeepOnWatcher", &al::createActorFunction}, + {"SwitchOpenMapParts", &al::createActorFunction}, + {"VisibleSwitchMapParts", &al::createActorFunction}, + {"WaveSurfMapParts", &al::createActorFunction}, + {"WheelMapParts", &al::createActorFunction}, + {"WobbleMapParts", &al::createActorFunction}, + {"WindBlowMapParts", &al::createActorFunction}, + {"Yoshi", &al::createActorFunction}, + {"YoshiFruit", &al::createActorFunction}, + {"YoshiFruitShineHolder", &al::createActorFunction}, + {"Yukimaru", &al::createActorFunction}, + {"YukimaruRacer", &al::createActorFunction}, + {"YukimaruRacerTiago", &al::createActorFunction} +}; \ No newline at end of file diff --git a/include/al/factory/CameraPoserFactory.h b/include/al/factory/CameraPoserFactory.h new file mode 100644 index 0000000..a6e68c5 --- /dev/null +++ b/include/al/factory/CameraPoserFactory.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Factory.h" + +namespace al { + + class CameraPoser; + + template + CameraPoser* createCameraPoserFunction(const char *name); + + typedef CameraPoser* (*createCameraPoser)(const char* name); + + class CameraPoserFactory : public Factory { + public: + CameraPoserFactory(const char *fName) __attribute__((noinline)) { + this->factoryName = fName; + this->actorTable = nullptr; + this->factoryCount = 0; + }; + + virtual CameraPoser *createEntranceCameraPoser(void) const; + // return new al::CameraPoserEntrance(スタート); + + int mLastUsedIndex = 0; + }; +} + +namespace alCameraPoserFactoryFunction { + void initAndCreateTableFromOtherTable2(al::CameraPoserFactory *,al::NameToCreator const*,int,al::NameToCreator const*,int); + void initAndCreateTableWithAnotherFactory(al::CameraPoserFactory *,al::CameraPoserFactory const*,al::NameToCreator const*,int); + void initAndCreateTableWithPresetPosers(al::CameraPoserFactory *,al::NameToCreator const*,int); +} + +namespace cc { + template + al::CameraPoser *createCustomCameraPoser(const char *name) + { + return new T(name); + }; +} \ No newline at end of file diff --git a/include/al/factory/CameraPoserFactoryEntries100.h b/include/al/factory/CameraPoserFactoryEntries100.h new file mode 100644 index 0000000..b0368ca --- /dev/null +++ b/include/al/factory/CameraPoserFactoryEntries100.h @@ -0,0 +1,70 @@ +#pragma once + +#include "CameraPoserFactory.h" +#include "al/factory/Factory.h" + +#include "cameras/CameraPoserCustom.h" + +class CameraPoserFollowLimit; +class ScenarioStartCameraPoserSimpleZoom; +class ScenarioStartCameraPoserRailMove; + +namespace al { + class CameraPoserFix; + class CameraPoserFixPoint; + class CameraPoserRace; + class CameraPoserRailMoveLookAt; + class CameraPoserKinopioBrigade; + class CameraPoserTalk; + class CameraPoserRailMoveMovie; + class CameraPoserBossBattle; + class CameraPoserEntrance; + class CameraPoserLookBoard; + class CameraPoserLookDown; + class CameraPoserSubjective; + class CameraPoserTower; + class KeyMoveCameraFix; + class KeyMoveCameraRailMove; + class KeyMoveCameraZoom; + class CameraPoserFix; + class CameraPoserFix; + class CameraPoserFix; + class CameraPoserFix; + class CameraPoserFix; + class CameraPoserFix; + class CameraPoserFix; +} +// 0xE in size +static al::NameToCreator poserEntries[] = { + {"制限付きフォロー", &al::createCameraPoserFunction}, + {"制限付き平行", &al::createCameraPoserFunction}, + {"2D平行", &al::createCameraPoserFunction}, + {"固定", &al::createCameraPoserFunction}, + {"完全固定", &al::createCameraPoserFunction}, + {"出入口専用固定", &al::createCameraPoserFunction}, + {"定点", &al::createCameraPoserFunction}, + {"その場定点", &al::createCameraPoserFunction}, + {"完全追従定点", &al::createCameraPoserFunction}, + {"レース", &al::createCameraPoserFunction}, + {"レール移動", &al::createCameraPoserFunction}, + {"キノピオ探検隊", &al::createCameraPoserFunction}, + {"会話用2点間", &al::createCameraPoserFunction}, + {"映像撮影レール", &al::createCameraPoserFunction}, + // Custom Posers + {"CameraPoserCustom", &cc::createCustomCameraPoser} // al::CameraPoserFollowSimple +}; + +// 0xB in size +static al::NameToCreator poserEntries2[] = { + {"ボス戦カメラ", &al::createCameraPoserFunction}, + {"スタート", &al::createCameraPoserFunction}, + {"看板用2点間", &al::createCameraPoserFunction}, + {"見下ろし", &al::createCameraPoserFunction}, + {"主観", &al::createCameraPoserFunction}, + {"塔", &al::createCameraPoserFunction}, + {"キー移動固定", &al::createCameraPoserFunction}, + {"キー移動レール移動", &al::createCameraPoserFunction}, + {"キー移動ズーム", &al::createCameraPoserFunction}, + {"シナリオ紹介シンプルズームカメラ", &al::createCameraPoserFunction}, + {"シナリオ紹介レール移動カメラ", &al::createCameraPoserFunction}, +}; \ No newline at end of file diff --git a/include/al/factory/Factory.h b/include/al/factory/Factory.h new file mode 100644 index 0000000..40c91e8 --- /dev/null +++ b/include/al/factory/Factory.h @@ -0,0 +1,42 @@ +#pragma once + +#include "types.h" +#include "al/util.hpp" +#include "logger.hpp" + +namespace al +{ + template + struct NameToCreator + { + const char* creatorName; + T createActorFunction; + }; + + template + class Factory { + public: + inline Factory() {}; + + virtual const char * convertName(char const *name) const { + return name; + }; + + inline T getCreator(const char *name) { + for (size_t i = 0; i < this->factoryCount; i++) + { + if(isEqualString(this->actorTable[i].creatorName, name)) { + return this->actorTable[i].createActorFunction; + } + } + return nullptr; + }; + + protected: + // 0x0 is vtable + const char *factoryName; // 0x8 + const al::NameToCreator *actorTable; // 0x10 + int factoryCount; // 0x18 + }; + +} // namespace al diff --git a/include/al/factory/ProjectActorFactory.h b/include/al/factory/ProjectActorFactory.h new file mode 100644 index 0000000..dacf869 --- /dev/null +++ b/include/al/factory/ProjectActorFactory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ActorFactory.h" +#include "ActorFactoryEntries100.h" + +class ProjectActorFactory : public al::ActorFactory { + public: + ProjectActorFactory(); +}; diff --git a/include/al/factory/ProjectCameraPoserFactory.h b/include/al/factory/ProjectCameraPoserFactory.h new file mode 100644 index 0000000..e2f39aa --- /dev/null +++ b/include/al/factory/ProjectCameraPoserFactory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "CameraPoserFactoryEntries100.h" +#include "CameraPoserFactory.h" + +class ProjectCameraPoserFactory : public al::CameraPoserFactory { + public: + ProjectCameraPoserFactory(); +}; diff --git a/include/al/gamepad/util.h b/include/al/gamepad/util.h new file mode 100644 index 0000000..a0c62aa --- /dev/null +++ b/include/al/gamepad/util.h @@ -0,0 +1,9 @@ +#pragma once + +namespace al { + class GamePadSystem { + public: + void changeSinglePlayMode(void); + void changeMultiPlayMode(int, int); + }; +} \ No newline at end of file diff --git a/include/al/hio/HioNode.h b/include/al/hio/HioNode.h new file mode 100644 index 0000000..dbd2452 --- /dev/null +++ b/include/al/hio/HioNode.h @@ -0,0 +1,16 @@ +#pragma once + +namespace al +{ + class IUseHioNode + { + public: + // ?? + }; + + class HioNode : public IUseHioNode + { + public: + // ?? + }; +}; \ No newline at end of file diff --git a/include/al/layout/BalloonMessage.h b/include/al/layout/BalloonMessage.h new file mode 100644 index 0000000..cbcad40 --- /dev/null +++ b/include/al/layout/BalloonMessage.h @@ -0,0 +1,68 @@ +#pragma once + +#include "LayoutActor.h" +#include "TalkMessageVoicePlayer.h" + +namespace al { + + struct BalloonMessageInitParam { + const char *mActorName; // 0x0 + const char *mLayoutName; // 0x8 + const char *mPaneName; // 0x10 + const char *mMessage; // 0x18 + float mStartDist; // 0x20 + float mEndDist; // 0x24 + const char *mGroupName; // 0x28 + float mActorOffset; // 0x30 + int mPlayerIndex; // 0x34 + int mIsUseSub; // 0x38 + bool mIsHideIfNotVis; // 0x3C + }; + + class BalloonMessage : public LayoutActor { + public: + BalloonMessage(const al::LiveActor*, const al::LayoutInitInfo&, + const al::BalloonMessageInitParam&, bool isUseDistance); + + void appear(void) override; + void control(void) override; + void updateTrans(void); + void hidePushA(void); + void appearWithPushA(void); + void showPushA(void); + void update(void); + void end(void); + void setText(char const*); + void setTextW(char16_t const*); + + bool isEnableAppear(void) const; + bool isWait(void) const; + bool isVoicePlayerPlaying(void) const; + bool isShowPushA(void) const; + bool isEnableEnd(void) const; + bool isNearPlayerActor(float) const; + + void exeAppear(void); + void exeWait(void); + void exeEnd(void); + void exeHide(void); + + const LiveActor *mTargetActor; // 0x130 + const char *mPaneName; // 0x138 + float mMinDist; // 0x140 + float mMaxDist; // 0x144 + sead::Vector3f mTargetOffset = sead::Vector3f::zero; // 0x148 + int mPlayerIndex; // 0x154 + int mIsUseSub; // 0x158 + bool mIsUseDistance; // 0x15C + bool mIsHideIfNotVis; // 0x15D + bool mIsUseVoice; // 0x15E + TalkMessageVoicePlayer* mTalkVoicePlayer = nullptr; // 0x160 + sead::FixedSafeString<0x40> mTalkMsgSe; // 0x168 + }; + + BalloonMessage *createBalloonMessage(al::LiveActor const*,al::ActorInitInfo const&); + BalloonMessage *createBalloonMessage(al::LiveActor const*,al::ActorInitInfo const&, char const*); + BalloonMessage *createBalloonMessage(al::LiveActor const*,al::ActorInitInfo const&,al::BalloonMessageInitParam const&); + BalloonMessage *createBalloonMessageNoAutoUpdate(al::LiveActor const*,al::ActorInitInfo const&,al::BalloonMessageInitParam const&); +} \ No newline at end of file diff --git a/include/al/layout/CoinCounter.h b/include/al/layout/CoinCounter.h new file mode 100644 index 0000000..003b4d4 --- /dev/null +++ b/include/al/layout/CoinCounter.h @@ -0,0 +1,9 @@ +#pragma once + +/* + * VTable Loc: 1CC3170 + */ + +#include "al/layout/LayoutActor.h" + +class CoinCounter : public al::LayoutActor {}; \ No newline at end of file diff --git a/include/al/layout/HtmlViewer.h b/include/al/layout/HtmlViewer.h new file mode 100644 index 0000000..7a976bb --- /dev/null +++ b/include/al/layout/HtmlViewer.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace al { +class HtmlViewer { +public: + void call(const char*, sead::BufferedSafeStringBase*) const; +}; +}; // namespace al \ No newline at end of file diff --git a/include/al/layout/IUseLayout.h b/include/al/layout/IUseLayout.h new file mode 100644 index 0000000..9e609d5 --- /dev/null +++ b/include/al/layout/IUseLayout.h @@ -0,0 +1,18 @@ +/** + * @file IUseLayout.h + * @brief Interface for classes that are layouts. + */ + +#pragma once + +#include "al/actor/IUseName.h" + +namespace al { + +struct LayoutKeeper; + +class IUseLayout : virtual public al::IUseName { + public: + virtual al::LayoutKeeper* getLayoutKeeper(void) const = 0; +}; +} // namespace al \ No newline at end of file diff --git a/include/al/layout/KeyRepeatCtrl.h b/include/al/layout/KeyRepeatCtrl.h new file mode 100644 index 0000000..05da84b --- /dev/null +++ b/include/al/layout/KeyRepeatCtrl.h @@ -0,0 +1,17 @@ +#pragma once + +namespace al +{ + struct KeyRepeatCtrl { + KeyRepeatCtrl(void); + void init(int, int); + void update(bool isUp, bool isDown); + void reset(void); + bool isUp(void) const; + bool isDown(void) const; + + int maxIndex = 0; + int frameTime = 0; + bool isNoMove = true; + }; +} // namespace al diff --git a/include/al/layout/LayoutActor.h b/include/al/layout/LayoutActor.h new file mode 100644 index 0000000..90fe054 --- /dev/null +++ b/include/al/layout/LayoutActor.h @@ -0,0 +1,87 @@ +#pragma once + +#include "al/actor/IUseName.h" +#include "al/audio/AudioKeeper.h" +#include "al/camera/CameraDirector.h" +#include "al/effect/EffectKeeper.h" +#include "al/hio/HioNode.h" +#include "al/layout/IUseLayout.h" +#include "al/message/IUseMessageSystem.h" +#include "al/nerve/Nerve.h" +#include "al/nerve/NerveKeeper.h" +#include "al/scene/SceneObjHolder.h" +#include "sead/prim/seadSafeString.h" +#include "LayoutSceneInfo.h" + +#include "al/LiveActor/LiveActor.h" + +namespace al { + +class LayoutActionKeeper; + +class IUseLayoutAction : virtual public IUseName { + public: + virtual al::LayoutActionKeeper* getLayoutActionKeeper() const = 0; +}; + +class LayoutTextPaneAnimator {}; + +class LayoutExecuteInfo {}; + +class HitReactionKeeper {}; + //: public al::IUseNerve, public al::IUseEffectKeeper, public al::IUseAudioKeeper, public al::IUseStageSwitch, public al::IUseSceneObjHolder, public al::IUseAreaObj, public al::IUseCamera, public al::IUseCollision, public al::IUseRail, public al::IUseHioNode + //: public al::IUseStageSwitch, public al::IUseAreaObj, public al::IUseCollision, public al::IUseRail, +class LayoutActor : public al::IUseHioNode, public al::IUseNerve, public al::IUseLayout, public al::IUseLayoutAction, public al::IUseMessageSystem, public al::IUseCamera, public al::IUseAudioKeeper, public al::IUseEffectKeeper, public al::IUseSceneObjHolder +{ + public: + LayoutActor(char const*); + + virtual al::NerveKeeper* getNerveKeeper(void) const {return mNerveKeeper;} + + virtual void appear(); + virtual void kill(); + virtual void movement(); + virtual void calcAnim(bool); + + virtual const char *getName(void) const {return mName.cstr();} + virtual al::EffectKeeper *getEffectKeeper(void) const {return mEffectKeeper;} + virtual al::AudioKeeper *getAudioKeeper(void) const {return mAudioKeeper;} + virtual al::LayoutActionKeeper *getLayoutActionKeeper(void) const {return mLytActionKeeper;} + virtual al::LayoutKeeper* getLayoutKeeper(void) const {return mLytKeeper;} + + void initLayoutKeeper(al::LayoutKeeper *); + void initActionKeeper(void); + void initTextPaneAnimator(al::LayoutTextPaneAnimator *); + void initExecuteInfo(al::LayoutExecuteInfo *); + void initHitReactionKeeper(al::HitReactionKeeper *); + void initSceneInfo(al::LayoutSceneInfo *); + void initLayoutPartsActorKeeper(int); + void initEffectKeeper(al::EffectKeeper *); + void initAudioKeeper(al::AudioKeeper *); + void initNerve(al::Nerve const*, int); + void setMainGroupName(char const*); + void syncAction(); + + virtual al::CameraDirector *getCameraDirector(void) const {return mLytSceneInfo->mCameraDirector;} + virtual al::SceneObjHolder *getSceneObjHolder(void) const {return mLytSceneInfo->mSceneObjHolder;} + virtual al::MessageSystem* getMessageSystem(void) const {return mLytSceneInfo->mMessageSystem;} + + virtual void control(); + + sead::FixedSafeString<0x80> mName; // 0x40 + NerveKeeper *mNerveKeeper; // 0xD8 + LayoutKeeper *mLytKeeper; // 0xE0 + LayoutActionKeeper *mLytActionKeeper; // 0xE8 + LayoutTextPaneAnimator *mTextPaneAnimator; // 0xF0 + EffectKeeper *mEffectKeeper; // 0xF8 + AudioKeeper *mAudioKeeper; // 0x100 + LayoutExecuteInfo *mExecuteInfo; // 0x108 + HitReactionKeeper *mHitReactionKeeper; // 0x110 + LayoutSceneInfo *mLytSceneInfo; // 0x118 + struct LayoutPartsActorKeeper *mLytPartsActorKeeper; // 0x120 + bool mIsAlive; // 0x128 +}; +} // namespace al + +static_assert(sizeof(al::LayoutActor) == 0x130); + diff --git a/include/al/layout/LayoutInitInfo.h b/include/al/layout/LayoutInitInfo.h new file mode 100644 index 0000000..319f0b7 --- /dev/null +++ b/include/al/layout/LayoutInitInfo.h @@ -0,0 +1,34 @@ +#pragma once + +#include "al/audio/AudioDirector.h" +#include "al/layout/LayoutSceneInfo.h" +#include "al/message/MessageSystem.h" +#include "al/rumble/PadRumbleDirector.h" +#include "al/camera/CameraDirector.h" +#include "al/execute/ExecuteDirector.h" +#include "al/layout/LayoutKit.h" +#include "game/System/GameSystemInfo.h" +#include "al/effect/EffectSystemInfo.h" +#include "al/scene/Scene.h" + +namespace al { + + class LayoutInitInfo : public LayoutSceneInfo { + public: + void init(al::ExecuteDirector*, al::EffectSystemInfo const*, al::SceneObjHolder*, + al::AudioDirector const*, al::CameraDirector*, al::LayoutSystem const*, + al::MessageSystem const*, al::GamePadSystem const*, al::PadRumbleDirector*); + + al::MessageSystem *getMessageSystem(void) const; + + void *qword30; + void *qword38; + void *qword40; + al::ExecuteDirector *mExecuteDirector; + al::EffectSystemInfo *mEffectSysInfo; + al::AudioDirector *mAudioDirector; + al::LayoutSystem *mLayoutSystem; + }; + void initLayoutInitInfo(al::LayoutInitInfo *,al::Scene const*,al::SceneInitInfo const&); + void initLayoutInitInfo(al::LayoutInitInfo *,al::LayoutKit const*,al::SceneObjHolder *,al::AudioDirector const*,al::LayoutSystem const*,al::MessageSystem const*,al::GamePadSystem const*); +} // namespace al diff --git a/include/al/layout/LayoutKit.h b/include/al/layout/LayoutKit.h new file mode 100644 index 0000000..4cda4f6 --- /dev/null +++ b/include/al/layout/LayoutKit.h @@ -0,0 +1,13 @@ +/** + * @file IUseLayout.h + * @brief Interface for classes that are layouts. + */ + +#pragma once + +namespace al { + class LayoutKit { + public: + + }; +} \ No newline at end of file diff --git a/include/al/layout/LayoutSceneInfo.h b/include/al/layout/LayoutSceneInfo.h new file mode 100644 index 0000000..61dc518 --- /dev/null +++ b/include/al/layout/LayoutSceneInfo.h @@ -0,0 +1,19 @@ +#pragma once + +#include "al/camera/CameraDirector.h" +#include "al/message/IUseMessageSystem.h" +#include "al/rumble/PadRumbleDirector.h" +#include "al/scene/SceneObjHolder.h" +#include "game/System/GameSystemInfo.h" + +namespace al { +class LayoutSceneInfo { + public: + void* gap; + al::CameraDirector *mCameraDirector; + al::PadRumbleDirector *mPadRumbleDirector; + al::SceneObjHolder *mSceneObjHolder; + al::MessageSystem* mMessageSystem; + al::GamePadSystem *mGamepadSystem; + }; +} diff --git a/include/al/layout/MenuSelectParts.h b/include/al/layout/MenuSelectParts.h new file mode 100644 index 0000000..c186b2d --- /dev/null +++ b/include/al/layout/MenuSelectParts.h @@ -0,0 +1,89 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/nerve/NerveExecutor.h" +#include "al/layout/KeyRepeatCtrl.h" +#include "al/util/LiveActorUtil.h" +#include "al/string/StringTmp.h" +#include "al/rumble/alPadRumbleFunction.h" +#include "al/rumble/PadRumbleParam.h" + +#include "al/util/NerveUtil.h" +#include "al/util/LayoutUtil.h" +#include "al/util/MathUtil.h" + +#include "game/GameData/GameDataHolderAccessor.h" + +#include "rs/util/SceneUtil.h" +#include "rs/util/UIUtil.h" + +#include + +/* +0 Continue +1 SeparatePlay +2 NewGame +3 Help +4 Save +5 Setting +*/ + +class MenuSelectParts : public al::NerveExecutor { + public: + MenuSelectParts(char const*,al::LayoutActor *,al::LiveActor *,al::LayoutInitInfo const&,int); + void update(void); + void appear(int); + void startActionPartsIllustSelectIndex(void); + void appearWait(void); + void setSelectMessage(int,char16_t const*); + bool isDecideContinue(void) const; + bool isDecideEnd(void) const; + bool isSelectContinue(void) const; + bool isDecideSetting(void) const; + bool isSelectSetting(void) const; + bool isDecideSave(void) const; + bool isSelectSave(void) const; + bool isDecideSeparatePlay(void) const; + bool isSelectSeparatePlay(void) const; + bool isDecideHelp(void) const; + bool isSelectHelp(void) const; + bool isDecideNewGame(void) const; + bool isSelectNewGame(void) const; + int calcPartsIndex(int) const; + void exeHide(void); + void exeAppear(void); + void startActionMarioSelectIndex(void); + void exeSelect(void); + void startActionMario(al::LiveActor *, const char *); + void exeDecideParts(void); + bool isInvalidSelect(void) const; + void exeDecideInterval(void); + void exeDecideEnd(void); + ~MenuSelectParts(); + + // void *vtable; + // void *unk1; // 0x08 + al::LayoutActor *mRootLayout; // 0x10 + int mMaxSelectParts; // 0x18 + int mCursorItemIdx; // 0x1C + sead::PtrArray mSelectParts; // 0x20-0x28 + al::LayoutActor *mCursorActor; // 0x30 + void *unk3; // 0x38 + al::LiveActor *mMarioHigh; // 0x40 + al::LiveActor *mCapEyes; // 0x48 + al::KeyRepeatCtrl *mKeyRepeatCtrl; // 0x50 + bool isMenuMain; // 0x58 +}; + +namespace +{ + NERVE_HEADER(MenuSelectParts, Hide) + NERVE_HEADER(MenuSelectParts, Appear) + NERVE_HEADER(MenuSelectParts, Select) + NERVE_HEADER(MenuSelectParts, DecideEnd) + NERVE_HEADER(MenuSelectParts, DecideParts) + NERVE_HEADER(MenuSelectParts, SelectSecond) + NERVE_HEADER(MenuSelectParts, DecideInterval) +} // namespace diff --git a/include/al/layout/SimpleLayoutAppearWaitEnd.h b/include/al/layout/SimpleLayoutAppearWaitEnd.h new file mode 100644 index 0000000..fc84300 --- /dev/null +++ b/include/al/layout/SimpleLayoutAppearWaitEnd.h @@ -0,0 +1,24 @@ +#pragma once + +#include "al/layout/LayoutActor.h" +namespace al { +class SimpleLayoutAppearWaitEnd : public al::LayoutActor { +public: + SimpleLayoutAppearWaitEnd(char const*, char const*, al::LayoutInitInfo const&, char const*, + bool); + + SimpleLayoutAppearWaitEnd(al::LayoutActor*, char const*, char const*, al::LayoutInitInfo const&, + char const*); + + void appear(void); + void end(void); + void startWait(void); + + void exeAppear(void); + void exeWait(void); + void exeEnd(void); + bool isWait(void) const; + bool isAppearOrWait(void) const; + +}; +} \ No newline at end of file diff --git a/include/al/layout/TalkMessageVoicePlayer.h b/include/al/layout/TalkMessageVoicePlayer.h new file mode 100644 index 0000000..db07e5e --- /dev/null +++ b/include/al/layout/TalkMessageVoicePlayer.h @@ -0,0 +1,19 @@ +#pragma once + +#include "al/audio/AudioKeeper.h" +#include "al/message/IUseMessageSystem.h" + +namespace al { +class TalkMessageVoicePlayer { +public: + TalkMessageVoicePlayer(); + + void start(al::IUseMessageSystem const*, al::IUseAudioKeeper const*, char16_t const*, int); + void stop(void); + void update(void); + void calcVoicePitch(int); + bool isPlaying(void) const; + + char size[0x440]; +}; +} \ No newline at end of file diff --git a/include/al/layout/WindowConfirm.h b/include/al/layout/WindowConfirm.h new file mode 100644 index 0000000..9e9f2cc --- /dev/null +++ b/include/al/layout/WindowConfirm.h @@ -0,0 +1,38 @@ +#pragma once + +#include "al/layout/LayoutActor.h" + +namespace al { +class WindowConfirm : public al::LayoutActor { +public: + + WindowConfirm(al::LayoutInitInfo const&, char const*, char const*); + + void appear(void); + void appearWithChoicingCancel(void); + void exeAppear(void); + void exeDecide(void); + void exeDecideAfter(void); + void exeEnd(void); + void exeHide(void); + void exeWait(void); + void isEnableInput(void); + void isNerveEnd(void); + void setCancelIdx(int); + void setCursorToPane(void); + void setListNum(int); + void setTxtList(int,char16_t const*); + void setTxtMessage(char16_t const*); + void tryCancel(void); + void tryCancelWithoutEnd(void); + void tryDecide(void); + void tryDecideWithoutEnd(void); + void tryDown(void); + void tryEnd(void); + void tryUp(void); + + char size[0x38]; + + +}; +} \ No newline at end of file diff --git a/include/al/layout/WindowConfirmWait.h b/include/al/layout/WindowConfirmWait.h new file mode 100644 index 0000000..7aa59ab --- /dev/null +++ b/include/al/layout/WindowConfirmWait.h @@ -0,0 +1,56 @@ +#pragma once + +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/util/NerveUtil.h" + +namespace al { + +class WindowConfirmWait : public al::LayoutActor { + public: + WindowConfirmWait(char const*, char const*, al::LayoutInitInfo const&); + + void setTxtMessage(char16_t const*); + void setTxtMessageConfirm(char16_t const*); + + void appear(void); + bool tryEnd(void); + bool tryEndForce(void); + void playLoop(void); + void endLoop(void); + void tryPageIn(void); + void tryPageOut(void); + void showPaneConfirm(void); + void tryConfirmDecide(void); + void updateHardKey(void); + + void exeHide(void); + void exeAppear(void); + void exeKeepWait(void); + void exeWait(void); + void exeWaitEnd(void); + void exeEnd(void); + void exePageIn(void); + void exePageOut(void); + void exePageOutEnd(void); + void exeConfirmDecide(void); + + al::LayoutActor *mPartsHardKey; + + }; +} // namespace al + +namespace { + NERVE_HEADER(WindowConfirmWait, Hide) + NERVE_HEADER(WindowConfirmWait, Appear) + NERVE_HEADER(WindowConfirmWait, KeepWait) + NERVE_HEADER(WindowConfirmWait, Wait) + NERVE_HEADER(WindowConfirmWait, WaitEnd) + NERVE_HEADER(WindowConfirmWait, End) + NERVE_HEADER(WindowConfirmWait, PageIn) + NERVE_HEADER(WindowConfirmWait, PageOut) + NERVE_HEADER(WindowConfirmWait, PageOutEnd) + NERVE_HEADER(WindowConfirmWait, ConfirmDecide) +} // namespace + +static_assert(sizeof(al::WindowConfirmWait) == 0x138, "Size of WindowConfirmWait"); \ No newline at end of file diff --git a/include/al/message/IUseMessageSystem.h b/include/al/message/IUseMessageSystem.h new file mode 100644 index 0000000..6d38ae0 --- /dev/null +++ b/include/al/message/IUseMessageSystem.h @@ -0,0 +1,10 @@ +#pragma once + +#include "MessageSystem.h" + +namespace al { + class IUseMessageSystem { + public: + virtual al::MessageSystem *getMessageSystem() const = 0; + }; +} \ No newline at end of file diff --git a/include/al/message/MessageSystem.h b/include/al/message/MessageSystem.h new file mode 100644 index 0000000..50a1972 --- /dev/null +++ b/include/al/message/MessageSystem.h @@ -0,0 +1,7 @@ +#pragma once + +namespace al { + class MessageSystem { + + }; +} \ No newline at end of file diff --git a/include/al/nerve/ActorStateBase.h b/include/al/nerve/ActorStateBase.h new file mode 100644 index 0000000..149860f --- /dev/null +++ b/include/al/nerve/ActorStateBase.h @@ -0,0 +1,14 @@ +#pragma once + +#include "NerveStateBase.h" +#include "al/LiveActor/LiveActor.h" + +namespace al { + class ActorStateBase : public al::NerveStateBase { + public: + ActorStateBase(const char*, al::LiveActor*); + + protected: + LiveActor* mLiveActor; + }; +} \ No newline at end of file diff --git a/include/al/nerve/HostStateBase.h b/include/al/nerve/HostStateBase.h new file mode 100644 index 0000000..0b1a772 --- /dev/null +++ b/include/al/nerve/HostStateBase.h @@ -0,0 +1,13 @@ +#pragma once + +#include "NerveStateBase.h" + +namespace al +{ + template + class HostStateBase : public NerveStateBase { + public: + HostStateBase(const char* name, T *host) : NerveStateBase(name), mHost(host){}; + T *mHost; + }; +} // namespace al diff --git a/include/al/nerve/Nerve.h b/include/al/nerve/Nerve.h new file mode 100644 index 0000000..73c228d --- /dev/null +++ b/include/al/nerve/Nerve.h @@ -0,0 +1,25 @@ +#pragma once + +namespace al +{ + class NerveKeeper; + + class IUseNerve + { + public: + + inline IUseNerve() + { + + } + + virtual NerveKeeper* getNerveKeeper() const = 0; + }; + + class Nerve + { + public: + virtual void execute(NerveKeeper *) = 0; + virtual void executeOnEnd(NerveKeeper *) const; + }; +}; \ No newline at end of file diff --git a/include/al/nerve/NerveAction.h b/include/al/nerve/NerveAction.h new file mode 100644 index 0000000..b2eaa0b --- /dev/null +++ b/include/al/nerve/NerveAction.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Nerve.h" + +namespace al +{ + class NerveAction : public al::Nerve + { + public: + NerveAction(); + + virtual const char* getActionName() const = 0; + + al::NerveAction* _8; + }; +}; + +namespace alNerveFunction +{ + class NerveActionCollector + { + public: + NerveActionCollector(); + + void addNerve(al::NerveAction *); + + int mActionCount; // _0 + int _4; + al::NerveAction* _8; + al::NerveAction* _10; + + static alNerveFunction::NerveActionCollector* sCurrentCollector; + }; +}; \ No newline at end of file diff --git a/include/al/nerve/NerveExecutor.h b/include/al/nerve/NerveExecutor.h new file mode 100644 index 0000000..f3e9057 --- /dev/null +++ b/include/al/nerve/NerveExecutor.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Nerve.h" +#include "NerveKeeper.h" + +namespace al +{ + class NerveExecutor : public IUseNerve + { + public: + NerveExecutor(const char *); + + virtual NerveKeeper* getNerveKeeper() const; + virtual ~NerveExecutor(); + + void initNerve(const al::Nerve *, int stateCount); + void updateNerve(); + + al::NerveKeeper* mKeeper; // _8 + }; +}; \ No newline at end of file diff --git a/include/al/nerve/NerveKeeper.h b/include/al/nerve/NerveKeeper.h new file mode 100644 index 0000000..be2efa8 --- /dev/null +++ b/include/al/nerve/NerveKeeper.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Nerve.h" + +namespace al +{ + class NerveStateCtrl; + + class NerveKeeper + { + public: + NerveKeeper(al::IUseNerve *, const al::Nerve *, int); + + void update(); + + void tryChangeNerve(); + void setNerve(const al::Nerve *); + const al::Nerve* getCurrentNerve() const; + + al::IUseNerve* mParent; // _0 + const al::Nerve* _8; + const al::Nerve* mNerve; // _10 + int mStep; // _18 + int _1C; + al::NerveStateCtrl* mStateCtrl; // _20 + unsigned long _28; + }; +}; \ No newline at end of file diff --git a/include/al/nerve/NerveStateBase.h b/include/al/nerve/NerveStateBase.h new file mode 100644 index 0000000..be04a9d --- /dev/null +++ b/include/al/nerve/NerveStateBase.h @@ -0,0 +1,21 @@ +#pragma once + +#include "NerveExecutor.h" + +namespace al +{ + class NerveStateBase : public NerveExecutor + { + public: + NerveStateBase(const char *); + + virtual ~NerveStateBase(); + virtual void init(); + virtual void appear(); + virtual void kill(); + virtual bool update(); + virtual void control(); + + bool mIsDead; // _10 + }; +}; \ No newline at end of file diff --git a/include/al/nerve/NerveStateCtrl.h b/include/al/nerve/NerveStateCtrl.h new file mode 100644 index 0000000..a448471 --- /dev/null +++ b/include/al/nerve/NerveStateCtrl.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Nerve.h" +#include "NerveStateBase.h" + +namespace al +{ + struct State + { + al::NerveStateBase* mStateBase; // _0 + const al::Nerve* mNerve; // _8 + const char* mName; // _10 + }; + + class NerveStateCtrl + { + public: + NerveStateCtrl(int); + + void addState(al::NerveStateBase *, const al::Nerve *, const char *); + bool updateCurrentState(); + void startState(const al::Nerve *); + void update(); + + State* findStateInfo(const al::Nerve *); + bool isCurrentStateEnd() const; + void tryEndCurrentState(); + + int _0; + int mStateCount; // _4 + State* mStates; // _8 + State* mCurrentState; // _10 + }; +}; \ No newline at end of file diff --git a/include/al/pose/ActorPoseKeeper.h b/include/al/pose/ActorPoseKeeper.h new file mode 100644 index 0000000..1d268e2 --- /dev/null +++ b/include/al/pose/ActorPoseKeeper.h @@ -0,0 +1,64 @@ +#pragma once + +#include "sead/math/seadMatrix.h" +#include "sead/math/seadQuat.h" +#include "sead/math/seadVector.h" + +namespace al +{ + class ActorPoseKeeperBase + { + public: + ActorPoseKeeperBase(); + + virtual const sead::Vector3& getRotate() const; + virtual const sead::Vector3& getScale() const; + virtual const sead::Vector3& getVelocity() const; + virtual const sead::Vector3& getFront() const; + virtual const sead::Vector3& getUp() const; + virtual const sead::Quat& getQuat() const; + virtual const sead::Vector3& getGravity() const; + virtual const sead::Matrix34& getMtx() const; + virtual sead::Vector3* getRotatePtr(); + virtual sead::Vector3* getScalePtr(); + virtual sead::Vector3* getVelocityPtr(); + virtual sead::Vector3* getFrontPtr(); + virtual sead::Vector3* getUpPtr(); + virtual sead::Quat* getQuatPtr(); + virtual sead::Vector3* getGravityPtr(); + virtual sead::Matrix34* getMtxPtr(); + virtual void updatePoseTrans(const sead::Vector3 &) = 0; + virtual void updatePoseRotate(const sead::Vector3 &) = 0; + virtual void updatePoseQuat(const sead::Quat &) = 0; + virtual void updatePoseMtx(const sead::Matrix34 *) = 0; + virtual void copyPose(const al::ActorPoseKeeperBase *); + virtual void calcBaseMtx(sead::Matrix34 *) = 0; + + sead::Vector3 mTranslation; // _8 + + static const sead::Vector3 sDefaultVelocity; + }; + + class ActorPoseKeeperTRSV : public ActorPoseKeeperBase + { + public: + virtual const sead::Vector3& getRotate() const; + virtual const sead::Vector3& getScale() const; + virtual const sead::Vector3& getVelocity() const; + + virtual sead::Vector3* getRotatePtr(); + virtual sead::Vector3* getScalePtr(); + virtual sead::Vector3* getVelocityPtr(); + + virtual void updatePoseTrans(const sead::Vector3 &); + virtual void updatePoseRotate(const sead::Vector3 &); + virtual void updatePoseQuat(const sead::Quat &); + virtual void updatePoseMtx(const sead::Matrix34 *); + + virtual void calcBaseMtx(sead::Matrix34 *); + + sead::Vector3 mRotation; // _14 + sead::Vector3 mScale; // _20 + sead::Vector3 mVelocity; // _2C + }; +}; \ No newline at end of file diff --git a/include/al/rail/RailKeeper.h b/include/al/rail/RailKeeper.h new file mode 100644 index 0000000..7d5d86b --- /dev/null +++ b/include/al/rail/RailKeeper.h @@ -0,0 +1,14 @@ +#pragma once + +#include "al/rail/RailRider.h" + +namespace al +{ + class RailKeeper + { + public: + virtual al::RailRider* getRailRider() const; + + al::RailRider* mRailRider; // _8 + }; +}; \ No newline at end of file diff --git a/include/al/rail/RailRider.h b/include/al/rail/RailRider.h new file mode 100644 index 0000000..150d17a --- /dev/null +++ b/include/al/rail/RailRider.h @@ -0,0 +1,12 @@ +#pragma once + +namespace al +{ + class RailRider; + + class IUseRail + { + public: + virtual al::RailRider* getRailRider() const = 0; + }; +}; \ No newline at end of file diff --git a/include/al/rumble/PadRumbleDirector.h b/include/al/rumble/PadRumbleDirector.h new file mode 100644 index 0000000..a45fc00 --- /dev/null +++ b/include/al/rumble/PadRumbleDirector.h @@ -0,0 +1,7 @@ +#pragma once + +namespace al { + class PadRumbleDirector { + public: + }; +} \ No newline at end of file diff --git a/include/al/rumble/PadRumbleParam.h b/include/al/rumble/PadRumbleParam.h new file mode 100644 index 0000000..51a1797 --- /dev/null +++ b/include/al/rumble/PadRumbleParam.h @@ -0,0 +1,15 @@ +#pragma once + +namespace al { + struct PadRumbleParam { + inline PadRumbleParam() = default; + float mRumbleNear = 0.0f; + float mRumbleFar = 3000.0f; + float mRumbleVolume = 1.0f; + float mRumblePitchVol = 1.0f; + float mRumblePitchLeft = 1.0f; + float mRumblePitchRight = 1.0f; + int unk = 0; + bool isUseController = false; + }; + } // namespace al \ No newline at end of file diff --git a/include/al/rumble/alPadRumbleFunction.h b/include/al/rumble/alPadRumbleFunction.h new file mode 100644 index 0000000..00d69e1 --- /dev/null +++ b/include/al/rumble/alPadRumbleFunction.h @@ -0,0 +1,52 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/layout/LayoutActor.h" +#include "PadRumbleParam.h" + +namespace al { + class PadRumbleDirector; +} + +namespace alPadRumbleFunction { + al::PadRumbleDirector *getPadRumbleDirector(al::LiveActor const*); + al::PadRumbleDirector *getPadRumbleDirector(al::LayoutActor const*); + void startPadRumble(al::PadRumbleDirector *,sead::Vector3 const&,char const*,float,float,int); + void startPadRumbleWithParam(al::PadRumbleDirector *,sead::Vector3 const&,char const*,al::PadRumbleParam const&,int); + void startPadRumble(al::LiveActor const*,char const*,float,float,int); + void startPadRumblePos(al::LiveActor const*,sead::Vector3 const&,char const*,float,float,int); + void startPadRumbleWithParam(al::LiveActor const*,sead::Vector3 const&,char const*,al::PadRumbleParam const&,int); + void startPadRumbleNo3D(al::PadRumbleDirector *,char const*,int); + void startPadRumbleNo3DWithParam(al::PadRumbleDirector *,char const*,al::PadRumbleParam const&,int); + void startPadRumbleNo3DWithParam(al::PadRumbleDirector *,char const*,float,float,float,float,int); + void startPadRumbleNo3D(al::LiveActor const*,char const*,int); + void startPadRumbleNo3DWithParam(al::LiveActor const*,char const*,al::PadRumbleParam const&,int); + void startPadRumbleNo3DWithParam(al::LiveActor const*,char const*,float,float,float,float,int); + void stopPadRumbleOneTime(al::PadRumbleDirector *,char const*,int); + void stopPadRumbleOneTime(al::LiveActor const*,char const*,int); + void startPadRumbleLoop(al::PadRumbleDirector *,char const*,sead::Vector3 const*,float,float,int); + void startPadRumbleLoopWithParam(al::PadRumbleDirector *,char const*,sead::Vector3 const*,al::PadRumbleParam const&,int); + void startPadRumbleLoop(al::LiveActor const*,char const*,sead::Vector3 const*,float,float,int); + void startPadRumbleLoopWithParam(al::LiveActor const*,char const*,sead::Vector3 const*,al::PadRumbleParam const&,int); + void startPadRumbleLoopNo3D(al::PadRumbleDirector *,char const*,sead::Vector3 const*,int); + void startPadRumbleLoopNo3DWithParam(al::PadRumbleDirector *,char const*,sead::Vector3 const*,al::PadRumbleParam const&,int); + void startPadRumbleLoopNo3D(al::LiveActor const*,char const*,sead::Vector3 const*,int); + void startPadRumbleLoopNo3DWithParam(al::LiveActor const*,char const*,sead::Vector3 const*,al::PadRumbleParam const&,int); + void stopPadRumbleLoop(al::PadRumbleDirector *,char const*,sead::Vector3 const*,int); + void stopPadRumbleLoop(al::LiveActor const*,char const*,sead::Vector3 const*,int); + void checkIsAlivePadRumbleLoop(al::PadRumbleDirector *,char const*,sead::Vector3 const*,int); + void checkIsAlivePadRumbleLoop(al::LiveActor const*,char const*,sead::Vector3 const*,int); + void startPadRumbleLoopControlable(al::LiveActor const*,char const*,sead::Vector3 const*,int); + void changePadRumbleLoopVolmue(al::LiveActor const*,char const*,sead::Vector3 const*,float,float,int); + void changePadRumbleLoopVolmueEaseInRange(al::LiveActor const*,char const*,sead::Vector3 const*,float,float,float,float,float,int); + void changePadRumbleLoopPitch(al::LiveActor const*,char const*,sead::Vector3 const*,float,float,int); + void startPadRumbleDirectValue(al::LiveActor const*,float,float,float,float,float,float,int); + void stopPadRumbleDirectValue(al::LiveActor const*,int); + void startPadRumbleWithVolume(al::LiveActor const*,char const*,float,float,int); + void startPadRumbleWithVolume(al::PadRumbleDirector *,char const*,float,float,int); + void makePadRumbleParamNearFarVolume(al::PadRumbleParam *,float,float,float); + void makePadRumbleParamNearFarVolumeLR(al::PadRumbleParam *,float,float,float,float); + void makePadRumbleParamNearFarVolumePitch(al::PadRumbleParam *,float,float,float,float); + void makePadRumbleParamNearFarVolumePitchLR(al::PadRumbleParam *,float,float,float,float,float,float); + +}; \ No newline at end of file diff --git a/include/al/scene/ISceneObj.h b/include/al/scene/ISceneObj.h new file mode 100644 index 0000000..240fc44 --- /dev/null +++ b/include/al/scene/ISceneObj.h @@ -0,0 +1,12 @@ +#pragma once + + +namespace al { + class ISceneObj { + public: + virtual const char* getSceneObjName(void) = 0; + virtual ~ISceneObj(); + virtual void initAfterPlacementSceneObj(struct ActorInitInfo const&) = 0; + virtual void initSceneObj(void) = 0; + }; +} \ No newline at end of file diff --git a/include/al/scene/Scene.h b/include/al/scene/Scene.h new file mode 100644 index 0000000..78bdb24 --- /dev/null +++ b/include/al/scene/Scene.h @@ -0,0 +1,37 @@ +#pragma once + +#include "SceneInitInfo.h" +#include +#include "al/audio/AudioKeeper.h" +#include "al/camera/CameraDirector.h" +#include "al/scene/SceneObjHolder.h" + +namespace al +{ + + class GraphicsInitArg; + + class Scene : public al::NerveExecutor, public al::IUseAudioKeeper, public al::IUseCamera, public al::IUseSceneObjHolder + { + public: + Scene(const char *); + + virtual ~Scene(); + virtual void init(const al::SceneInitInfo &); + virtual void appear(); + virtual void kill(); + virtual void movement(); + virtual void control(); + virtual void drawMain(); + virtual void drawSub(); + virtual al::AudioKeeper* getAudioKeeper(); + virtual al::SceneObjHolder* getSceneObjHolder(); + virtual al::CameraDirector* getCameraDirector(); + + void initDrawSystemInfo(al::SceneInitInfo const&); + + void initLiveActorKitWithGraphics(al::GraphicsInitArg const &, al::SceneInitInfo const &, int, int, int); + + unsigned char _28[0xD8-0x28]; + }; +}; \ No newline at end of file diff --git a/include/al/scene/SceneCreator.h b/include/al/scene/SceneCreator.h new file mode 100644 index 0000000..dc7d41d --- /dev/null +++ b/include/al/scene/SceneCreator.h @@ -0,0 +1,13 @@ +#pragma once + +namespace al +{ + class SceneCreator; + + class IUseSceneCreator + { + public: + virtual al::SceneCreator* getSceneCreator() const = 0; + virtual al::SceneCreator* setSceneCreator() const = 0; + }; +}; \ No newline at end of file diff --git a/include/al/scene/SceneInitInfo.h b/include/al/scene/SceneInitInfo.h new file mode 100644 index 0000000..16a6976 --- /dev/null +++ b/include/al/scene/SceneInitInfo.h @@ -0,0 +1,15 @@ +#pragma once + +#include "game/GameData/GameDataHolderAccessor.h" +#include "game/System/GameSystemInfo.h" +#include "types.h" + +namespace al { + struct SceneInitInfo { + al::GameSystemInfo * gameSysInfo; + GameDataHolderAccessor gameDataHolder; + undefined field_0x10[8]; + char *initStageName; + u32 scenarioNo; + }; +} diff --git a/include/al/scene/SceneObjHolder.h b/include/al/scene/SceneObjHolder.h new file mode 100644 index 0000000..ddf5afd --- /dev/null +++ b/include/al/scene/SceneObjHolder.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ISceneObj.h" + +namespace al { + + class SceneObjHolder { + public: + SceneObjHolder(al::ISceneObj* (*)(int), int); + + ISceneObj *tryGetObj(int) const; // unsafe get still + void setSceneObj(al::ISceneObj *,int); + bool isExist(int) const; + void initAfterPlacementSceneObj(struct ActorInitInfo const&); + ISceneObj *getObj(int) const; + void create(int); + + }; + + class IUseSceneObjHolder + { + public: + virtual al::SceneObjHolder* getSceneObjHolder() const = 0; + + static const char* sSceneObjName; + }; +}; \ No newline at end of file diff --git a/include/al/screen/ScreenPointKeeper.h b/include/al/screen/ScreenPointKeeper.h new file mode 100644 index 0000000..f4e9fe2 --- /dev/null +++ b/include/al/screen/ScreenPointKeeper.h @@ -0,0 +1,10 @@ +#pragma once + +namespace al +{ + class ScreenPointKeeper + { + public: + + }; +}; \ No newline at end of file diff --git a/include/al/sensor/HitSensor.h b/include/al/sensor/HitSensor.h new file mode 100644 index 0000000..2ffa19a --- /dev/null +++ b/include/al/sensor/HitSensor.h @@ -0,0 +1,44 @@ +#pragma once + +#include "al/sensor/SensorHitGroup.h" +#include "sead/math/seadVector.h" +#include "sead/math/seadMatrix.h" + +namespace al +{ + + class LiveActor; + + class HitSensor + { + public: + HitSensor(al::LiveActor *, const char *, unsigned int, float, unsigned short, const sead::Vector3 *, const sead::Matrix34 *, const sead::Vector3 &); + + bool trySensorSort(); + void setFollowPosPtr(const sead::Vector3 *); + void setFollowMtxPtr(const sead::Matrix34 *); + void validate(); + void invalidate(); + void validateBySystem(); + void invalidateBySystem(); + void update(); + void addHitSensor(al::HitSensor *); + + const char* mName; // _0 + int _8; + float _unkC; + float _10; + float _14; + float _18; + unsigned short mMaxSensorCount; // _1C + unsigned short mSensorCount; // _1E + al::HitSensor** mSensors; // _20 + unsigned long _28; + al::SensorHitGroup* mHitGroup; // _30 + bool mIsValidBySystem; // _38 + bool mIsValid; // _39 + al::LiveActor* mParentActor; // _40 + const sead::Vector3* mFollowPos; // _48 + const sead::Matrix34* mFollowMtx; // _50 + }; +}; \ No newline at end of file diff --git a/include/al/sensor/HitSensorKeeper.h b/include/al/sensor/HitSensorKeeper.h new file mode 100644 index 0000000..03edf6d --- /dev/null +++ b/include/al/sensor/HitSensorKeeper.h @@ -0,0 +1,27 @@ +#pragma once + +#include "types.h" +#include "al/LiveActor/LiveActor.h" +#include "al/sensor/SensorHitGroup.h" +#include "sead/math/seadVector.h" +#include "sead/math/seadMatrix.h" +#include "HitSensor.h" + +namespace al +{ + class HitSensorKeeper + { + public: + HitSensorKeeper(int); + bool addSensor(al::LiveActor *sensorHost, char const *sensorName, u32 typeEnum, float radius, ushort maxCount, const sead::Vector3f *position, const sead::Matrix34f *matrix, const sead::Vector3f &scale); + void update(void); + int getSensorNum(void) const; + al::HitSensor *getSensor(int) const; + void clear(void); + void validate(void); + void invalidate(void); + void validateBySystem(void); + void invalidateBySystem(void); + al::HitSensor *getSensor(char const *sensorName) const; + }; +}; \ No newline at end of file diff --git a/include/al/sensor/SensorHitGroup.h b/include/al/sensor/SensorHitGroup.h new file mode 100644 index 0000000..4007652 --- /dev/null +++ b/include/al/sensor/SensorHitGroup.h @@ -0,0 +1,20 @@ +#pragma once + +namespace al +{ + class HitSensor; + + class SensorHitGroup + { + public: + SensorHitGroup(int, const char *); + + void add(al::HitSensor *); + void remove(al::HitSensor *); + al::HitSensor* getSensor(int) const; + + int _0; + int mSensorCount; // _4 + al::HitSensor** mSensors; // _8 + }; +}; \ No newline at end of file diff --git a/include/al/sensor/SensorMsg.h b/include/al/sensor/SensorMsg.h new file mode 100644 index 0000000..b092204 --- /dev/null +++ b/include/al/sensor/SensorMsg.h @@ -0,0 +1,8 @@ +#pragma once + +namespace al +{ + class SensorMsg { + + }; +} // namespace al diff --git a/include/al/sequence/Sequence.h b/include/al/sequence/Sequence.h new file mode 100644 index 0000000..f140ce0 --- /dev/null +++ b/include/al/sequence/Sequence.h @@ -0,0 +1,12 @@ +#pragma once + +#include "al/nerve/NerveExecutor.h" +#include "al/audio/AudioKeeper.h" +#include "al/scene/SceneCreator.h" + +namespace al { + class Sequence : public al::NerveExecutor, public al::IUseAudioKeeper, public al::IUseSceneCreator { + public: + Sequence(const char *seqName); + }; +} \ No newline at end of file diff --git a/include/al/sequence/SequenceInitInfo.h b/include/al/sequence/SequenceInitInfo.h new file mode 100644 index 0000000..c8db4e1 --- /dev/null +++ b/include/al/sequence/SequenceInitInfo.h @@ -0,0 +1,11 @@ +#pragma once + +#include "game/System/GameSystemInfo.h" + +namespace al { + class SequenceInitInfo { + public: + SequenceInitInfo(al::GameSystemInfo const *sysInf) : mSystemInfo(sysInf) {} + const GameSystemInfo *mSystemInfo; + }; +} \ No newline at end of file diff --git a/include/al/stage/StageInfo.h b/include/al/stage/StageInfo.h new file mode 100644 index 0000000..4e0c2a5 --- /dev/null +++ b/include/al/stage/StageInfo.h @@ -0,0 +1,12 @@ +#pragma once + +#include "al/byaml/ByamlIter.h" + +namespace al { + + class Resource; + + struct StageInfo { + StageInfo(al::Resource *, al::ByamlIter const &, al::ByamlIter const &); + }; +} \ No newline at end of file diff --git a/include/al/string/StringTmp.h b/include/al/string/StringTmp.h new file mode 100644 index 0000000..8e93e08 --- /dev/null +++ b/include/al/string/StringTmp.h @@ -0,0 +1,17 @@ +#pragma once + +namespace al { + +template +class StringTmp : public sead::FixedSafeString { // equal to WFormatFixedSafeString +public: + StringTmp(const char* format, ...) : sead::FixedSafeString() { + std::va_list args; + va_start(args, format); + this->formatV(format, args); + va_end(args); + } + ~StringTmp() = default; +}; + +} // namespace al diff --git a/include/al/switch/StageSwitchKeeper.h b/include/al/switch/StageSwitchKeeper.h new file mode 100644 index 0000000..ca79dcc --- /dev/null +++ b/include/al/switch/StageSwitchKeeper.h @@ -0,0 +1,21 @@ +#pragma once + +#include "al/actor/IUseName.h" + +namespace al +{ + class StageSwitchKeeper; + + class IUseStageSwitch : virtual public al::IUseName + { + public: + virtual al::StageSwitchKeeper* getStageSwitchKeeper() const = 0; + virtual void initStageSwitchKeeper() = 0; + }; + + class StageSwitchKeeper + { + public: + StageSwitchKeeper(); + }; +}; \ No newline at end of file diff --git a/include/al/util.hpp b/include/al/util.hpp new file mode 100644 index 0000000..3ef495d --- /dev/null +++ b/include/al/util.hpp @@ -0,0 +1,510 @@ +#pragma once + +#include "al/scene/ISceneObj.h" +#include "al/scene/SceneObjHolder.h" +#include "al/util/AudioUtil.h" +#include "al/util/ControllerUtil.h" +#include "al/util/GraphicsUtil.h" +#include "al/util/LayoutUtil.h" +#include "al/util/LiveActorUtil.h" +#include "al/util/MathUtil.h" +#include "al/util/NerveUtil.h" +#include "al/util/CameraUtil.h" + +namespace al +{ + class LiveActor; + + class AreaObj; + +} +#include +#include +#include +#include +#include + +#include "al/scene/Scene.h" +#include "al/PlayerHolder/PlayerHolder.h" +#include "al/audio/AudioKeeper.h" +#include "al/camera/Projection.h" +#include "al/camera/CameraTargetBase.h" +#include "al/layout/IUseLayout.h" +#include "al/layout/LayoutKit.h" +#include "al/layout/LayoutActor.h" +#include "al/sensor/SensorMsg.h" +#include "al/stage/StageInfo.h" +#include "al/area/AreaObjGroup.h" +#include "al/async/FunctorBase.h" +#include "al/execute/ExecuteDirector.h" + +#include "game/Player/PlayerActorHakoniwa.h" + +#include "agl/DrawContext.h" + +#include "nn/ui2d/Texture.h" + +#include "types.h" + +template +al::LiveActor* createActorFunction(const char *name); + +namespace al +{ +// getters + + struct SceneMsgCtrl; + + sead::Vector3f *getCameraUp(al::IUseCamera const *, int); + + sead::Vector3f *getScale(al::LiveActor const *); + + float *getScaleX(al::LiveActor const *); + + float *getScaleY(al::LiveActor const *); + + float *getScaleZ(al::LiveActor const *); + + al::PlayerHolder *getScenePlayerHolder(al::Scene const *); + + PlayerActorBase *getPlayerActor(al::LiveActor const *, int); + + PlayerActorBase *tryGetPlayerActor(al::PlayerHolder const *, int); + + sead::Heap *getCurrentHeap(void); + + al::Projection *getProjection(al::IUseCamera const *, int); + + int getSubActorNum(al::LiveActor const *); + + al::LiveActor *getSubActor(al::LiveActor const *, const char *); + al::LiveActor *tryGetSubActor(al::LiveActor const *, const char *); + + al::LiveActor *getSubActor(al::LiveActor const *, int); + + int getPlayerControllerPort(int); + + char const *getActionName(al::LiveActor const *); + + char const *getActionFrame(al::LiveActor const *); + + bool isSklAnimExist(al::LiveActor const *, const char *); + bool clearSklAnimInterpole(al::LiveActor *); + + // setters + + void setTransY(al::LiveActor *, float); + + void setTrans(al::LiveActor *, sead::Vector3f const &); + + void setScale(al::LiveActor *, sead::Vector3f const &); + + void setScale(al::LiveActor *, float, float, float); + + void setScaleAll(al::LiveActor *, float); + + void setScaleX(al::LiveActor *, float); + + void setScaleY(al::LiveActor *, float); + + void setScaleZ(al::LiveActor *, float); + + void setGravity(al::LiveActor const *, sead::Vector3f const &); + + void setFront(al::LiveActor *, sead::Vector3f const &); + + void setQuat(al::LiveActor *, const sead::Quatf &); + + void setVelocityZero(al::LiveActor *); + + void setEffectParticleScale(al::IUseEffectKeeper *actor, char const *effectName, float scale); + + // layout stuff + + al::LayoutInitInfo *getLayoutInitInfo(al::ActorInitInfo const&); + + void requestCaptureRecursive(al::LayoutActor const*); + + void startAction(IUseLayoutAction*, const char *, const char *); + + bool isActionPlaying(al::IUseLayoutAction *, const char *action, const char *group); + + bool isActionEnd(al::IUseLayoutAction const*, char const*); + + void setPaneTexture(al::IUseLayout *, char const *, nn::ui2d::TextureInfo const *); + + void setPaneString(al::IUseLayout *layout, char const *paneName, char16_t const *, ushort); + + void setPaneStringFormat(al::IUseLayout *layout, char const *paneName, char const *format,...); + + void setPaneLocalTrans(al::IUseLayout *layout, const char *paneName, sead::Vector3f const &); + void setPaneLocalTrans(al::IUseLayout *layout, const char *paneName, sead::Vector2f const &); + void setPaneLocalSize(al::IUseLayout *layout, const char *paneName, sead::Vector2f const &); + void setPaneLocalScale(al::IUseLayout *layout, const char *paneName, sead::Vector2f const &); + void setPaneLocalRotate(al::IUseLayout *layout, const char *paneName, sead::Vector3f const &); + + sead::Vector3f &getPaneLocalTrans(const al::IUseLayout *layout, const char *paneName); + void getPaneLocalSize(sead::Vector2f *, const al::IUseLayout *layout, const char *paneName); + sead::Vector2f &getPaneLocalScale(const al::IUseLayout *layout, const char *paneName); + sead::Vector3f &getPaneLocalRotate(const al::IUseLayout *layout, const char *paneName); + + bool killLayoutIfActive(al::LayoutActor *); + + // camera stuff + + void setCameraTarget(al::IUseCamera *, al::CameraTargetBase *); + + // calc functions + + f32 calcDistance(al::LiveActor const *, al::LiveActor const*); // calculates distance between two actors + + f32 calcDistance(al::LiveActor const *, sead::Vector3f const&); // calculates distance between an actor and a position in the world + + void calcFrontDir(sead::Vector3f *result, al::LiveActor const *actor); + + // velocity stuff + + void addVelocity(al::LiveActor *,sead::Vector3f const&); + + void setVelocity(al::LiveActor *,sead::Vector3f const&); + + void scaleVelocityExceptDirection(al::LiveActor *,sead::Vector3f const&, float); + + void addVelocityToGravity(al::LiveActor *, float); + + // animation stuff + + void startVisAnimForAction(al::LiveActor*, char const*); + + void startVisAnim(al::LiveActor *, char const *); + void startMtpAnim(al::LiveActor *, char const *); + void startMclAnim(al::LiveActor *, char const *); + + float getSklAnimBlendWeight(al::LiveActor const*,int); + void setSklAnimBlendWeight(al::LiveActor *,float,int); + void setSklAnimBlendWeightDouble(al::LiveActor *, float); + void setSklAnimBlendWeightDouble(al::LiveActor *, float,float); + void setSklAnimBlendWeightTriple(al::LiveActor *, float,float,float); + void setSklAnimBlendWeightQuad(al::LiveActor *, float,float,float,float); + void setSklAnimBlendWeightFivefold(al::LiveActor *,float,float,float,float,float); + void setSklAnimBlendWeightSixfold(al::LiveActor *, float,float,float,float,float,float); + + void offCalcAnim(al::LiveActor *); + void onCalcAnim(al::LiveActor*); + + bool isVisAnimExist(const al::LiveActor *, const char *); + bool isMtpAnimExist(const al::LiveActor *, const char *); + bool isMclAnimExist(const al::LiveActor *, const char *); + + bool isActionEnd(al::LiveActor const *); + + bool isActionPlaying(al::LiveActor const *, char const *); + + bool tryStartActionSubActorAll(al::LiveActor *, char const *); + + bool tryStartActionIfNotPlaying(al::LiveActor*, char const*); + + bool tryStartAction(al::LiveActor *, char const *); + + void startAction(al::LiveActor *, char const *); + + const char * getPlayingSklAnimName(const al::LiveActor *actor, int index); + + bool tryStartSklAnimIfNotPlaying(al::LiveActor *actor, const char * animName); + + bool tryStartSklAnimIfExist(al::LiveActor *actor, const char * animName); + + // byml stuff + + bool tryGetByamlU8(uchar *,al::ByamlIter const&,char const*); + bool tryGetByamlU16(ushort *,al::ByamlIter const&,char const*); + bool tryGetByamlS16(short *,al::ByamlIter const&,char const*); + bool tryGetByamlS32(int *,al::ByamlIter const&,char const*); + bool tryGetByamlU32(uint *,al::ByamlIter const&,char const*); + bool tryGetByamlS64(long *,al::ByamlIter const&,char const*); + bool tryGetByamlU64(ulong *,al::ByamlIter const&,char const*); + bool tryGetByamlF32(float *,al::ByamlIter const&,char const*); + bool tryGetByamlV2f(sead::Vector2 *,al::ByamlIter const&); + bool tryGetByamlV3f(sead::Vector3 *,al::ByamlIter const&); + bool tryGetByamlV4f(sead::Vector4 *,al::ByamlIter const&); + bool tryGetByamlScale(sead::Vector3 *,al::ByamlIter const&); + bool tryGetByamlV2s32(sead::Vector2 *,al::ByamlIter const&); + bool tryGetByamlV3s32(sead::Vector3 *,al::ByamlIter const&); + bool tryGetByamlBox3f(sead::BoundBox3 *,al::ByamlIter const&); + bool tryGetByamlV3f(sead::Vector3 *,al::ByamlIter const&,char const*); + bool tryGetByamlV2f(sead::Vector2 *,al::ByamlIter const&,char const*); + bool tryGetByamlV4f(sead::Vector4 *,al::ByamlIter const&,char const*); + bool tryGetByamlScale(sead::Vector3 *,al::ByamlIter const&,char const*); + bool tryGetByamlV2s32(sead::Vector2 *,al::ByamlIter const&,char const*); + bool tryGetByamlV3s32(sead::Vector3 *,al::ByamlIter const&,char const*); + bool tryGetByamlBox3f(sead::BoundBox3 *,al::ByamlIter const&,char const*); + bool tryGetByamlString(char const**,al::ByamlIter const&,char const*); + bool tryGetByamlColor(sead::Color4f *,al::ByamlIter const&); + bool tryGetByamlColor(sead::Color4f *,al::ByamlIter const&,char const*); + bool tryGetByamlBool(bool *,al::ByamlIter const&,char const*); + bool tryGetByamlKeyStringOrNULL(al::ByamlIter const&,char const*); + bool tryGetByamlKeyIntOrZero(al::ByamlIter const&,char const*); + bool tryGetByamlKeyU32OrZero(al::ByamlIter const&,char const*); + bool tryGetByamlKeyFloatOrZero(al::ByamlIter const&,char const*); + bool tryGetByamlKeyBoolOrFalse(al::ByamlIter const&,char const*); + bool tryGetByamlIterByKey(al::ByamlIter *,al::ByamlIter const&,char const*); + bool tryGetByamlKeyAndIntByIndex(char const**,int *,al::ByamlIter const&,int); + + // nerve stuff + + bool isLessStep(al::IUseNerve const*,int); // checks if the current nerve has been activated for a certain amount of frames(?) + + bool isFirstStep(al::IUseNerve const *); + + bool isNerve(al::IUseNerve const*, al::Nerve const*); + + void setNerve(al::IUseNerve *,al::Nerve const*); + + // effect stuff + + void emitEffect(al::IUseEffectKeeper *effectKeeper, char const *effectName, sead::Vector3f const *effectPosition); + + bool tryEmitEffect(al::IUseEffectKeeper *effectKeeper, char const *effectName, sead::Vector3f const *effectPosition); + + void tryDeleteEffect(al::IUseEffectKeeper *effectKeeper, char const *effectName); + + // sensor stuff + + // enum SensorType { + // Unknown, // 0 + // Player, // 1 + // PlayerAttack, // 2 + // PlayerFoot // 3 + // }; + + al::HitSensor *getHitSensor(al::LiveActor const *host, char const *name); + + al::LiveActor *getSensorHost(al::HitSensor const *); + + void invalidateHitSensors(al::LiveActor *); + void validateHitSensors(al::LiveActor *); + + void invalidateHitSensor(al::LiveActor *, const char *); + void validateHitSensor(al::LiveActor *, const char *); + + void addHitSensor(al::LiveActor *actor, al::ActorInitInfo const &initInfo, char const *sensorName, uint typeEnum, float radius, ushort maxCount, sead::Vector3f const& position); + + bool isMsgPlayerTrampleReflect(al::SensorMsg const *); + + bool isSensorPlayerAttack(al::HitSensor const *targetSensor); + + bool sendMsgPlayerHipDropKnockDown(al::HitSensor *target, al::HitSensor *source); + + // audio + + void tryPauseBgmIfLowPriority(al::IUseAudioKeeper const *keeper, const char *audioName, int unk); + + // player stuff + + void getClassName(const char **namePtr, const al::ActorInitInfo &info); + + void getDisplayName(const char **namePtr, const al::ActorInitInfo &info); + + // stage switch stuff + + // stage init stuff + + + bool tryInitPlacementSingleObject(al::Scene*, al::ActorInitInfo const&, int, char const*, + char const*); + + bool tryInitPlacementSingleObject(al::Scene *,al::ActorInitInfo const&,int,char const*); + + al::StageInfo *getStageInfoMap(al::Scene const*,int); + + void tryGetPlacementInfoAndCount(al::PlacementInfo *, int *, al::StageInfo const*, char const*); + + void getPlacementInfoByIndex(al::PlacementInfo*, al::PlacementInfo const&, int); + + bool tryGetPlacementId(al::PlacementId *pId, al::PlacementInfo const &placement); //{ return pId->init(placement); }; + bool tryGetPlacementId(al::PlacementId *pId, al::ActorInitInfo const &placement); + + void getObjectName(const char **namePtr, const al::PlacementInfo &placementInfo); + void getObjectName(const char **namePtr, const al::ActorInitInfo &placementInfo); + + bool tryGetObjectName(const char **namePtr, const al::PlacementInfo &placementInfo); + bool tryGetObjectName(const char **namePtr, const al::ActorInitInfo &placementInfo); + + bool tryGetStringArg(const char **namePtr, const al::PlacementInfo &info, const char *key); + bool tryGetStringArg(const char **namePtr, const al::ActorInitInfo &info, const char *key); + + bool tryGetClassName(const char **namePtr, const al::PlacementInfo &info); + bool tryGetClassName(const char **namePtr, const al::ActorInitInfo &info); + + bool tryGetDisplayName(const char **namePtr, const al::PlacementInfo &info); + bool tryGetDisplayName(const char **namePtr, const al::ActorInitInfo &info); + + bool tryGetTrans(sead::Vector3f *, al::PlacementInfo const&); + + // scene init + + void initPlacementObjectMap(al::Scene *, al::ActorInitInfo const &, char const *); + void initPlacementObjectDesign(al::Scene *, al::ActorInitInfo const &, char const *); + void initPlacementObjectSound(al::Scene *, al::ActorInitInfo const &, char const *); + + LiveActor* createPlacementActorFromFactory(al::ActorInitInfo const&, al::PlacementInfo const*); + + // layout init stuff + + void initLayoutActor(al::LayoutActor *,al::LayoutInitInfo const&,char const*,char const*); + + // actor init stuff + + bool tryGetArg(int *, al::ActorInitInfo const&, char const*); + bool tryGetArg(int *, al::PlacementInfo const&, char const*); + + bool tryGetArg(float *, al::ActorInitInfo const&, char const*); + bool tryGetArg(float *, al::PlacementInfo const&, char const*); + + bool tryGetArg(bool *, al::ActorInitInfo const&, char const*); + bool tryGetArg(bool *, al::PlacementInfo const&, char const*); + + bool tryGetArgV3f(sead::Vector3 *,al::ActorInitInfo const&, char const*); + bool tryGetArgV3f(sead::Vector3 *,al::PlacementInfo const&, char const*); + + bool tryGetArgV2f(sead::Vector2 *,al::ActorInitInfo const&, char const*); + bool tryGetArgV2f(sead::Vector2 *,al::PlacementInfo const&, char const*); + + void registerExecutorFunctor(char const *, al::ExecuteDirector *, al::FunctorBase const &); + + void initExecutorPlayer(al::LiveActor *,al::ActorInitInfo const&); + void initExecutorPlayerPreMovement(al::LiveActor *,al::ActorInitInfo const&); + void initExecutorPlayerMovement(al::LiveActor *,al::ActorInitInfo const&); + void initExecutorPlayerModel(al::LiveActor *,al::ActorInitInfo const&); + void initExecutorPlayerDecoration(al::LiveActor *,al::ActorInitInfo const&); + void initExecutorModelUpdate(al::LiveActor *, al::ActorInitInfo const &); + void initActorWithArchiveName(al::LiveActor *actor, al::ActorInitInfo const &initInfo, sead::SafeString const &archiveName, char const *suffix); + void initExecutorUpdate(al::LiveActor *,al::ActorInitInfo const&, const char *); + void initExecutorDraw(al::LiveActor *,al::ActorInitInfo const&, const char *); + void initActor(al::LiveActor *, al::ActorInitInfo const&); + void initActorCommon(al::LiveActor *actor, al::ActorInitInfo const &info, char const *dataFolder, char const *archiveName, char const *suffix); // not a real symbol + void initActorSuffix(al::LiveActor*, al::ActorInitInfo const&, char const*); + void initActorInitInfo(al::ActorInitInfo *,al::Scene const*,al::PlacementInfo const*,al::LayoutInitInfo const*,al::ActorFactory const*,al::SceneMsgCtrl *,GameDataHolderBase *); + + // misc + + void readSaveDataSync(const char* dataFile, uint, uint); + + bool isSuccessSaveDataSequence(); + + void validateCollisionParts(al::LiveActor*); + + void invalidateCollisionParts(al::LiveActor *); + + bool isExistCollisionParts(const al::LiveActor *); + + void copyPose(al::LiveActor *from, const al::LiveActor *to); + + void hideModelIfShow(al::LiveActor *); + void showModelIfHide(al::LiveActor *); + + void invalidateClipping(al::LiveActor *); + + void validateClipping(al::LiveActor *); + + void hideSilhouetteModelIfShow(al::LiveActor *); + + bool isExistDitherAnimator(const al::LiveActor *); + + void validateDitherAnim(al::LiveActor *); + void invalidateDitherAnim(al::LiveActor *); + + bool isHideModel(const al::LiveActor *); + + bool isDead(const al::LiveActor *); + + bool isAlive(const al::LiveActor *); + + char16_t *getSystemMessageString(al::IUseMessageSystem const *, char const *, char const *); + + void initPlacementByStageInfo(al::StageInfo const *, char const *, al::ActorInitInfo const &); + + al::AreaObjGroup *tryFindAreaObjGroup(al::IUseAreaObj const *, const char *areaName); + + sead::DrawContext *getSceneDrawContext(al::Scene const*); // these two things are all thats needed to setup text writer in the right context + + sead::LogicalFrameBuffer *getSceneFrameBufferMain(al::Scene const*); + + int getLayoutDisplayWidth(); + int getLayoutDisplayHeight(); + + void executeDraw(al::LayoutKit const *, char const *); + + bool isExistFile(sead::SafeString const &filePath); + + al::StageInfo *getStageInfoMap(al::Scene const*,int); + + bool isVisAnimExist(const al::LiveActor *, const char *); + + bool isInAreaObj(al::LiveActor const *, const char *); + + al::AreaObj *tryFindAreaObj(al::LiveActor const *, const char *); + + void tryGetAreaObjArg(int *, al::AreaObj const *, const char *); + void tryGetAreaObjArg(float *, al::AreaObj const *, const char *); + void tryGetAreaObjArg(bool *, al::AreaObj const *, const char *); + + void tryGetAreaObjStringArg(const char **, al::AreaObj const *, const char *); + + void offCollide(al::LiveActor *); + void onCollide(al::LiveActor *); + + bool tryStartSe(al::IUseAudioKeeper const *, sead::SafeStringBase const &); + + void startSe(al::IUseAudioKeeper const *, sead::SafeStringBase const &); + + void startHitReaction(al::LiveActor const *, char const*); + + bool isInDeathArea(al::LiveActor const *); + + void calcCameraUpDir(sead::Vector3f *, al::IUseCamera const*, int); + + const unsigned char *tryGetBymlFromArcName(sead::SafeStringBase const &, sead::SafeStringBase const &); + + class ActorInitInfo; + + bool getArg(int *, const al::ActorInitInfo &, const char *); + + bool isActiveDemo(const al::Scene *); + + bool isEqualString(char const *, char const *); + bool isEqualString(sead::SafeStringBase const &, sead::SafeStringBase const &); + + bool isEqualSubString(char const *, char const *); + + bool isOnGround(al::LiveActor const*, uint); + + bool isActiveDemo(al::Scene const *); + + bool isInWaterPos(al::LiveActor const*, sead::Vector3f const &); + + // interpolation functions + + void lerpVec(sead::Vector3f *result, sead::Vector3f const& from, sead::Vector3f const& to, float rate); + + void slerpQuat(sead::Quatf *result, sead::Quatf const& from, sead::Quatf const& to, float rate); + + // dither anim stuff + + bool isExistDitherAnimator(al::LiveActor const *); + + void stopDitherAnimAutoCtrl(al::LiveActor *); + void restartDitherAnimAutoCtrl(al::LiveActor *); + + void validateDitherAnim(al::LiveActor *); + void invalidateDitherAnim(al::LiveActor *); + + float getDitherAnimNearClipStartDistance(al::LiveActor const *); + float getDitherAnimNearClipEndDistance(al::LiveActor const *); + + void setDitherAnimSphereRadius(al::LiveActor *, float); + void setDitherAnimBoundingBox(al::LiveActor *, sead::Vector3f const&); + void setDitherAnimMaxAlpha(al::LiveActor *, float); + void setDitherAnimClippingJudgeLocalOffset(al::LiveActor *, sead::Vector3f const&); + void setDitherAnimClippingJudgeParam(al::LiveActor *, const char *); +} diff --git a/include/al/util/AudioUtil.h b/include/al/util/AudioUtil.h new file mode 100644 index 0000000..b46d632 --- /dev/null +++ b/include/al/util/AudioUtil.h @@ -0,0 +1,18 @@ +#pragma once + +#include "al/audio/AudioKeeper.h" +#include "sead/prim/seadSafeString.h" + +namespace al { + +bool checkIsPlayingSe(al::IUseAudioKeeper const*, const sead::SafeString&, const char*); + +bool isPlayingBgm(al::IUseAudioKeeper const*); + +bool isPlayingBgm(al::IUseAudioKeeper const*,char const*); + +void stopAllBgm(al::IUseAudioKeeper const*, int); + +bool tryStopAllBgm(al::IUseAudioKeeper const *, int); + +} \ No newline at end of file diff --git a/include/al/util/CameraUtil.h b/include/al/util/CameraUtil.h new file mode 100644 index 0000000..922266f --- /dev/null +++ b/include/al/util/CameraUtil.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "al/LiveActor/LiveActor.h" +#include "al/camera/CameraDirector.h" +#include "al/camera/CameraTicket.h" +#include "al/camera/CameraTargetBase.h" +#include "al/scene/Scene.h" + +namespace al { + +struct SceneCameraInfo; +struct PauseCameraCtrl; +struct CameraPoseInfo; + +sead::Vector3f* getCameraUp(al::IUseCamera const*, int); + +void requestStopCameraVerticalAbsorb(al::IUseCamera *); + +bool isActiveCamera(al::CameraTicket const*); +bool isActiveCameraTarget(al::CameraTargetBase const*); +bool isActiveCameraSubTarget(al::CameraSubTargetBase const*); +bool isActiveCameraInterpole(al::IUseCamera const*,int); +bool isActiveCameraInterpole(al::SceneCameraInfo const*, int); + +void startCamera(al::IUseCamera const*,al::CameraTicket *,int priority); +void startCameraSub(al::IUseCamera const*,al::CameraTicket *,int priority); +void startCameraShakeByAction(al::LiveActor const*,char const*,char const*,int,int); +void startCameraShakeByHitReaction(al::IUseCamera const*,char const*,char const*,char const*,int,int); +void startCameraInterpole(al::IUseCamera const*,int,int); +void restartCameraByDeathPlayer(al::Scene *); +void startCameraSnapShotMode(al::Scene *,bool); +void startCameraPause(al::PauseCameraCtrl*); + +void endCamera(al::IUseCamera const*,al::CameraTicket *,int,bool); +void endCameraWithNextCameraPose(al::IUseCamera const*,al::CameraTicket *,al::CameraPoseInfo const*,int); +void endCameraSub(al::IUseCamera const*,al::CameraTicket *,int); +void endCameraSnapShotMode(al::Scene *); +void endCameraPause(al::PauseCameraCtrl *); + +} \ No newline at end of file diff --git a/include/al/util/ControllerUtil.h b/include/al/util/ControllerUtil.h new file mode 100644 index 0000000..b093f51 --- /dev/null +++ b/include/al/util/ControllerUtil.h @@ -0,0 +1,40 @@ +#pragma once + +#include "sead/math/seadVector.h" + +namespace al { + bool isPadTriggerUp(int port); + bool isPadTriggerDown(int port); + bool isPadTriggerLeft(int port); + bool isPadTriggerRight(int port); + + bool isPadTriggerA(int port); + bool isPadTriggerB(int port); + bool isPadTriggerX(int port); + bool isPadTriggerY(int port); + + bool isPadTriggerZL(int port); + bool isPadTriggerZR(int port); + + bool isPadTriggerL(int port); + bool isPadTriggerR(int port); + + bool isPadHoldUp(int port); + bool isPadHoldDown(int port); + bool isPadHoldLeft(int port); + bool isPadHoldRight(int port); + + bool isPadHoldA(int port); + bool isPadHoldB(int port); + bool isPadHoldX(int port); + bool isPadHoldY(int port); + + bool isPadHoldL(int port); + bool isPadHoldR(int port); + + bool isPadHoldZL(int port); + bool isPadHoldZR(int port); + + sead::Vector2f *getLeftStick(int); + sead::Vector2f *getRightStick(int); +} \ No newline at end of file diff --git a/include/al/util/GraphicsUtil.h b/include/al/util/GraphicsUtil.h new file mode 100644 index 0000000..29ad602 --- /dev/null +++ b/include/al/util/GraphicsUtil.h @@ -0,0 +1,26 @@ +#pragma once + +namespace sead { + class LookAtCamera; + class Projection; +} + +namespace al { + class IUseCamera; + class Scene; + + void updateKitListPrev(Scene *); + void updateKitList(Scene *, const char *); + void updateKitListPost(Scene *); + + sead::LookAtCamera *getLookAtCamera(al::IUseCamera const*,int); + sead::Projection *getProjectionSead(al::IUseCamera const*,int); +} // namespace al + + + +// TODO: get this out of here +namespace rs +{ + void requestGraphicsPresetAndCubeMapPause(const al::Scene *); +} // namespace rs diff --git a/include/al/util/LayoutUtil.h b/include/al/util/LayoutUtil.h new file mode 100644 index 0000000..398ed7d --- /dev/null +++ b/include/al/util/LayoutUtil.h @@ -0,0 +1,53 @@ +#include +#include +#include +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" + +typedef unsigned short int ushort; + +namespace nn::ui2d { +class TextureInfo; +} + +namespace al +{ + + class IUseLayout; + class IUseLayoutAction; + + char16_t* getPaneStringBuffer(IUseLayout const* lyt, const char* paneName); + int getPaneStringBufferLength(IUseLayout const* lyt, const char* paneName); + + void startFreezeActionEnd(IUseLayoutAction*, char const*, char const*); + + void startHitReaction(LayoutActor const *, const char *, const char *); + + void hidePane(IUseLayout* lyt, const char* paneName); + void hidePaneNoRecursive(IUseLayout* lyt, const char* paneName); + void showPane(IUseLayout* lyt, const char* paneName); + void showPaneNoRecursive(IUseLayout* lyt, const char* paneName); + + void hidePaneRoot(al::IUseLayout *); + void hidePaneRootNoRecursive(al::IUseLayout *); + void showPaneRoot(al::IUseLayout *); + void showPaneRootNoRecursive(al::IUseLayout *); + + bool isHidePane(const IUseLayout *lyt, const char *paneName); + bool isHidePaneRoot(al::IUseLayout const*); + bool isActionPlaying(IUseLayoutAction *, const char *action, const char *group); + bool isActionEnd(IUseLayoutAction const*, char const*); + bool isExistPane(const al::IUseLayout *lyt, char const *paneName); + + void initLayoutActor(LayoutActor*, LayoutInitInfo const&, char const*, char const*); + void setActionFrameRate(IUseLayoutAction *,float,char const*); + void setPaneString(IUseLayout *layout, char const *paneName, char16_t const *paneValue, ushort); + void setPaneStringFormat(IUseLayout* layout, char const* paneName, char const* format, ...); + void setPaneTexture(IUseLayout*, char const*, nn::ui2d::TextureInfo const*); + void calcLayoutPosFromWorldPos(sead::Vector2f*, const al::IUseCamera*, const sead::Vector3f &); + void calcLayoutPosFromWorldPosSub(sead::Vector2f *, const al::IUseCamera *, const sead::Vector3f &); + void calcPaneTrans(sead::Vector2f*, IUseLayout const*, char const*); + void setLocalTrans(IUseLayout*, sead::Vector2f const&); + void setLocalScale(IUseLayout *, float); + +} // namespace al diff --git a/include/al/util/LiveActorUtil.h b/include/al/util/LiveActorUtil.h new file mode 100644 index 0000000..ace88f8 --- /dev/null +++ b/include/al/util/LiveActorUtil.h @@ -0,0 +1,169 @@ +#pragma once + +#include +#include +#include +#include +#include "al/LiveActor/LiveActor.h" +#include "al/async/FunctorBase.h" +#include "al/collision/Collider.h" +#include "game/Player/PlayerActorHakoniwa.h" +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" + +typedef unsigned int uint; + +namespace al { + + void tryInitFixedModelGpuBuffer(const LiveActor*); + void offUpdateMovementEffectAudioCollisionSensor(const LiveActor*); + void hideModel(LiveActor *); + void hideModelIfShow(const LiveActor*); + void showModelIfHide(const LiveActor*); + void setModelAlphaMask(const LiveActor*, float); + void resetPosition(const LiveActor*); + void onSyncClippingSubActor(LiveActor*, const LiveActor*); + void onSyncHideSubActor(LiveActor*, const LiveActor*); + void onSyncAlphaMaskSubActor(LiveActor*, const LiveActor*); + void setMaterialProgrammable(LiveActor*); + void startAction(LiveActor*, char const*); + void startAction(IUseLayoutAction*, const char *, const char *); + void startFreezeActionEnd(IUseLayoutAction *,char const*,char const*); + void startHitReaction(LiveActor*, char const*); + void invalidateClipping(const LiveActor *); + void validateClipping(const LiveActor *); + void setNerveAtActionEnd(LiveActor*, const al::Nerve*); + void updateMaterialCodeWater(LiveActor *); + void updateMaterialCodeWater(LiveActor *, bool); + void appearItem(const LiveActor *); + void turnToTarget(LiveActor*, const sead::Vector3f&, float); + void turnToTarget(LiveActor*, const al::LiveActor *, float); + + void expandClippingRadiusByShadowLength(LiveActor *,sead::Vector3f *, float); + + void initJointLocalXRotator(const LiveActor *,const float *,const char *); + void initJointLocalYRotator(const LiveActor *,const float *,const char *); + void initJointLocalZRotator(const LiveActor*, const float*, const char*); + + void initActorPoseTRSV(al::LiveActor *); + void initActorPoseTRMSV(al::LiveActor *); + void initActorPoseTRGMSV(al::LiveActor *); + void initActorPoseTFSV(al::LiveActor *); + void initActorPoseTFUSV(al::LiveActor *); + void initActorPoseTFGSV(al::LiveActor *); + void initActorPoseTQSV(al::LiveActor *); + void initActorPoseTQGSV(al::LiveActor *); + void initActorPoseTQGMSV(al::LiveActor *); + void initActorPoseT(al::LiveActor *,sead::Vector3 const&); + void initActorPoseTR(al::LiveActor *,sead::Vector3 const&,sead::Vector3 const&); + + void initLayoutPartsActor(LayoutActor*, LayoutActor*, const LayoutInitInfo&, char const*, + char const*); + void initCreateActorWithPlacementInfo(LiveActor*, const al::ActorInitInfo&); + void initMapPartsActor(LiveActor *, const al::ActorInitInfo &, const char *); + void initActorWithArchiveName(LiveActor*, const al::ActorInitInfo&, const sead::SafeString&, const char*); + void initJointControllerKeeper(const LiveActor*, int); + void initJointGlobalQuatController(const LiveActor*, const sead::Quatf*, const char*); + + void appearBreakModelRandomRotateY(LiveActor *); + + bool isNear(const LiveActor *, const LiveActor *, float); + bool isClipped(const LiveActor*); + bool isDead(const LiveActor*); + bool isAlive(const LiveActor*); + bool isHideModel(const LiveActor*); + bool isEffectEmitting(const IUseEffectKeeper*, const char*); + bool isActionEnd(const LiveActor*); + bool isActionPlaying(const LiveActor*, const char *); + bool isInvalidClipping(const LiveActor*); + bool isInWater(const LiveActor *); + bool isInWaterArea(const LiveActor *); + bool isOnGround(const LiveActor *, unsigned int); + bool isOnStageSwitch(IUseStageSwitch const *, const char *); + bool isValidStageSwitch(IUseStageSwitch const *, const char *); + bool isFallNextMove(const LiveActor *, const sead::Vector3f &, float, float); + bool isInDeathArea(LiveActor *); + bool isCollidedFloorCode(LiveActor *, const char *); + bool isNoCollide(LiveActor const *); + bool isNearPlayer(const LiveActor*, float); + bool isFallOrDamageCodeNextMove(const LiveActor *, const sead::Vector3f &, float, float); + bool isFaceToTargetDegreeH(const LiveActor*, const sead::Vector3f &, const sead::Vector3f &, float); + + bool tryOnSwitchDeadOn(IUseStageSwitch *); + bool trySyncStageSwitchAppear(LiveActor *); + PlayerActorHakoniwa* tryFindNearestPlayerActor(const LiveActor *); + bool tryFindNearestPlayerPos(sead::Vector3f *, const LiveActor *); + bool tryAddRippleMiddle(LiveActor*); + bool tryStartActionIfNotPlaying(LiveActor*, const char*); + + float getClippingRadius(al::LiveActor const*); + sead::Vector3f *getClippingObb(al::LiveActor *); + sead::Vector3f *getClippingCenterPos(al::LiveActor const*); + + sead::Vector3f& getTrans(const LiveActor*); + sead::Vector3f* getTransPtr(LiveActor*); + sead::Vector3f& getGravity(const LiveActor*); + sead::Vector3f* getGravityPtr(const LiveActor*); + sead::Vector3f& getFront(const LiveActor*); + sead::Vector3f* getFrontPtr(LiveActor*); + sead::Vector3f& getVelocity(const LiveActor*); + sead::Vector3f* getVelocityPtr(LiveActor*); + sead::Quatf& getQuat(al::LiveActor const*); + sead::Quatf* getQuatPtr(al::LiveActor *); + Collider* getActorCollider(LiveActor*); + + sead::Matrix34f* getJointMtxPtr(const LiveActor*, const char*); //return type might be const + + sead::Quatf* getQuatPtr(LiveActor *); + + sead::Vector3f* getOnGroundNormal(const LiveActor *, uint); + + void scaleVelocity(LiveActor*, float); + void scaleVelocityDirection(LiveActor*, sead::Vector3f const&, float); + + void setClippingObb(LiveActor*, sead::BoundBox3f const&); + void setClippingInfo(LiveActor*, float, sead::Vector3f const*); + void setClippingNearDistance(LiveActor *,float); + + void setTrans(LiveActor *, sead::Vector3f const &); + void setVelocity(LiveActor*, sead::Vector3f const&); + void setVelocity(LiveActor*, float, float, float); + void setVelocityX(LiveActor*, float); + void setVelocityY(LiveActor*, float); + void setVelocityZ(LiveActor*, float); + void setVelocityZero(LiveActor*); + void setVelocityBlowAttackAndTurnToTarget(LiveActor *, sead::Vector3f const&, + float, float); + void setActionFrameRate(LiveActor*, float); + + void addVelocityToGravityFittedGround(LiveActor*, float, unsigned int); + void addVelocityToGravity(LiveActor*, float); + void addVelocityToDirection(LiveActor*, sead::Vector3f const &, float); + void addVelocity(LiveActor*, sead::Vector3f const &); + void addVelocityX(LiveActor*, float); + void addVelocityY(LiveActor*, float); + void addVelocityZ(LiveActor*, float); + + void calcFrontDir(sead::Vector3f *, const LiveActor *); + void calcQuat(sead::Quatf*, const LiveActor*); + void calcJointFrontDir(sead::Vector3f*, const LiveActor*, const char*); + void calcJointPos(sead::Vector3f*, const LiveActor*, const char*); + + void makeQuatUpFront(sead::Quatf *, sead::Vector3f const &, sead::Vector3f const &); + + void rotateQuatYDirDegree(LiveActor *, float); + + f32* findActorParamF32(const LiveActor*, const char*); + + s32* findActorParamS32(const LiveActor*, const char*); + + LiveActor* getSubActor(const LiveActor*, const char*); //NOTE: unknown return type + + bool listenStageSwitchOnAppear(IUseStageSwitch *, al::FunctorBase const &functor); +} + +namespace rs { + +sead::Vector3f* getPlayerPos(const al::LiveActor*); + +} diff --git a/include/al/util/MathUtil.h b/include/al/util/MathUtil.h new file mode 100644 index 0000000..7347430 --- /dev/null +++ b/include/al/util/MathUtil.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +namespace al { + +class LiveActor; + +void normalize(sead::Vector3f*); +float normalize(float, float, float); +float normalize(signed int, signed int, signed int); + +float easeIn(float); +float easeOut(float); +float easeInOut(float); + +float modf(float, float); +int modi(int, int); + +float sign(float); +int sign(int); + +float squareIn(float); +float squareOut(float); + +float powerIn(float, float); +float powerOut(float, float); + +float lerpValue(float, float, float); + +bool isNearZero(float, float); + +bool isNearZero(sead::Vector3f const &, float); + +template +inline T clamp(T value, T min, T max) { + if (value < min) + return min; + if (value > max) + return max; + return value; +} + +float calcSpringDumperForce(float unk1, float unk2, float unk3, float unk4); + +void separateVectorHV(sead::Vector3f*, sead::Vector3f*, const sead::Vector3f &, + const sead::Vector3f &); + +bool tryNormalizeOrDirZ(sead::Vector3f *); +bool tryNormalizeOrDirZ(sead::Vector3f *, sead::Vector3f const&); +bool tryNormalizeOrZero(sead::Vector3f*, sead::Vector3f const&); + +float calcAngleToTargetH(LiveActor const*,sead::Vector3f const&); +float calcAngleToTargetV(LiveActor const*,sead::Vector3f const&); +float calcAngleRadian(sead::Vector3f const&,sead::Vector3f const&); +float calcAngleDegree(sead::Vector3f const&,sead::Vector3f const&); +float calcAngleDegree(sead::Vector2f const&,sead::Vector2f const&); +float calcAngleOnPlaneRadian(sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +float calcAngleOnPlaneDegree(sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +float calcAngleOnPlaneDegreeOrZero(sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +float calcAngleSignOnPlane(sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +bool tryCalcAngleDegree(float *,sead::Vector3f const&,sead::Vector3f const&); +bool tryCalcAngleOnPlaneDegree(float *,sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); + +}; // namespace al \ No newline at end of file diff --git a/include/al/util/NerveUtil.h b/include/al/util/NerveUtil.h new file mode 100644 index 0000000..5a30051 --- /dev/null +++ b/include/al/util/NerveUtil.h @@ -0,0 +1,59 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/nerve/Nerve.h" +#include "al/nerve/NerveStateBase.h" + +#define NERVE_HEADER(Class, Action) \ + class Class##Nrv##Action : public al::Nerve { \ + public: \ + void execute(al::NerveKeeper*) override; \ + }; \ + Class##Nrv##Action nrv##Class##Action; + +#define NERVE_IMPL_(Class, Action, ActionFunc) \ + void Class##Nrv##Action::execute(al::NerveKeeper* keeper) { \ + static_cast(keeper->mParent)->exe##ActionFunc(); \ + } +#define NERVE_IMPL(Class, Action) NERVE_IMPL_(Class, Action, Action) + +// Fruity Nerve Implmentation +#define NERVE_DEF(CLASS, ACTION) \ + struct CLASS##Nrv##ACTION : public al::Nerve { \ + inline void execute(al::NerveKeeper* keeper) override \ + { \ + static_cast(keeper->mParent)->exe##ACTION(); \ + }; \ + const CLASS##Nrv##ACTION nrv##CLASS##ACTION; \ + } + +namespace al +{ + void setNerve(al::IUseNerve *, const al::Nerve *); + void setNerveAtStep(al::IUseNerve *, const al::Nerve *, int); + bool isStep(const al::IUseNerve *, int); + void setNerveAtGreaterEqualStep(al::IUseNerve *, const al::Nerve *, int); + bool isGreaterEqualStep(const al::IUseNerve *, int); + bool isNerve(const al::IUseNerve *, const al::Nerve *); + int getNerveStep(const al::IUseNerve *); + const al::Nerve* getCurrentNerve(const al::IUseNerve *); + bool isFirstStep(const al::IUseNerve *); + bool isLessStep(const al::IUseNerve *, int); + bool isGreaterStep(const al::IUseNerve *, int); + bool isInRangeStep(const al::IUseNerve *, int, int); + bool isIntervalStep(const al::IUseNerve *, int, int); + bool isIntervalOnOffStep(const al::IUseNerve *, int, int); + bool isNewNerve(const al::IUseNerve *); + int calcNerveInterval(const al::IUseNerve *, int, int); + float calcNerveRate(const al::IUseNerve *, int); + float calcNerveRate(const al::IUseNerve *, int, int); + float calcNerveEaseInRate(const al::IUseNerve *, int); + float calcNerveEaseInRate(const al::IUseNerve *, int, int); + + void initNerveState(al::IUseNerve *, al::NerveStateBase *, const al::Nerve *, const char *); + void initNerve(LiveActor *, const al::Nerve *, int); + void addNerveState(al::IUseNerve *, al::NerveStateBase *, const al::Nerve *, const char *); + bool updateNerveState(al::IUseNerve *); + bool updateNerveStateAndNextNerve(al::IUseNerve *, const al::Nerve *); + bool isStateEnd(const al::IUseNerve *); +}; \ No newline at end of file diff --git a/include/al/util/VectorUtil.h b/include/al/util/VectorUtil.h new file mode 100644 index 0000000..9a3fc81 --- /dev/null +++ b/include/al/util/VectorUtil.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include "al/LiveActor/LiveActor.h" + +namespace al { + +class KeyPoseKeeper; + +bool isNearZero(const sead::Vector2f&, float); +void verticalizeVec(sead::Vector3f*, const sead::Vector3f&, const sead::Vector3f&); +bool tryNormalizeOrZero(sead::Vector3f*); +void normalize(sead::Vector3f*); +void turnVecToVecDegree(sead::Vector3f*, const sead::Vector3f&, const sead::Vector3f&, float); +void turnVecToVecRate(sead::Vector3f*, const sead::Vector3f&, const sead::Vector3f&, float); + +void calcQuatFront(sead::Vector3f*, const sead::Quatf&); +void makeQuatFrontUp(sead::Quatf *, const sead::Vector3f &, + const sead::Vector3f &); + +float calcAngleToTargetH(al::LiveActor const*,sead::Vector3f const&); +float calcAngleToTargetV(al::LiveActor const*,sead::Vector3f const&); +float calcAngleRadian(sead::Vector3f const&,sead::Vector3f const&); +float calcAngleDegree(sead::Vector3f const&,sead::Vector3f const&); +float calcAngleDegree(sead::Vector2f const&,sead::Vector2f const&); +bool tryCalcAngleDegree(float *,sead::Vector3f const&,sead::Vector3f const&); +float calcAngleOnPlaneRadian(sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +float calcAngleOnPlaneDegree(sead::Vector3f const&, sead::Vector3f const&, sead::Vector3f const&); +float calcAngleOnPlaneDegreeOrZero(sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +bool tryCalcAngleOnPlaneDegree(float *,sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +float calcAngleSignOnPlane(sead::Vector3f const&, sead::Vector3f const&, sead::Vector3f const&); + +void calcDir(sead::Vector3f *,sead::Vector3f const&,sead::Vector3f const&); +void calcDirBetweenSensors(sead::Vector3f *,al::HitSensor const*,al::HitSensor const*); +void calcDirBetweenSensorsH(sead::Vector3f *,al::HitSensor const*,al::HitSensor const*); +void calcDirBetweenSensorsNormal(sead::Vector3f *,al::HitSensor const*,al::HitSensor const*,sead::Vector3f); +void calcDirClockwiseToDir(sead::Vector3f *,al::LiveActor const*,sead::Vector3f const&); +void calcDirClockwiseToPlayer(sead::Vector3f *,al::LiveActor const*); +void calcDirClockwiseToPos(sead::Vector3f *,al::LiveActor const*,sead::Vector3f const&); +void calcDirFromLongitudeLatitude(sead::Vector3f *,float,float); +void calcDirH(sead::Vector3f *,sead::Vector3f const&,sead::Vector3f const&); +void calcDirIndexNearXY(sead::Vector3 const&,sead::Vector3 const&); +void calcDirIndexNearYZ(sead::Vector3 const&,sead::Vector3 const&); +void calcDirIndexNearZX(sead::Vector3 const&,sead::Vector3 const&); +void calcDirOnPlane(sead::Vector3f *,sead::Vector3f const&,sead::Vector3f const&,sead::Vector3f const&); +void calcDirSlide(sead::Vector3f *,sead::Vector3f const&,sead::Vector3f const&); +void calcDirToActor(sead::Vector3f *,al::LiveActor const*,al::LiveActor const*); +void calcDirToActorH(sead::Vector3f *,al::LiveActor const*,al::LiveActor const*); +void calcDirToActorH(sead::Vector3f *,al::LiveActor const*,sead::Vector3f const&); +void calcDirToNextKey(sead::Vector3f *,al::KeyPoseKeeper const*); +void calcDirVerticalAny(sead::Vector3f *,sead::Vector3f const&); +void calcDirViewInput(sead::Vector3f *,sead::Vector2f const&,sead::Vector3f const&,sead::Matrix34f const*); +void calcDirViewInput2D(sead::Vector3f *,sead::Vector2f const&,sead::Vector3f const&,sead::Matrix34f const*); + +void rotateVectorCenterDegree(sead::Vector3f *output, sead::Vector3f const &dir,sead::Vector3f const &axis, sead::Vector3f const ¢er, float angle); + +void rotateVectorDegree(sead::Vector3f* output, const sead::Vector3f& dir, + const sead::Vector3f& axis, float angle); + +void rotateVectorDegreeX(sead::Vector3f*, float); + +void rotateVectorDegreeY(sead::Vector3f*, float); + +void rotateVectorDegreeZ(sead::Vector3f*, float); + +void rotateVectorQuat(sead::Vector3f *,sead::Quatf const&); + +} // namespace al diff --git a/include/algorithms/CaptureAnims.h b/include/algorithms/CaptureAnims.h new file mode 100644 index 0000000..86b47c9 --- /dev/null +++ b/include/algorithms/CaptureAnims.h @@ -0,0 +1,493 @@ +#pragma once + +#include +#include "crc32.h" +#include "basis/seadTypes.h" + +namespace CaptureAnims { + + enum class Type : s16 { + Unknown = -1, + // Capture Anims + BubbleCannonJump, + Down, + HackCancelJump, + HackHighJump, + HackJump, + HackStart, + HackStartDown, + HackWait, + HackWaitSpeedy, + LandFrontDown, + LandFrontUp, + MoveSpeedy, + Up, + Angry, + AppearSign, + AppearStart, + AttackSuccess, + Blow, + BlowDown, + Clash, + Coverd, + DamageCap, + Find, + HackCharge, + HackChargeFull, + HackChargeTurn, + HackRunCoverd, + PressDown, + RunCoverd, + RunNaked, + Swoon, + SwoonEnd, + SwoonStart, + Trampled, + TrampledEnd, + TurnCoverd, + TurnNaked, + BlowEnd, + BlowSign, + BlowStart, + BlowWide, + Reaction, + Charge, + ChargeStart, + Dummy, + Idle, + ReactionCap, + ReactionCapSand, + ReactionJump, + ReactionJumpSand, + RunHack, + RunStartHack, + WaitHack, + Attack, + AttackHit, + AttackSecond, + AttackSecondSign, + AttackSign, + HackAttack, + HackAttackJump, + HackAttackMiss, + HackAttackQuick, + HackJumpEnd, + HackJumpHigh, + JumpEnd, + JumpStart, + PressDownBlow, + PressDownPress, + SwoonStartLand, + HighFall, + HighJump, + LandSwim, + NpcFall, + NpcJump, + NpcJumpStart, + RunFast, + RunSlow, + RunStay, + Swim, + SwimHackStart, + SwimReaction, + SwimSwoon, + SwimSwoonEnd, + SwimSwoonStart, + SwimSwoonStartLand, + SwoonStartFall, + UpperPunch, + Appear, + Shot, + SwoonTrampled, + AttackEnd, + AttackStart, + MoveEnd, + MoveStart, + Recover, + Trample, + Expand, + ExpandAir, + ExpandEnemy, + ExpandSign, + ExpandSignEnemy, + ExpandWait, + ExpandWaitEnd, + Hold, + Shrink, + ShrinkAir, + ShrinkReactionHead, + ShrinkReactionHip, + TurnEnemy, + TurnHack, + WaitAir, + FallNormal, + Glide, + GlideDrop, + GlideL, + GlideLStart, + GlideR, + GlideRStart, + GlideRise, + GlideStart, + HackEnd, + HackEndFly, + JumpEndNormal, + ReactionTrampled, + Talk, + WaitNormal, + WaitSandSink, + AttackTrample, + AttackUp, + Break, + BreakGroundHit, + BreakReaction, + BreakWait, + FallFly, + Fly, + FlyChase, + FlyWait, + RecoverSign, + Boost, + BoostStart, + Explosion, + FallDown, + FlyWaitStart, + Standby, + Turbo, + AttackTower, + DashTower, + DashTowerBottom, + FallTower, + FindTower, + HackEndTower, + JumpTower, + LandTower, + Miss, + MissTower, + PressDownTower, + Reset, + RideOn, + RideOnTower, + RunTower, + RunTowerBottom, + SandGeyser, + SandGeyserTower, + SandWaitTower, + SandWalkTower, + SlideTower, + SwoonLoop, + WaitTower, + WalkTower, + WalkTowerBottom, + EnemyRun, + HackRun, + Choke, + ChokeEnd, + ChokeStart, + HackStartChoke, + Look, + Swallow, + SwoonChoke, + SwoonEndChoke, + SwoonStartChoke, + Vomit, + WaitChoke, + DashR, + FishingUp, + FishingUpGold, + JumpGround, + ReactionWall, + RollingRail, + RollingRailReverse, + RollingReverse, + SwimStart, + SwimSurfaceEnemy, + SwimWaitStart, + TurnPlayer, + WaitGround, + AdlibWait, + Generate, + JumpEndHigh, + JumpEndLow, + Stretch, + StretchFall, + StretchRun, + StretchWait, + UpperObjStart, + UpperObjWait, + BlowDownCommon, + PressDownCommon, + Reload, + Shoot, + SwoonReaction, + AttackMove, + AttackMoveForest, + AttackWaitL, + AttackWaitR, + DashAfterL, + DashAfterR, + DashL, + EnemyAttack, + EnemyAttackL, + EnemyAttackLEnd, + EnemyAttackLStart, + EnemyAttackR, + EnemyAttackREnd, + EnemyAttackRStart, + EnemyAttackSign, + EnemyFall, + EnemyFallStart, + EnemyLand, + EnemyRunStart, + FindMario, + FootHoldJumpL, + FootHoldJumpR, + FootHoldL, + FootHoldR, + HackStartShort, + HackStartWithTurn, + ListPose, + Lost, + MoveTouch, + NoticeSniff, + ReactionSeaOfTrees, + RunAfterL, + RunAfterR, + RunL, + RunR, + RunStartL, + RunStartR, + SleepReactionHipDrop, + SleepSeaOfTrees, + SwoonStartDashClash, + TurnEndL, + TurnEndR, + TurnL180, + TurnL90, + TurnR180, + TurnR90, + WaitL, + WaitR, + WaitSniff, + WakeUpSwoonStart, + WalkAfterL, + WalkAfterR, + WalkEndL, + WalkEndR, + WalkGroundL, + WalkGroundR, + WalkL, + WalkR, + WalkStart, + WalkStartL, + WalkStartR, + AppearBall, + JumpStick, + SpinL, + SpinR, + SpinThrustL, + SpinThrustR, + Stick, + StickBendHorizontal, + StickBendVertical, + Thrust, + ThrustCancel, + ThrustEnd, + ThrustSign, + ThrustWait, + TrampleDown, + AttackPull, + Back, + BackEnd, + BackReturn, + HackDash, + HackDashPull, + HackMove, + HackPull, + HackPullStart, + HitAction, + HitEnd, + HoleIn, + Return, + Bubble, + // All Capture Actions + Bull, + Byugo, + BlowStartEnemy, + BlowStartHack, + IdleHack, + Stop, + FireBros, + Frog, + HackLand, + HackOff, + HackOn, + NpcLand, + HammerBros, + Imomu, + ExpandAirEnemy, + ExpandAirHack, + ExpandHack, + ExpandSignHack, + ShrinkAirHack, + ShrinkEnemy, + ShrinkHack, + ShrinkReactionHeadEnemy, + ShrinkReactionHeadHack, + ShrinkReactionHipEnemy, + ShrinkReactionHipHack, + Kakku, + FallHack, + KaronWing, + EnemyFly, + HackFly, + Killer, + Default, + SignExplosion, + Kuribo, + KuriboWing, + PackunFire, + ChokeDefault, + ChokeCap, + Eat, + VomitCap, + VomitDefault, + PackunPoison, + EatDown, + Pukupuku, + DashRSurface, + DashRWater, + RollingRSurface, + RollingRWater, + RollingLSurface, + RollingLWater, + SwimStartSurface, + SwimStartWater, + SwimWaitStartSurface, + SwimWaitStartWater, + SwimWaitSurface, + SwimWaitWater, + SwimWaitWaterHack, + SwimWater, + WaitMove, + AdlibWaitMove, + RunMove, + FallMove, + JumpEndEnemy, + JumpEndLowHack, + JumpEndHighHack, + Tank, + BlowDownCaptureParade, + PressDownCaptureParade, + MoveL, + MoveR, + MoveAfterL, + MoveAfterR, + MoveStartL, + MoveStartR, + Tsukkun, + StickBend, + ThrustEnemy, + ThrustHack, + ThrustWaitEnemy, + ThrustWaitHack, + Wanwan, + BackChain, + BackDirect, + WanwanBig, + End + }; + + static constexpr size_t ToValue(Type type) { return static_cast(type); } + + static constexpr Type ToType(std::uint16_t value) {return static_cast(value);} + + static constexpr std::array s_Strs { + // Capture Anims + "BubbleCannonJump", "Down", "HackCancelJump", "HackHighJump", "HackJump", "HackStart", + "HackStartDown", "HackWait", "HackWaitSpeedy", "LandFrontDown", "LandFrontUp", + "MoveSpeedy", "Up", "Angry", "AppearSign", "AppearStart", "AttackSuccess", "Blow", + "BlowDown", "Clash", "Coverd", "DamageCap", "Find", "HackCharge", "HackChargeFull", + "HackChargeTurn", "HackRunCoverd", "PressDown", "RunCoverd", "RunNaked", "Swoon", + "SwoonEnd", "SwoonStart", "Trampled", "TrampledEnd", "TurnCoverd", "TurnNaked", + "BlowEnd", "BlowSign", "BlowStart", "BlowWide", "Reaction", "Charge", "ChargeStart", + "Dummy", "Idle", "ReactionCap", "ReactionCapSand", "ReactionJump", "ReactionJumpSand", + "RunHack", "RunStartHack", "WaitHack", "Attack", "AttackHit", "AttackSecond", + "AttackSecondSign", "AttackSign", "HackAttack", "HackAttackJump", "HackAttackMiss", + "HackAttackQuick", "HackJumpEnd", "HackJumpHigh", "JumpEnd", "JumpStart", + "PressDownBlow", "PressDownPress", "SwoonStartLand", "HighFall", "HighJump", "LandSwim", + "NpcFall", "NpcJump", "NpcJumpStart", "RunFast", "RunSlow", "RunStay", "Swim", + "SwimHackStart", "SwimReaction", "SwimSwoon", "SwimSwoonEnd", "SwimSwoonStart", + "SwimSwoonStartLand", "SwoonStartFall", "UpperPunch", "Appear", "Shot", "SwoonTrampled", + "AttackEnd", "AttackStart", "MoveEnd", "MoveStart", "Recover", "Trample", "Expand", + "ExpandAir", "ExpandEnemy", "ExpandSign", "ExpandSignEnemy", "ExpandWait", + "ExpandWaitEnd", "Hold", "Shrink", "ShrinkAir", "ShrinkReactionHead", + "ShrinkReactionHip", "TurnEnemy", "TurnHack", "WaitAir", "FallNormal", "Glide", + "GlideDrop", "GlideL", "GlideLStart", "GlideR", "GlideRStart", "GlideRise", + "GlideStart", "HackEnd", "HackEndFly", "JumpEndNormal", "ReactionTrampled", "Talk", + "WaitNormal", "WaitSandSink", "AttackTrample", "AttackUp", "Break", "BreakGroundHit", + "BreakReaction", "BreakWait", "FallFly", "Fly", "FlyChase", "FlyWait", "RecoverSign", + "Boost", "BoostStart", "Explosion", "FallDown", "FlyWaitStart", "Standby", "Turbo", + "AttackTower", "DashTower", "DashTowerBottom", "FallTower", "FindTower", "HackEndTower", + "JumpTower", "LandTower", "Miss", "MissTower", "PressDownTower", "Reset", "RideOn", + "RideOnTower", "RunTower", "RunTowerBottom", "SandGeyser", "SandGeyserTower", + "SandWaitTower", "SandWalkTower", "SlideTower", "SwoonLoop", "WaitTower", "WalkTower", + "WalkTowerBottom", "EnemyRun", "HackRun", "Choke", "ChokeEnd", "ChokeStart", + "HackStartChoke", "Look", "Swallow", "SwoonChoke", "SwoonEndChoke", "SwoonStartChoke", + "Vomit", "WaitChoke", "DashR", "FishingUp", "FishingUpGold", "JumpGround", + "ReactionWall", "RollingRail", "RollingRailReverse", "RollingReverse", "SwimStart", + "SwimSurfaceEnemy", "SwimWaitStart", "TurnPlayer", "WaitGround", "AdlibWait", + "Generate", "JumpEndHigh", "JumpEndLow", "Stretch", "StretchFall", "StretchRun", + "StretchWait", "UpperObjStart", "UpperObjWait", "BlowDownCommon", "PressDownCommon", + "Reload", "Shoot", "SwoonReaction", "AttackMove", "AttackMoveForest", "AttackWaitL", + "AttackWaitR", "DashAfterL", "DashAfterR", "DashL", "EnemyAttack", "EnemyAttackL", + "EnemyAttackLEnd", "EnemyAttackLStart", "EnemyAttackR", "EnemyAttackREnd", + "EnemyAttackRStart", "EnemyAttackSign", "EnemyFall", "EnemyFallStart", "EnemyLand", + "EnemyRunStart", "FindMario", "FootHoldJumpL", "FootHoldJumpR", "FootHoldL", + "FootHoldR", "HackStartShort", "HackStartWithTurn", "ListPose", "Lost", "MoveTouch", + "NoticeSniff", "ReactionSeaOfTrees", "RunAfterL", "RunAfterR", "RunL", "RunR", + "RunStartL", "RunStartR", "SleepReactionHipDrop", "SleepSeaOfTrees", + "SwoonStartDashClash", "TurnEndL", "TurnEndR", "TurnL180", "TurnL90", "TurnR180", + "TurnR90", "WaitL", "WaitR", "WaitSniff", "WakeUpSwoonStart", "WalkAfterL", + "WalkAfterR", "WalkEndL", "WalkEndR", "WalkGroundL", "WalkGroundR", "WalkL", "WalkR", + "WalkStart", "WalkStartL", "WalkStartR", "AppearBall", "JumpStick", "SpinL", "SpinR", + "SpinThrustL", "SpinThrustR", "Stick", "StickBendHorizontal", "StickBendVertical", + "Thrust", "ThrustCancel", "ThrustEnd", "ThrustSign", "ThrustWait", "TrampleDown", + "AttackPull", "Back", "BackEnd", "BackReturn", "HackDash", "HackDashPull", "HackMove", + "HackPull", "HackPullStart", "HitAction", "HitEnd", "HoleIn", "Return", + // All Capture Actions + "Bubble", "Bull", "Byugo", "BlowStartEnemy", "BlowStartHack", "IdleHack", "Stop", + "FireBros", "Frog", "HackLand", "HackOff", "HackOn", "NpcLand", "HammerBros", "Imomu", + "ExpandAirEnemy", "ExpandAirHack", "ExpandHack", "ExpandSignHack", "ShrinkAirHack", + "ShrinkEnemy", "ShrinkHack", "ShrinkReactionHeadEnemy", "ShrinkReactionHeadHack", + "ShrinkReactionHipEnemy", "ShrinkReactionHipHack", "Kakku", "FallHack", "KaronWing", + "EnemyFly", "HackFly", "Killer", "Default", "SignExplosion", "Kuribo", "KuriboWing", + "PackunFire", "ChokeDefault", "ChokeCap", "Eat", "VomitCap", "VomitDefault", + "PackunPoison", "EatDown", "Pukupuku", "DashRSurface", "DashRWater", "RollingRSurface", + "RollingRWater", "RollingLSurface", "RollingLWater", "SwimStartSurface", + "SwimStartWater", "SwimWaitStartSurface", "SwimWaitStartWater", "SwimWaitSurface", + "SwimWaitWater", "SwimWaitWaterHack", "SwimWater", "WaitMove", "AdlibWaitMove", + "RunMove", "FallMove", "JumpEndEnemy", "JumpEndLowHack", "JumpEndHighHack", "Tank", + "BlowDownCaptureParade", "PressDownCaptureParade", "MoveL", "MoveR", "MoveAfterL", + "MoveAfterR", "MoveStartL", "MoveStartR", "Tsukkun", "StickBend", "ThrustEnemy", + "ThrustHack", "ThrustWaitEnemy", "ThrustWaitHack", "Wanwan", "BackChain", "BackDirect", + "WanwanBig" + }; + + // these ifdefs are really dumb but it makes clangd happy so /shrug +#ifndef ANALYZER + static constexpr crc32::HashArray s_Hashes(s_Strs); +#endif + + static constexpr Type FindType(std::string_view const& str) { +#ifndef ANALYZER + return ToType(s_Hashes.FindIndex(str)); +#else + return Type::Unknown; +#endif + } + + static constexpr const char *FindStr(Type type) { + return s_Strs.at(ToValue(type)); + } +} \ No newline at end of file diff --git a/include/algorithms/CaptureTypes.h b/include/algorithms/CaptureTypes.h new file mode 100644 index 0000000..b98552a --- /dev/null +++ b/include/algorithms/CaptureTypes.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include "crc32.h" +#include "basis/seadTypes.h" + +namespace CaptureTypes { + + enum class Type : s16 { + Unknown = -1, + AnagramAlphabetCharacter, + Byugo, + Bubble, + Bull, + Car, + ElectricWire, + KillerLauncherMagnum, + KuriboPossessed, + WanwanBig, + KillerLauncher, + Koopa, + Wanwan, + Pukupuku, + PukupukuSnow, + Gamane, + FireBrosPossessed, + PackunFire, + Frog, + Kakku, + Hosui, + HammerBrosPossessed, + Megane, + KaronWing, + KuriboWing, + PackunPoison, + Radicon, + Tank, + Tsukkun, + TRex, + TRexSleep, + TRexPatrol, + Imomu, + SenobiGeneratePoint, + End + }; + + static constexpr size_t ToValue(Type type) { return static_cast(type); } + + static constexpr Type ToType(std::uint16_t value) {return static_cast(value);} + + static constexpr std::array s_Strs { + "AnagramAlphabetCharacter", + "Byugo", + "Bubble", + "Bull", + "Car", + "ElectricWire", + "KillerLauncherMagnum", + "KuriboPossessed", + "WanwanBig", // has sub-actors + "KillerLauncher", + "Koopa", + "Wanwan", // has sub-actors + "Pukupuku", + "PukupukuSnow", + "Gamane", // has sub-actors + "FireBrosPossessed", + "PackunFire", + "Frog", + "Kakku", + "Hosui", + "HammerBrosPossessed", + "Megane", + "KaronWing", + "KuriboWing", + "PackunPoison", + "Radicon", + "Tank", + "Tsukkun", + "TRex", + "TRexSleep", + "TRexPatrol", + "Imomu", + "SenobiGeneratePoint" + }; + + // these ifdefs are really dumb but it makes clangd happy so /shrug +#ifndef ANALYZER + static constexpr crc32::HashArray s_Hashes(s_Strs); +#endif + + static constexpr Type FindType(std::string_view const& str) { +#ifndef ANALYZER + return ToType(s_Hashes.FindIndex(str)); +#else + return Type::Unknown; +#endif + } + + static constexpr const char *FindStr(Type type) { + return s_Strs.at(ToValue(type)); + } +} \ No newline at end of file diff --git a/include/algorithms/PlayerAnims.h b/include/algorithms/PlayerAnims.h new file mode 100644 index 0000000..5e2e9ad --- /dev/null +++ b/include/algorithms/PlayerAnims.h @@ -0,0 +1,1159 @@ +#pragma once + +#include +#include "crc32.h" +#include "basis/seadTypes.h" + +namespace PlayerAnims { + + enum class Type : s16 { + Unknown = -1, + // Action Names + ChairSitDown, + DamageDown, + DamageDownSwim, + DamageDownSwimSurface, + DemoWorldMoveForwardFirst, + DemoWorldMoveForwardNormal, + MotorcycleRide, + MotorcycleRideClash, + MotorcycleRideJump, + MotorcycleRideLand, + MotorcycleRideRunStart, + Move, + MoveMoon, + ExitPictureDown, + NoDamageDown, + NoDamageDownSwim, + NoDamageDownSwimSurface, + RestartSpinJumpL, + RestartSpinJumpR, + JumpCapCatch, + JumpCapCatchAir, + SandWait, + SandWalk, + StartSpinJumpL, + StartSpinJumpR, + SwimSurfaceNormal, + SwimSurfaceCarry, + DemoGetShineGrandStartNormal, + DemoGetShineGrandStartEmpty, + DemoCapOffWait, + // Anim Names + AreaWait64, + AreaWaitAloha, + AreaWaitBalance, + AreaWaitBeach, + AreaWaitCold, + AreaWaitDance01, + AreaWaitDance02, + AreaWaitDance03, + AreaWaitFight, + AreaWaitGetwarm, + AreaWaitGuitar, + AreaWaitHeatGround, + AreaWaitHot, + AreaWaitJog, + AreaWaitLookUp, + AreaWaitSayCheese, + AreaWaitScared, + AreaWaitSearch, + AreaWaitSigh, + AreaWaitSitDown, + AreaWaitSleepy, + AreaWaitSmell, + AreaWaitStink, + AreaWaitStretch, + AreaWaitView, + AreaWaitWaterfall, + AreaWaitWonder, + ArrowShoot, + ArrowShootStart, + BallToss, + BallTossGround, + BattleWait, + BedSleep, + BedSleepStart, + BedSnooze, + BedSnoozeStart, + BikeRide, + Bind, + Brake, + BubbleJump, + BubbleStart, + BubbleWait, + CanoeDamage, + CanoeDamageEnd, + CanoeDamageStart, + CanoePaddle, + CanoeTurnL, + CanoeTurnR, + CanoeWait, + Carry, + CarryFront, + CarryFrontStart, + CarryFrontThrow, + CarryUp, + CarryUpStart, + CarryUpThrow, + CatapultStart, + CatchCap, + CatchCapJump, + CatchCapJumpParts, + CatchCapJumpPartsReverse, + CatchCapParts, + CatchKoopaCap, + ClashWorldStandUp, + ClimbTreeHandStandWait, + Damage, + DamageLand, + DamageSwim, + DamageSwimSurface, + DamageSwimSurfaceLand, + DamageWait, + Dash, + DashBrake, + DashFast, + DashTurn, + Dead, + Dead01, + Dead02, + Dead03, + Dead04, + DeadFall, + DeadFire, + DeadIce, + DeadPoison, + DeadSand, + DeadWallow, + DeadWater, + DemoAppearFromHome, + DemoBattleEndGiantWanderBoss, + DemoBattleStartBossKnuckle, + DemoBattleStartBreeda, + DemoBattleStartBreedaFirst, + DemoBattleStartGolemClimb, + DemoBossRaidAttack, + DemoCrashHome, + DemoCrashHomeFall, + DemoEnding01, + DemoEnding02, + DemoEnding03, + DemoEnding04, + DemoEnterChurch, + DemoGetShine, + DemoGetShineCloseUp, + DemoGetShineEnd, + DemoGetShineGrandCloseUp, + DemoGetShineGrandCloseUpEnd, + DemoGetShineGrandEnd, + DemoGetShineGrandStart, + DemoGetShineLand, + DemoGetShinePaper, + DemoGetShineRock, + DemoGetShineScissors, + DemoGetShineStart, + DemoHackFirst, + DemoHackKoopa01, + DemoHackKoopa02, + DemoJangoCapSearch, + DemoJangoGetUp, + DemoJangoLookAround, + DemoJangoStolen, + DemoJangoStolenLoop, + DemoMeetCapNpcC01, + DemoMeetCapNpcC01TalkWait, + DemoMeetCapNpcC02, + DemoMeetCapNpcC02ATalkWait, + DemoMeetCapNpcC02BTalkWait, + DemoMeetCapNpcC03, + DemoMeetCapNpcC04TalkWait, + DemoMeetCapNpcC06TalkWait, + DemoMeetCapNpcC07TalkWait, + DemoMeetCapNpcC08TalkWait, + DemoMeetCapNpcC10A, + DemoMeetCapNpcC10ATalkWait, + DemoMeetCapNpcC10B, + DemoMeetCapNpcC10BTalkWait, + DemoOpening01, + DemoOpeningCap, + DemoOpeningDown, + DemoPayToHome, + DemoReset, + DemoReturnToHome, + DemoSitDownTalk, + DemoSitDownWaitFirst, + DemoStartJango, + DemoStartJango2, + DemoStartUpHome, + DemoStartUpHomeSky, + DemoStartWaterfall, + DemoTakeOffKoopaForMoon, + DemoTalkCapManHeroAppear, + DemoTalkCapManHeroWalk, + DemoWarpEnd, + DemoWarpStart, + DemoWorldLavaScenario1End, + DemoWorldMoveBackward, + DemoWorldMoveBackward1, + DemoWorldMoveBackward2, + DemoWorldMoveBackward3, + DemoWorldMoveForward, + DemoWorldMoveForwardArrive, + DemoWorldMoveForwardNormal1, + DemoWorldMoveForwardNormal2, + DemoWorldMoveForwardNormal3, + DemoWorldMoveMoonBackward, + DemoWorldMoveMoonForward, + DemoWorldMoveMoonForwardFirst, + DemoWorldTakeoff, + DemoWorldTakeoffEast, + DemoWorldTakeoffForMoon, + DemoWorldTakeoffForMoonFirst, + DemoWorldTakeoffForMoonFirstTuxedo, + DemoWorldTakeoffWest, + DemoWorldWarpHole01, + DemoWorldWarpHole02, + DemoWorldWarpHole03, + Dive, + DiveInWater, + DokanIn, + DokanInHome, + DokanInUpsideDown, + DokanJump, + DokanOut, + DokanOutUpsideDown, + DokanReady, + DokanReadyHome, + DokanSideIn, + DokanSideOut, + DoorIn, + DoorOut, + DoorStart, + Fall, + Fire, + FireRun, + FireRunStart, + GetShine, + GetShineCity, + GetShineEmpty, + GetShineGrand, + GetShineSub, + GrabCeilJump, + GrabCeilStart, + GrabCeilSwing, + GrabCeilWait, + Hack, + HeadSliding, + HeadSlidingStart, + HipDrop, + HipDropLand, + HipDropReaction, + HipDropStart, + HomeCapManHeroAppear, + Jump, + Jump2, + Jump3, + JumpBack, + JumpBroad, + JumpBroad2, + JumpBroad3, + JumpBroad4, + JumpBroad5, + JumpBroad6, + JumpBroad7, + JumpBroad8, + JumpBroad9, + JumpCapCatchCommon, + JumpCapLeapFrog, + JumpDashFast, + JumpEndHack, + JumpFlap, + JumpHipDrop, + JumpInterp, + JumpNpcTrample, + JumpObjectReaction, + JumpReverse, + JumpReverseInterp, + JumpTurn, + Kick, + KoopaCapPunchFinishL, + KoopaCapPunchFinishLStart, + KoopaCapPunchFinishR, + KoopaCapPunchFinishRStart, + KoopaCapPunchL, + KoopaCapPunchLStart, + KoopaCapPunchR, + KoopaCapPunchRStart, + KoopaDemoBattleStartChurchFirstLv2, + KoopaDemoBattleStartChurchLv2, + KoopaDemoBattleStartChurchSecondLv2, + KoopaDemoBattleStartFirstLv1, + KoopaDemoBattleStartFirstLv2, + KoopaDemoBattleStartSecondLv1, + KoopaDemoBattleStartSecondLv2, + KoopaDemoBattleStartTalkBlendLv1, + KoopaDemoClashBasement, + Land, + LandDownFall, + LandJump3, + LandRolling, + LandStiffen, + LandTurn, + MainScenarioCameraWait, + MofumofuDemoOpening1, + MofumofuDemoOpening2, + MotorcycleRideC, + MotorcycleRideClashC, + MotorcycleRideClashL, + MotorcycleRideClashR, + MotorcycleRideJumpC, + MotorcycleRideJumpL, + MotorcycleRideJumpR, + MotorcycleRideL, + MotorcycleRideLandC, + MotorcycleRideLandL, + MotorcycleRideLandR, + MotorcycleRideOn, + MotorcycleRideR, + MotorcycleRideRunStartC, + MotorcycleRideRunStartL, + MotorcycleRideRunStartR, + MotorcycleRideStart, + MotorcycleRideStartL, + MotorcycleRideStartR, + MotorcycleWait, + OpeningStandUp, + PauseMenu2Player, + PauseMenuContinue, + PauseMenuContinueEnd, + PauseMenuData, + PauseMenuHelp, + PauseMenuNewGame, + PauseMenuSave, + PauseMenuStart, + PauseMenuWait, + PoleCatch, + PoleClimb, + PoleFall, + PoleHandStandEnd, + PoleHandStandJump, + PoleHandStandStart, + PoleHandStandTurn, + PoleHandStandWait, + PoleTurn, + PoleWait, + Punch, + Push, + RabbitGet, + RaceManHack, + RaceManHackEnd, + RaceManHackStart, + RaceManJumpEnd, + RaceManJumpStart, + RaceManResultLose, + RaceManResultWin, + RaceResultLose, + RaceResultWin, + ReactionCapOn, + Rolling, + RollingEnd, + RollingEndBackUp, + RollingJump, + RollingLand, + RollingStandUp, + RollingStart, + RoswellPut, + RoswellRun, + RoswellThrow, + RoswellWait, + Run, + RunMoon, + RunStart, + SandWaitNormal, + SandWaitStrong, + SandWalkNormal, + SandWalkStrong, + SitDown, + SitDownEnd, + SitDownLand, + SitDownSleep, + SitDownSleepEnd, + SitDownSleepStart, + SitDownStart, + Sleep, + SleepStart, + Slide, + SlopeSlide, + SlopeSlideBack, + Snooze, + SnoozeStart, + SphinxRideClash, + SphinxRideFall, + SphinxRideGetOn, + SphinxRideJump, + SphinxRideJumpStart, + SphinxRideOn, + SphinxRideRide, + SphinxRideRideOn, + SphinxRideRideStartL, + SphinxRideRideStartR, + SphinxRideRunSlow, + SphinxRideStop, + SphinxRideStopStart, + SpinCap, + SpinCapAirStart, + SpinCapAirStartCapCatch, + SpinCapAirStartDoubleDown, + SpinCapAirStartDoubleLeft, + SpinCapAirStartDoubleRight, + SpinCapAirStartDoubleUp, + SpinCapAirStartLeft, + SpinCapAirStartLong, + SpinCapAirStartRight, + SpinCapEnd, + SpinCapJumpStart, + SpinCapStart, + SpinCapStart2Left, + SpinCapStart2Right, + SpinCapStart3Left, + SpinCapStart3Right, + SpinCapStartCapCatch, + SpinCapStartDoubleDown, + SpinCapStartDoubleLeft, + SpinCapStartDoubleRight, + SpinCapStartDoubleUp, + SpinCapStartLeft, + SpinCapStartLong, + SpinCapStartRight, + SpinCapStartRolling, + SpinCapWait, + SpinGroundL, + SpinGroundR, + SpinJump, + SpinJumpDownFall, + SpinJumpDownFallL, + SpinJumpDownFallR, + SpinJumpL, + SpinJumpLoop, + SpinJumpR, + SpinJumpStart, + SpinSeparate, + SpinSeparateSwim, + SquatEnd, + SquatLand, + SquatStart, + SquatWait, + SquatWalk, + StabStart, + StabUndarWait, + StabUnderStart, + StabWait, + StabWallWait, + SwimDieOver, + SwimDive, + SwimFallIn, + SwimHeadSliding, + SwimHeadSlidingEnd, + SwimHeadSlidingIn, + SwimHeadSlidingStart, + SwimHipDrop, + SwimHipDropLand, + SwimHipDropStart, + SwimJumpHipDrop, + SwimLand, + SwimSpinCapStart, + SwimSpinCapStartDoubleDown, + SwimSpinCapStartDoubleLeft, + SwimSpinCapStartDoubleRight, + SwimSpinCapStartDoubleUp, + SwimSpinCapStartLeft, + SwimSpinCapStartRight, + SwimSquat, + SwimSquatWalk, + SwimStand, + SwimStandMove, + SwimStandSurface, + SwimStandWait, + SwimSurface, + SwimTalkWait, + SwimTrample, + SwimTrampoline, + SwimWait, + SwimWalk, + TalkEnd, + TalkTurn, + TalkTurnL90, + TalkTurnR90, + TalkWait, + TestDemoDiverA, + TestDemoDiverB, + TestDemoE3001, + TestRunBack, + TestRunFront, + TestRunLeft, + TestRunRight, + TestRunWait, + ThrowCap, + ThrowCapJump, + ThrowCapJumpParts, + ThrowCapParts, + TouchJump, + TouchJumpLong, + TouchJumpLongSign, + TractorBubbleEmd, + TractorBubbleEnd, + TractorBubbleStart, + TractorBubbleWait, + TreeClimb, + TreeFall, + Turn, + TurnPoint, + TurnStep, + Wait, + WaitCold, + WaitDemo, + WaitDigPoint, + WaitHot, + WaitRelax, + WaitRelaxStart, + WaitVeryCold, + Walk, + WalkDemo, + WalkSoft, + WallCatch, + WallCatchEnd, + WallCatchEndFast, + WallCatchEndJump, + WallCatchMoveL, + WallCatchMoveR, + WallCatchStart, + WallJump, + WallKeep, + WallKeepReverse, + WallLand, + WallSlideL, + WallSlideR, + WarpIn, + WarpOut, + WarpWait, + WaterRoadMove, + WearEnd, + WhipAttackEnd, + WhipAttackStart, + WhipAttackWait, + WhipWait, + WorldWarpBind, + WorldWarpBindStart, + WorldWarpIn, + WorldWarpOut, + // Capture Anims + BubbleCannonJump, + Down, + HackCancelJump, + HackHighJump, + HackJump, + HackStart, + HackStartDown, + HackWait, + HackWaitSpeedy, + LandFrontDown, + LandFrontUp, + MoveSpeedy, + Up, + Angry, + AppearSign, + AppearStart, + AttackSuccess, + Blow, + BlowDown, + Clash, + Coverd, + DamageCap, + Find, + HackCharge, + HackChargeFull, + HackChargeTurn, + HackRunCoverd, + PressDown, + RunCoverd, + RunNaked, + Swoon, + SwoonEnd, + SwoonStart, + Trampled, + TrampledEnd, + TurnCoverd, + TurnNaked, + BlowEnd, + BlowSign, + BlowStart, + BlowWide, + Reaction, + Charge, + ChargeStart, + Dummy, + Idle, + ReactionCap, + ReactionCapSand, + ReactionJump, + ReactionJumpSand, + RunHack, + RunStartHack, + WaitHack, + Attack, + AttackHit, + AttackSecond, + AttackSecondSign, + AttackSign, + HackAttack, + HackAttackJump, + HackAttackMiss, + HackAttackQuick, + HackJumpEnd, + HackJumpHigh, + JumpEnd, + JumpStart, + PressDownBlow, + PressDownPress, + SwoonStartLand, + HighFall, + HighJump, + LandSwim, + NpcFall, + NpcJump, + NpcJumpStart, + RunFast, + RunSlow, + RunStay, + Swim, + SwimHackStart, + SwimReaction, + SwimSwoon, + SwimSwoonEnd, + SwimSwoonStart, + SwimSwoonStartLand, + SwoonStartFall, + UpperPunch, + Appear, + Shot, + SwoonTrampled, + AttackEnd, + AttackStart, + MoveEnd, + MoveStart, + Recover, + Trample, + Expand, + ExpandAir, + ExpandEnemy, + ExpandSign, + ExpandSignEnemy, + ExpandWait, + ExpandWaitEnd, + Hold, + Shrink, + ShrinkAir, + ShrinkReactionHead, + ShrinkReactionHip, + TurnEnemy, + TurnHack, + WaitAir, + FallNormal, + Glide, + GlideDrop, + GlideL, + GlideLStart, + GlideR, + GlideRStart, + GlideRise, + GlideStart, + HackEnd, + HackEndFly, + JumpEndNormal, + ReactionTrampled, + Talk, + WaitNormal, + WaitSandSink, + AttackTrample, + AttackUp, + Break, + BreakGroundHit, + BreakReaction, + BreakWait, + FallFly, + Fly, + FlyChase, + FlyWait, + RecoverSign, + Boost, + BoostStart, + Explosion, + FallDown, + FlyWaitStart, + Standby, + Turbo, + AttackTower, + DashTower, + DashTowerBottom, + FallTower, + FindTower, + HackEndTower, + JumpTower, + LandTower, + Miss, + MissTower, + PressDownTower, + Reset, + RideOn, + RideOnTower, + RunTower, + RunTowerBottom, + SandGeyser, + SandGeyserTower, + SandWaitTower, + SandWalkTower, + SlideTower, + SwoonLoop, + WaitTower, + WalkTower, + WalkTowerBottom, + EnemyRun, + HackRun, + Choke, + ChokeEnd, + ChokeStart, + HackStartChoke, + Look, + Swallow, + SwoonChoke, + SwoonEndChoke, + SwoonStartChoke, + Vomit, + WaitChoke, + DashR, + FishingUp, + FishingUpGold, + JumpGround, + ReactionWall, + RollingRail, + RollingRailReverse, + RollingReverse, + SwimStart, + SwimSurfaceEnemy, + SwimWaitStart, + TurnPlayer, + WaitGround, + AdlibWait, + Generate, + JumpEndHigh, + JumpEndLow, + Stretch, + StretchFall, + StretchRun, + StretchWait, + UpperObjStart, + UpperObjWait, + BlowDownCommon, + PressDownCommon, + Reload, + Shoot, + SwoonReaction, + AttackMove, + AttackMoveForest, + AttackWaitL, + AttackWaitR, + DashAfterL, + DashAfterR, + DashL, + EnemyAttack, + EnemyAttackL, + EnemyAttackLEnd, + EnemyAttackLStart, + EnemyAttackR, + EnemyAttackREnd, + EnemyAttackRStart, + EnemyAttackSign, + EnemyFall, + EnemyFallStart, + EnemyLand, + EnemyRunStart, + FindMario, + FootHoldJumpL, + FootHoldJumpR, + FootHoldL, + FootHoldR, + HackStartShort, + HackStartWithTurn, + ListPose, + Lost, + MoveTouch, + NoticeSniff, + ReactionSeaOfTrees, + RunAfterL, + RunAfterR, + RunL, + RunR, + RunStartL, + RunStartR, + SleepReactionHipDrop, + SleepSeaOfTrees, + SwoonStartDashClash, + TurnEndL, + TurnEndR, + TurnL180, + TurnL90, + TurnR180, + TurnR90, + WaitL, + WaitR, + WaitSniff, + WakeUpSwoonStart, + WalkAfterL, + WalkAfterR, + WalkEndL, + WalkEndR, + WalkGroundL, + WalkGroundR, + WalkL, + WalkR, + WalkStart, + WalkStartL, + WalkStartR, + AppearBall, + JumpStick, + SpinL, + SpinR, + SpinThrustL, + SpinThrustR, + Stick, + StickBendHorizontal, + StickBendVertical, + Thrust, + ThrustCancel, + ThrustEnd, + ThrustSign, + ThrustWait, + TrampleDown, + AttackPull, + Back, + BackEnd, + BackReturn, + HackDash, + HackDashPull, + HackMove, + HackPull, + HackPullStart, + HitAction, + HitEnd, + HoleIn, + Return, + Bubble, + // All Capture Actions + Bull, + Byugo, + BlowStartEnemy, + BlowStartHack, + IdleHack, + Stop, + FireBros, + Frog, + HackLand, + HackOff, + HackOn, + NpcLand, + HammerBros, + Imomu, + ExpandAirEnemy, + ExpandAirHack, + ExpandHack, + ExpandSignHack, + ShrinkAirHack, + ShrinkEnemy, + ShrinkHack, + ShrinkReactionHeadEnemy, + ShrinkReactionHeadHack, + ShrinkReactionHipEnemy, + ShrinkReactionHipHack, + Kakku, + FallHack, + KaronWing, + EnemyFly, + HackFly, + Killer, + Default, + SignExplosion, + Kuribo, + KuriboWing, + PackunFire, + ChokeDefault, + ChokeCap, + Eat, + VomitCap, + VomitDefault, + PackunPoison, + EatDown, + Pukupuku, + DashRSurface, + DashRWater, + RollingRSurface, + RollingRWater, + RollingLSurface, + RollingLWater, + SwimStartSurface, + SwimStartWater, + SwimWaitStartSurface, + SwimWaitStartWater, + SwimWaitSurface, + SwimWaitWater, + SwimWaitWaterHack, + SwimWater, + WaitMove, + AdlibWaitMove, + RunMove, + FallMove, + JumpEndEnemy, + JumpEndLowHack, + JumpEndHighHack, + Tank, + BlowDownCaptureParade, + PressDownCaptureParade, + MoveL, + MoveR, + MoveAfterL, + MoveAfterR, + MoveStartL, + MoveStartR, + Tsukkun, + StickBend, + ThrustEnemy, + ThrustHack, + ThrustWaitEnemy, + ThrustWaitHack, + Wanwan, + BackChain, + BackDirect, + WanwanBig, + End + }; + + static constexpr size_t ToValue(Type type) { return static_cast(type); } + + static constexpr Type ToType(std::uint16_t value) {return static_cast(value);} + + static constexpr std::array s_Strs { + // Action Names + "ChairSitDown", "DamageDown", "DamageDownSwim", "DamageDownSwimSurface", + "DemoWorldMoveForwardFirst", "DemoWorldMoveForwardNormal", "MotorcycleRide", + "MotorcycleRideClash", "MotorcycleRideJump", "MotorcycleRideLand", + "MotorcycleRideRunStart", "Move", "MoveMoon", "ExitPictureDown", "NoDamageDown", + "NoDamageDownSwim", "NoDamageDownSwimSurface", "RestartSpinJumpL", "RestartSpinJumpR", + "JumpCapCatch", "JumpCapCatchAir", "SandWait", "SandWalk", "StartSpinJumpL", + "StartSpinJumpR", "SwimSurfaceNormal", "SwimSurfaceCarry", + "DemoGetShineGrandStartNormal", "DemoGetShineGrandStartEmpty", "DemoCapOffWait", + // Anim Names + "AreaWait64", "AreaWaitAloha", "AreaWaitBalance", "AreaWaitBeach", "AreaWaitCold", + "AreaWaitDance01", "AreaWaitDance02", "AreaWaitDance03", "AreaWaitFight", + "AreaWaitGetwarm", "AreaWaitGuitar", "AreaWaitHeatGround", "AreaWaitHot", "AreaWaitJog", + "AreaWaitLookUp", "AreaWaitSayCheese", "AreaWaitScared", "AreaWaitSearch", + "AreaWaitSigh", "AreaWaitSitDown", "AreaWaitSleepy", "AreaWaitSmell", "AreaWaitStink", + "AreaWaitStretch", "AreaWaitView", "AreaWaitWaterfall", "AreaWaitWonder", "ArrowShoot", + "ArrowShootStart", "BallToss", "BallTossGround", "BattleWait", "BedSleep", + "BedSleepStart", "BedSnooze", "BedSnoozeStart", "BikeRide", "Bind", "Brake", + "BubbleJump", "BubbleStart", "BubbleWait", "CanoeDamage", "CanoeDamageEnd", + "CanoeDamageStart", "CanoePaddle", "CanoeTurnL", "CanoeTurnR", "CanoeWait", "Carry", + "CarryFront", "CarryFrontStart", "CarryFrontThrow", "CarryUp", "CarryUpStart", + "CarryUpThrow", "CatapultStart", "CatchCap", "CatchCapJump", "CatchCapJumpParts", + "CatchCapJumpPartsReverse", "CatchCapParts", "CatchKoopaCap", "ClashWorldStandUp", + "ClimbTreeHandStandWait", "Damage", "DamageLand", "DamageSwim", "DamageSwimSurface", + "DamageSwimSurfaceLand", "DamageWait", "Dash", "DashBrake", "DashFast", "DashTurn", + "Dead", "Dead01", "Dead02", "Dead03", "Dead04", "DeadFall", "DeadFire", "DeadIce", + "DeadPoison", "DeadSand", "DeadWallow", "DeadWater", "DemoAppearFromHome", + "DemoBattleEndGiantWanderBoss", "DemoBattleStartBossKnuckle", "DemoBattleStartBreeda", + "DemoBattleStartBreedaFirst", "DemoBattleStartGolemClimb", "DemoBossRaidAttack", + "DemoCrashHome", "DemoCrashHomeFall", "DemoEnding01", "DemoEnding02", "DemoEnding03", + "DemoEnding04", "DemoEnterChurch", "DemoGetShine", "DemoGetShineCloseUp", + "DemoGetShineEnd", "DemoGetShineGrandCloseUp", "DemoGetShineGrandCloseUpEnd", + "DemoGetShineGrandEnd", "DemoGetShineGrandStart", "DemoGetShineLand", + "DemoGetShinePaper", "DemoGetShineRock", "DemoGetShineScissors", "DemoGetShineStart", + "DemoHackFirst", "DemoHackKoopa01", "DemoHackKoopa02", "DemoJangoCapSearch", + "DemoJangoGetUp", "DemoJangoLookAround", "DemoJangoStolen", "DemoJangoStolenLoop", + "DemoMeetCapNpcC01", "DemoMeetCapNpcC01TalkWait", "DemoMeetCapNpcC02", + "DemoMeetCapNpcC02ATalkWait", "DemoMeetCapNpcC02BTalkWait", "DemoMeetCapNpcC03", + "DemoMeetCapNpcC04TalkWait", "DemoMeetCapNpcC06TalkWait", "DemoMeetCapNpcC07TalkWait", + "DemoMeetCapNpcC08TalkWait", "DemoMeetCapNpcC10A", "DemoMeetCapNpcC10ATalkWait", + "DemoMeetCapNpcC10B", "DemoMeetCapNpcC10BTalkWait", "DemoOpening01", "DemoOpeningCap", + "DemoOpeningDown", "DemoPayToHome", "DemoReset", "DemoReturnToHome", "DemoSitDownTalk", + "DemoSitDownWaitFirst", "DemoStartJango", "DemoStartJango2", "DemoStartUpHome", + "DemoStartUpHomeSky", "DemoStartWaterfall", "DemoTakeOffKoopaForMoon", + "DemoTalkCapManHeroAppear", "DemoTalkCapManHeroWalk", "DemoWarpEnd", "DemoWarpStart", + "DemoWorldLavaScenario1End", "DemoWorldMoveBackward", "DemoWorldMoveBackward1", + "DemoWorldMoveBackward2", "DemoWorldMoveBackward3", "DemoWorldMoveForward", + "DemoWorldMoveForwardArrive", "DemoWorldMoveForwardNormal1", + "DemoWorldMoveForwardNormal2", "DemoWorldMoveForwardNormal3", + "DemoWorldMoveMoonBackward", "DemoWorldMoveMoonForward", + "DemoWorldMoveMoonForwardFirst", "DemoWorldTakeoff", "DemoWorldTakeoffEast", + "DemoWorldTakeoffForMoon", "DemoWorldTakeoffForMoonFirst", + "DemoWorldTakeoffForMoonFirstTuxedo", "DemoWorldTakeoffWest", "DemoWorldWarpHole01", + "DemoWorldWarpHole02", "DemoWorldWarpHole03", "Dive", "DiveInWater", "DokanIn", + "DokanInHome", "DokanInUpsideDown", "DokanJump", "DokanOut", "DokanOutUpsideDown", + "DokanReady", "DokanReadyHome", "DokanSideIn", "DokanSideOut", "DoorIn", "DoorOut", + "DoorStart", "Fall", "Fire", "FireRun", "FireRunStart", "GetShine", "GetShineCity", + "GetShineEmpty", "GetShineGrand", "GetShineSub", "GrabCeilJump", "GrabCeilStart", + "GrabCeilSwing", "GrabCeilWait", "Hack", "HeadSliding", "HeadSlidingStart", "HipDrop", + "HipDropLand", "HipDropReaction", "HipDropStart", "HomeCapManHeroAppear", "Jump", + "Jump2", "Jump3", "JumpBack", "JumpBroad", "JumpBroad2", "JumpBroad3", "JumpBroad4", + "JumpBroad5", "JumpBroad6", "JumpBroad7", "JumpBroad8", "JumpBroad9", + "JumpCapCatchCommon", "JumpCapLeapFrog", "JumpDashFast", "JumpEndHack", "JumpFlap", + "JumpHipDrop", "JumpInterp", "JumpNpcTrample", "JumpObjectReaction", "JumpReverse", + "JumpReverseInterp", "JumpTurn", "Kick", "KoopaCapPunchFinishL", + "KoopaCapPunchFinishLStart", "KoopaCapPunchFinishR", "KoopaCapPunchFinishRStart", + "KoopaCapPunchL", "KoopaCapPunchLStart", "KoopaCapPunchR", "KoopaCapPunchRStart", + "KoopaDemoBattleStartChurchFirstLv2", "KoopaDemoBattleStartChurchLv2", + "KoopaDemoBattleStartChurchSecondLv2", "KoopaDemoBattleStartFirstLv1", + "KoopaDemoBattleStartFirstLv2", "KoopaDemoBattleStartSecondLv1", + "KoopaDemoBattleStartSecondLv2", "KoopaDemoBattleStartTalkBlendLv1", + "KoopaDemoClashBasement", "Land", "LandDownFall", "LandJump3", "LandRolling", + "LandStiffen", "LandTurn", "MainScenarioCameraWait", "MofumofuDemoOpening1", + "MofumofuDemoOpening2", "MotorcycleRideC", "MotorcycleRideClashC", + "MotorcycleRideClashL", "MotorcycleRideClashR", "MotorcycleRideJumpC", + "MotorcycleRideJumpL", "MotorcycleRideJumpR", "MotorcycleRideL", "MotorcycleRideLandC", + "MotorcycleRideLandL", "MotorcycleRideLandR", "MotorcycleRideOn", "MotorcycleRideR", + "MotorcycleRideRunStartC", "MotorcycleRideRunStartL", "MotorcycleRideRunStartR", + "MotorcycleRideStart", "MotorcycleRideStartL", "MotorcycleRideStartR", "MotorcycleWait", + "OpeningStandUp", "PauseMenu2Player", "PauseMenuContinue", "PauseMenuContinueEnd", + "PauseMenuData", "PauseMenuHelp", "PauseMenuNewGame", "PauseMenuSave", "PauseMenuStart", + "PauseMenuWait", "PoleCatch", "PoleClimb", "PoleFall", "PoleHandStandEnd", + "PoleHandStandJump", "PoleHandStandStart", "PoleHandStandTurn", "PoleHandStandWait", + "PoleTurn", "PoleWait", "Punch", "Push", "RabbitGet", "RaceManHack", "RaceManHackEnd", + "RaceManHackStart", "RaceManJumpEnd", "RaceManJumpStart", "RaceManResultLose", + "RaceManResultWin", "RaceResultLose", "RaceResultWin", "ReactionCapOn", "Rolling", + "RollingEnd", "RollingEndBackUp", "RollingJump", "RollingLand", "RollingStandUp", + "RollingStart", "RoswellPut", "RoswellRun", "RoswellThrow", "RoswellWait", "Run", + "RunMoon", "RunStart", "SandWaitNormal", "SandWaitStrong", "SandWalkNormal", + "SandWalkStrong", "SitDown", "SitDownEnd", "SitDownLand", "SitDownSleep", + "SitDownSleepEnd", "SitDownSleepStart", "SitDownStart", "Sleep", "SleepStart", "Slide", + "SlopeSlide", "SlopeSlideBack", "Snooze", "SnoozeStart", "SphinxRideClash", + "SphinxRideFall", "SphinxRideGetOn", "SphinxRideJump", "SphinxRideJumpStart", + "SphinxRideOn", "SphinxRideRide", "SphinxRideRideOn", "SphinxRideRideStartL", + "SphinxRideRideStartR", "SphinxRideRunSlow", "SphinxRideStop", "SphinxRideStopStart", + "SpinCap", "SpinCapAirStart", "SpinCapAirStartCapCatch", "SpinCapAirStartDoubleDown", + "SpinCapAirStartDoubleLeft", "SpinCapAirStartDoubleRight", "SpinCapAirStartDoubleUp", + "SpinCapAirStartLeft", "SpinCapAirStartLong", "SpinCapAirStartRight", "SpinCapEnd", + "SpinCapJumpStart", "SpinCapStart", "SpinCapStart2Left", "SpinCapStart2Right", + "SpinCapStart3Left", "SpinCapStart3Right", "SpinCapStartCapCatch", + "SpinCapStartDoubleDown", "SpinCapStartDoubleLeft", "SpinCapStartDoubleRight", + "SpinCapStartDoubleUp", "SpinCapStartLeft", "SpinCapStartLong", "SpinCapStartRight", + "SpinCapStartRolling", "SpinCapWait", "SpinGroundL", "SpinGroundR", "SpinJump", + "SpinJumpDownFall", "SpinJumpDownFallL", "SpinJumpDownFallR", "SpinJumpL", + "SpinJumpLoop", "SpinJumpR", "SpinJumpStart", "SpinSeparate", "SpinSeparateSwim", + "SquatEnd", "SquatLand", "SquatStart", "SquatWait", "SquatWalk", "StabStart", + "StabUndarWait", "StabUnderStart", "StabWait", "StabWallWait", "SwimDieOver", + "SwimDive", "SwimFallIn", "SwimHeadSliding", "SwimHeadSlidingEnd", "SwimHeadSlidingIn", + "SwimHeadSlidingStart", "SwimHipDrop", "SwimHipDropLand", "SwimHipDropStart", + "SwimJumpHipDrop", "SwimLand", "SwimSpinCapStart", "SwimSpinCapStartDoubleDown", + "SwimSpinCapStartDoubleLeft", "SwimSpinCapStartDoubleRight", "SwimSpinCapStartDoubleUp", + "SwimSpinCapStartLeft", "SwimSpinCapStartRight", "SwimSquat", "SwimSquatWalk", + "SwimStand", "SwimStandMove", "SwimStandSurface", "SwimStandWait", "SwimSurface", + "SwimTalkWait", "SwimTrample", "SwimTrampoline", "SwimWait", "SwimWalk", "TalkEnd", + "TalkTurn", "TalkTurnL90", "TalkTurnR90", "TalkWait", "TestDemoDiverA", + "TestDemoDiverB", "TestDemoE3001", "TestRunBack", "TestRunFront", "TestRunLeft", + "TestRunRight", "TestRunWait", "ThrowCap", "ThrowCapJump", "ThrowCapJumpParts", + "ThrowCapParts", "TouchJump", "TouchJumpLong", "TouchJumpLongSign", "TractorBubbleEmd", + "TractorBubbleEnd", "TractorBubbleStart", "TractorBubbleWait", "TreeClimb", "TreeFall", + "Turn", "TurnPoint", "TurnStep", "Wait", "WaitCold", "WaitDemo", "WaitDigPoint", + "WaitHot", "WaitRelax", "WaitRelaxStart", "WaitVeryCold", "Walk", "WalkDemo", + "WalkSoft", "WallCatch", "WallCatchEnd", "WallCatchEndFast", "WallCatchEndJump", + "WallCatchMoveL", "WallCatchMoveR", "WallCatchStart", "WallJump", "WallKeep", + "WallKeepReverse", "WallLand", "WallSlideL", "WallSlideR", "WarpIn", "WarpOut", + "WarpWait", "WaterRoadMove", "WearEnd", "WhipAttackEnd", "WhipAttackStart", + "WhipAttackWait", "WhipWait", "WorldWarpBind", "WorldWarpBindStart", "WorldWarpIn", + "WorldWarpOut", + // Capture Anims + "BubbleCannonJump", "Down", "HackCancelJump", "HackHighJump", "HackJump", "HackStart", + "HackStartDown", "HackWait", "HackWaitSpeedy", "LandFrontDown", "LandFrontUp", + "MoveSpeedy", "Up", "Angry", "AppearSign", "AppearStart", "AttackSuccess", "Blow", + "BlowDown", "Clash", "Coverd", "DamageCap", "Find", "HackCharge", "HackChargeFull", + "HackChargeTurn", "HackRunCoverd", "PressDown", "RunCoverd", "RunNaked", "Swoon", + "SwoonEnd", "SwoonStart", "Trampled", "TrampledEnd", "TurnCoverd", "TurnNaked", + "BlowEnd", "BlowSign", "BlowStart", "BlowWide", "Reaction", "Charge", "ChargeStart", + "Dummy", "Idle", "ReactionCap", "ReactionCapSand", "ReactionJump", "ReactionJumpSand", + "RunHack", "RunStartHack", "WaitHack", "Attack", "AttackHit", "AttackSecond", + "AttackSecondSign", "AttackSign", "HackAttack", "HackAttackJump", "HackAttackMiss", + "HackAttackQuick", "HackJumpEnd", "HackJumpHigh", "JumpEnd", "JumpStart", + "PressDownBlow", "PressDownPress", "SwoonStartLand", "HighFall", "HighJump", "LandSwim", + "NpcFall", "NpcJump", "NpcJumpStart", "RunFast", "RunSlow", "RunStay", "Swim", + "SwimHackStart", "SwimReaction", "SwimSwoon", "SwimSwoonEnd", "SwimSwoonStart", + "SwimSwoonStartLand", "SwoonStartFall", "UpperPunch", "Appear", "Shot", "SwoonTrampled", + "AttackEnd", "AttackStart", "MoveEnd", "MoveStart", "Recover", "Trample", "Expand", + "ExpandAir", "ExpandEnemy", "ExpandSign", "ExpandSignEnemy", "ExpandWait", + "ExpandWaitEnd", "Hold", "Shrink", "ShrinkAir", "ShrinkReactionHead", + "ShrinkReactionHip", "TurnEnemy", "TurnHack", "WaitAir", "FallNormal", "Glide", + "GlideDrop", "GlideL", "GlideLStart", "GlideR", "GlideRStart", "GlideRise", + "GlideStart", "HackEnd", "HackEndFly", "JumpEndNormal", "ReactionTrampled", "Talk", + "WaitNormal", "WaitSandSink", "AttackTrample", "AttackUp", "Break", "BreakGroundHit", + "BreakReaction", "BreakWait", "FallFly", "Fly", "FlyChase", "FlyWait", "RecoverSign", + "Boost", "BoostStart", "Explosion", "FallDown", "FlyWaitStart", "Standby", "Turbo", + "AttackTower", "DashTower", "DashTowerBottom", "FallTower", "FindTower", "HackEndTower", + "JumpTower", "LandTower", "Miss", "MissTower", "PressDownTower", "Reset", "RideOn", + "RideOnTower", "RunTower", "RunTowerBottom", "SandGeyser", "SandGeyserTower", + "SandWaitTower", "SandWalkTower", "SlideTower", "SwoonLoop", "WaitTower", "WalkTower", + "WalkTowerBottom", "EnemyRun", "HackRun", "Choke", "ChokeEnd", "ChokeStart", + "HackStartChoke", "Look", "Swallow", "SwoonChoke", "SwoonEndChoke", "SwoonStartChoke", + "Vomit", "WaitChoke", "DashR", "FishingUp", "FishingUpGold", "JumpGround", + "ReactionWall", "RollingRail", "RollingRailReverse", "RollingReverse", "SwimStart", + "SwimSurfaceEnemy", "SwimWaitStart", "TurnPlayer", "WaitGround", "AdlibWait", + "Generate", "JumpEndHigh", "JumpEndLow", "Stretch", "StretchFall", "StretchRun", + "StretchWait", "UpperObjStart", "UpperObjWait", "BlowDownCommon", "PressDownCommon", + "Reload", "Shoot", "SwoonReaction", "AttackMove", "AttackMoveForest", "AttackWaitL", + "AttackWaitR", "DashAfterL", "DashAfterR", "DashL", "EnemyAttack", "EnemyAttackL", + "EnemyAttackLEnd", "EnemyAttackLStart", "EnemyAttackR", "EnemyAttackREnd", + "EnemyAttackRStart", "EnemyAttackSign", "EnemyFall", "EnemyFallStart", "EnemyLand", + "EnemyRunStart", "FindMario", "FootHoldJumpL", "FootHoldJumpR", "FootHoldL", + "FootHoldR", "HackStartShort", "HackStartWithTurn", "ListPose", "Lost", "MoveTouch", + "NoticeSniff", "ReactionSeaOfTrees", "RunAfterL", "RunAfterR", "RunL", "RunR", + "RunStartL", "RunStartR", "SleepReactionHipDrop", "SleepSeaOfTrees", + "SwoonStartDashClash", "TurnEndL", "TurnEndR", "TurnL180", "TurnL90", "TurnR180", + "TurnR90", "WaitL", "WaitR", "WaitSniff", "WakeUpSwoonStart", "WalkAfterL", + "WalkAfterR", "WalkEndL", "WalkEndR", "WalkGroundL", "WalkGroundR", "WalkL", "WalkR", + "WalkStart", "WalkStartL", "WalkStartR", "AppearBall", "JumpStick", "SpinL", "SpinR", + "SpinThrustL", "SpinThrustR", "Stick", "StickBendHorizontal", "StickBendVertical", + "Thrust", "ThrustCancel", "ThrustEnd", "ThrustSign", "ThrustWait", "TrampleDown", + "AttackPull", "Back", "BackEnd", "BackReturn", "HackDash", "HackDashPull", "HackMove", + "HackPull", "HackPullStart", "HitAction", "HitEnd", "HoleIn", "Return", + // All Capture Actions + "Bubble", "Bull", "Byugo", "BlowStartEnemy", "BlowStartHack", "IdleHack", "Stop", + "FireBros", "Frog", "HackLand", "HackOff", "HackOn", "NpcLand", "HammerBros", "Imomu", + "ExpandAirEnemy", "ExpandAirHack", "ExpandHack", "ExpandSignHack", "ShrinkAirHack", + "ShrinkEnemy", "ShrinkHack", "ShrinkReactionHeadEnemy", "ShrinkReactionHeadHack", + "ShrinkReactionHipEnemy", "ShrinkReactionHipHack", "Kakku", "FallHack", "KaronWing", + "EnemyFly", "HackFly", "Killer", "Default", "SignExplosion", "Kuribo", "KuriboWing", + "PackunFire", "ChokeDefault", "ChokeCap", "Eat", "VomitCap", "VomitDefault", + "PackunPoison", "EatDown", "Pukupuku", "DashRSurface", "DashRWater", "RollingRSurface", + "RollingRWater", "RollingLSurface", "RollingLWater", "SwimStartSurface", + "SwimStartWater", "SwimWaitStartSurface", "SwimWaitStartWater", "SwimWaitSurface", + "SwimWaitWater", "SwimWaitWaterHack", "SwimWater", "WaitMove", "AdlibWaitMove", + "RunMove", "FallMove", "JumpEndEnemy", "JumpEndLowHack", "JumpEndHighHack", "Tank", + "BlowDownCaptureParade", "PressDownCaptureParade", "MoveL", "MoveR", "MoveAfterL", + "MoveAfterR", "MoveStartL", "MoveStartR", "Tsukkun", "StickBend", "ThrustEnemy", + "ThrustHack", "ThrustWaitEnemy", "ThrustWaitHack", "Wanwan", "BackChain", "BackDirect", + "WanwanBig" + }; + + // these ifdefs are really dumb but it makes clangd happy so /shrug +#ifndef ANALYZER + static constexpr crc32::HashArray s_Hashes(s_Strs); +#endif + + static constexpr Type FindType(std::string_view const& str) { +#ifndef ANALYZER + return ToType(s_Hashes.FindIndex(str)); +#else + return Type::Unknown; +#endif + } + + static constexpr const char *FindStr(Type type) { + return s_Strs.at(ToValue(type)); + } +} \ No newline at end of file diff --git a/include/algorithms/WipeTypes.h b/include/algorithms/WipeTypes.h new file mode 100644 index 0000000..5f40910 --- /dev/null +++ b/include/algorithms/WipeTypes.h @@ -0,0 +1,77 @@ +#pragma once + +// Probably will go unused + +#include +#include "crc32.h" +#include "basis/seadTypes.h" + +namespace WipeTypes { + + enum class Type : s16 { + Unknown = -1, + 中間ワープ, // Intermediate warp, + タイトルロゴ, // Title logo, + ワープ絵画, // Warp painting, + ホーム離陸, // Home takeoff, + 地下工場, // Underground factory, + 樹海, // Aokigahara, + クッパLV1後, // After Bowser LV1, + 教会崩落, // Church collapse, + 教会落下, // Church fall, + 電線脱出ED, // Wire escape ED, + ワールドワープホール, // World Warp Hall, + 教会に入る, // Enter the church, + 教会から出る, // Get out of the church, + 墜落W前, // Before the crash W, + オープニング, // opening, + ホーム陥落, // Home fall, + 溶岩シナリオ1終了, // Lava scenario 1 end, + ワールド間移動デモ室内へ用, // For inter-world movement demo room, + ワールド間移動デモ終了, // Inter-world movement demo finished, + End + }; + + static constexpr size_t ToValue(Type type) { return static_cast(type); } + + static constexpr Type ToType(std::uint16_t value) {return static_cast(value);} + + static constexpr std::array s_Strs { + "中間ワープ", + "タイトルロゴ", + "ワープ絵画", + "ホーム離陸", + "地下工場", + "樹海", + "クッパLV1後", + "教会崩落", + "教会落下", + "電線脱出ED", + "ワールドワープホール", + "教会に入る", + "教会から出る", + "墜落W前", + "オープニング", + "ホーム陥落", + "溶岩シナリオ1終了", + "ワールド間移動デモ室内へ用", + "ワールド間移動デモ終了用" + }; + + // these ifdefs are really dumb but it makes clangd happy so /shrug +#ifndef ANALYZER + static constexpr crc32::HashArray s_Hashes(s_Strs); +#endif + + static constexpr Type FindType(std::string_view const& str) { +#ifndef ANALYZER + return ToType(s_Hashes.FindIndex(str)); +#else + return Type::Unknown; +#endif + } + + static constexpr const char *FindStr(Type type) { + return s_Strs.at(ToValue(type)); + } +} \ No newline at end of file diff --git a/include/algorithms/crc32.h b/include/algorithms/crc32.h new file mode 100644 index 0000000..ddc8e83 --- /dev/null +++ b/include/algorithms/crc32.h @@ -0,0 +1,122 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +// these ifdefs are really dumb but it makes clangd happy so /shrug +#ifndef ANALYZER +#include +#endif + +// Credits to Shadow for making a majority of this code for me! + +namespace crc32 { + + static constexpr uint32_t s_Table[256] = + { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL + }; + + constexpr uint32_t HashStr(std::string_view const& str) + { + uint32_t crc = 0xffffffff; + for (auto c : str) + crc = (crc >> 8) ^ s_Table[(crc ^ c) & 0xff]; + return crc ^ 0xffffffff; + } + + template + struct HashArray { + using HashType = std::uint32_t; + using PairType = std::pair; + using HashArrayType = std::array; + + HashArrayType m_Hashes; + + constexpr HashArray(std::array const& strings) : m_Hashes() { + /* Hash and store all provided strings. */ + for(size_t i = 0; i < strings.size(); i++) { + m_Hashes[i] = { crc32::HashStr(strings[i]), i }; + } + + /* Sort by hashes. */ + std::sort(m_Hashes.begin(), m_Hashes.end(), [](auto &left, auto &right) { + return left.first < right.first; + }); + } + + constexpr std::int64_t FindIndex(std::string_view const& str) const { + auto hash = crc32::HashStr(str); + auto begin = m_Hashes.cbegin(); + auto end = m_Hashes.cend(); + +#ifndef ANALYZER + /* Binary search for string. */ + auto pair = std::ranges::lower_bound(begin, end, hash, {}, &PairType::first); + + if (pair == end || pair->first != hash) + return -1; + else + return pair->second; +#else + return -1; +#endif + + } + }; +} \ No newline at end of file diff --git a/include/cameras/CameraPoserCustom.h b/include/cameras/CameraPoserCustom.h new file mode 100644 index 0000000..e1e00a7 --- /dev/null +++ b/include/cameras/CameraPoserCustom.h @@ -0,0 +1,33 @@ +#pragma once + +#include "al/camera/CameraPoser.h" +#include "al/camera/alCameraPoserFunction.h" + +#include "sead/math/seadVector.h" + +#include "al/util.hpp" + +// cc = custom cameras + +namespace cc { + class CameraPoserCustom : public al::CameraPoser { + public: + CameraPoserCustom(char const*); + virtual void loadParam(al::ByamlIter const&) override; + virtual void start(al::CameraStartInfo const&) override; + virtual void init() override; + void reset(void); + virtual void update(void) override; + virtual void movement(void) override; + + float mOffsetY = 120.0f; + float mDist = 1600.0f; + float mAngleV = 0.0f; + float mAngleH = 0.0f; + float mPrevH = 0.0f; + float mPrevV = 0.0f; + float mSnapSpeed = 0.15f; + bool mIsResetAngleIfSwitchTarget = false; + sead::Vector3f mPrevTargetDir = sead::Vector3f::ey; + }; +} \ No newline at end of file diff --git a/include/debugMenu.hpp b/include/debugMenu.hpp new file mode 100644 index 0000000..717c504 --- /dev/null +++ b/include/debugMenu.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "sead/devenv/seadDebugFontMgrNvn.h" +#include "sead/gfx/seadPrimitiveRenderer.h" +#include "sead/gfx/seadTextWriter.h" +#include "sead/basis/seadNew.h" +#include "sead/gfx/seadViewport.h" + +#include "agl/DrawContext.h" +#include "agl/utl.h" + +#include "game/System/GameSystem.h" + +#include "al/util.hpp" +#include "logger.hpp" + +extern sead::TextWriter *gTextWriter; + +bool setupDebugMenu(agl::DrawContext* context, sead::Viewport* viewport); + +void drawBackground(agl::DrawContext *context); \ No newline at end of file diff --git a/include/game/Actors/ChurchDoor.h b/include/game/Actors/ChurchDoor.h new file mode 100644 index 0000000..252a830 --- /dev/null +++ b/include/game/Actors/ChurchDoor.h @@ -0,0 +1,7 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +class ChurchDoor : public al::LiveActor { + +}; \ No newline at end of file diff --git a/include/game/Actors/CoinStackGroup.h b/include/game/Actors/CoinStackGroup.h new file mode 100644 index 0000000..713575f --- /dev/null +++ b/include/game/Actors/CoinStackGroup.h @@ -0,0 +1,12 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +struct CoinStack; // stub class + +class CoinStackGroup : public al::LiveActor +{ + public: + void setStackAsCollected(CoinStack *stack); + void generateCoinStackGroup(al::ActorInitInfo const &, int count); +}; \ No newline at end of file diff --git a/include/game/Actors/IUseDemoSkip.h b/include/game/Actors/IUseDemoSkip.h new file mode 100644 index 0000000..adef6f4 --- /dev/null +++ b/include/game/Actors/IUseDemoSkip.h @@ -0,0 +1,5 @@ +#pragma once + +class IUseDemoSkip { + +}; \ No newline at end of file diff --git a/include/game/Actors/KuriboHack.h b/include/game/Actors/KuriboHack.h new file mode 100644 index 0000000..3e00007 --- /dev/null +++ b/include/game/Actors/KuriboHack.h @@ -0,0 +1,7 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +class KuriboHack : public al::LiveActor { + +}; \ No newline at end of file diff --git a/include/game/Actors/ReflectBombGenerator.h b/include/game/Actors/ReflectBombGenerator.h new file mode 100644 index 0000000..93f0b57 --- /dev/null +++ b/include/game/Actors/ReflectBombGenerator.h @@ -0,0 +1,3 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" \ No newline at end of file diff --git a/include/game/Actors/Shine.h b/include/game/Actors/Shine.h new file mode 100644 index 0000000..f27db23 --- /dev/null +++ b/include/game/Actors/Shine.h @@ -0,0 +1,38 @@ +#pragma once + +#include "al/actor/ActorDimensionKeeper.h" +#include "game/Info/QuestInfo.h" +#include "types.h" + +#include "game/Info/ShineInfo.h" +#include "game/Interfaces/IUseDimension.h" +#include "al/LiveActor/LiveActor.h" + +class Shine : public al::LiveActor , public IUseDimension { + public: + Shine(const char* actorName); + + ActorDimensionKeeper *getActorDimensionKeeper() const override; + + void offAppear(); + void onAppear(); + + void getDirectWithDemo(void); + void getDirect(); + void get(); + + void onSwitchGet(void); + + bool isGot() const; + + void setGrandShine(void); + + unsigned char padding[0x10]; + // 0x11C mIsEmptyShine + ShineInfo *curShineInfo; // 0x120 + unsigned char padding_290[0x278 - 0x128]; + QuestInfo *shineQuestInfo; // 0x278 + void *unkPtr1; // 0x280 + ActorDimensionKeeper *mDimensionKeeper; // 0x288 + int shineId; // 0x290 +}; \ No newline at end of file diff --git a/include/game/Actors/ShineTowerRocket.h b/include/game/Actors/ShineTowerRocket.h new file mode 100644 index 0000000..1d8c4ec --- /dev/null +++ b/include/game/Actors/ShineTowerRocket.h @@ -0,0 +1,9 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/event/IEventFlowEventReceiver.h" +#include "IUseDemoSkip.h" + +class ShineTowerRocket : public al::LiveActor, public al::IEventFlowEventReceiver, public IUseDemoSkip { + // virtual void makeActorDead 0x30 +}; \ No newline at end of file diff --git a/include/game/Actors/WorldEndBorderKeeper.h b/include/game/Actors/WorldEndBorderKeeper.h new file mode 100644 index 0000000..51ab886 --- /dev/null +++ b/include/game/Actors/WorldEndBorderKeeper.h @@ -0,0 +1,21 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/nerve/NerveExecutor.h" +#include "math/seadVector.h" + +class WorldEndBorderKeeper : public al::NerveExecutor { +public: + WorldEndBorderKeeper(al::LiveActor const*); + void exeInside(void); + void exeOutside(void); + void exePullBack(void); + void exeWaitBorder(void); + void reset(void); + void update(sead::Vector3f const&,sead::Vector3f const&,bool); + ~WorldEndBorderKeeper(); + + al::LiveActor* mActor; + sead::Vector3f unkVec1; // = sead::Vector3f::ex; + sead::Vector3f unkVec2; // = sead::Vector3f::ex; +}; \ No newline at end of file diff --git a/include/game/Controller/ControllerAppletFunction.h b/include/game/Controller/ControllerAppletFunction.h new file mode 100644 index 0000000..421e96d --- /dev/null +++ b/include/game/Controller/ControllerAppletFunction.h @@ -0,0 +1,9 @@ +#pragma once + +#include "al/gamepad/util.h" + +class ControllerAppletFunction { + public: + void connectControllerSinglePlay(al::GamePadSystem *); + void connectControllerSeparatePlay(al::GamePadSystem *); +}; \ No newline at end of file diff --git a/include/game/GameData/GameDataFile.h b/include/game/GameData/GameDataFile.h new file mode 100644 index 0000000..0dcd582 --- /dev/null +++ b/include/game/GameData/GameDataFile.h @@ -0,0 +1,310 @@ +/** + * @file GameDataFile.h + * @brief Holds data for an individual save file. + */ + +#pragma once + +#include "al/scene/SceneObjHolder.h" +#include "types.h" +#include "UniqueObjInfo.h" + +#include "sead/math/seadVector.h" +#include "sead/stream/seadStream.h" + +namespace al { + class ActorInitInfo; + class PlacementInfo; + class PlacementId; +} + +class GameDataHolder; +class ShineInfo; + +class GameDataFile +{ + public: + GameDataFile(GameDataHolder *); + void initializeData(void); + void tryReadByamlData(uchar const*); + void tryFindCoinCollectInfo(char const*,char const*); + void tryFindShineIndexByUniqueId(int); + void tryFindCoinCollectIndexByUniqueId(int); + void buyDefaultItem(void); + void unlockAchievementShineName(void); + void updateWorldMapIndex(void); + void updateWorldWarpIndex(void); + void initializeCheckpointTable(void); + void generateSaveDataIdForPrepo(void); + void resetMapIcon(void); + void wearDefault(void); + void initializeHintList(void); + void initializeCoinCollectList(void); + void resetTempData(void); + void addPlayTime(int,al::IUseSceneObjHolder const*); + void updateSaveTime(void); + void updateSaveTimeForDisp(void); + void updateSaveInfoForDisp(void); + void generateSaveDataIdForPrepoForWrite(void); + void resetSaveDataIdForPrepoForWrite(void); + void startStage(char const*,int); + void checkIsHomeStage(char const*); + void setGameClear(void); + void startDemoStage(char const*); + void changeNextStage(struct ChangeStageInfo const*,int); + void returnPrevStage(void); + void changeNextStageWithDemoWorldWarp(char const*); + void changeNextStageWithWorldWarpHole(char const*); + void restartStage(void); + void calcNextScenarioNo(void); + void tryGetStageNameCurrent(void); + void changeWipeType(char const*); + void setRestartPointId(al::PlacementId const*); + void clearStartId(void); + void tryGetRestartPointIdString(void); + void endStage(void); + void missAndRestartStage(void); + void checkGotShine(char const*); + void tryGetNextMainScenarioLabel(sead::BufferedSafeString *,sead::BufferedSafeString *); + void tryGetNextMainScenarioPos(sead::Vector3f *); + void tryFindNextMainScenarioInfo(void); + void addPayShine(int); + void addPayShineCurrentAll(void); + void addKey(int); + void addOpenDoorLockNum(int); + void tryFindSaveObjS32(al::PlacementId const*); + void addSessionMember(struct SessionMusicianType const&); + void addCoinCollect(al::PlacementId const*); + void useCoinCollect(int); + void tryFindExistCoinCollectStageName(int); + void payCoinToSphinx(void); + void answerCorrectSphinxQuiz(void); + void answerCorrectSphinxQuizAll(void); + void talkLocalLanguage(void); + void saveWorldTravelingStatus(char const*); + void startWorldTravelingPeach(void); + void setGrowFlowerTime(al::PlacementId const*,ulong); + void addGrowFlowerGrowLevel(al::PlacementId const*,uint); + void findGrowFlowerPotIdFromSeedId(al::PlacementId const*); + void addCoin(int); + void addPlayerJumpCount(void); + void addPlayerThrowCapCount(void); + void readFromStream(sead::ReadStream *,uchar *); + void tryReadByamlDataFromStream(sead::ReadStream *,uchar *,int); + void writeToStream(sead::WriteStream *,sead::Heap *); + void tryWriteByByaml(sead::WriteStream *,sead::Heap *); + void calcCheckpointIndexInScenario(int); + void changeNextSceneByGotCheckpoint(int); + void changeNextSceneByWarp(void); + void changeNextSceneByHome(void); + void startYukimaruRace(void); + void startYukimaruRaceTutorial(void); + void startRaceManRace(void); + void registerCheckpointTrans(al::PlacementId const*,sead::Vector3f const&); + void calcGetCheckpointNum(void); + void calcRestHintNum(void); + void unlockHint(void); + void unlockHintImpl(int); + void unlockHintAmiibo(void); + void unlockHintAddByMoonRock(void); + void calcHintNum(void); + void calcHintTrans(int); + void findHint(int); + void calcHintTransMostEasy(void); + void findHintInfoMostEasy(void); + void calcHintMoonRockNum(void); + void calcHintMoonRockTrans(int); + void findHintMoonRock(int); + void tryUnlockShineName(int,int); + void calcShineIndexTableNameAvailable(int *,int *,int); + void calcShineIndexTableNameUnlockable(int *,int *,int); + void unlockWorld(int); + void noPlayDemoWorldWarp(void); + void calcWorldWarpHoleThroughNum(void); + void enteredStage(void); + //void buyItem(ShopItem::ItemInfo const*,bool); + //void tryFindItemList(ShopItem::ItemInfo const*); + void calcHaveClothNum(void); + void calcHaveCapNum(void); + void calcHaveStickerNum(void); + void calcHaveGiftNum(void); + void buyItemAll(void); + void wearCostume(char const*); + void wearCap(char const*); + void addHackDictionary(char const*); + void findShine(int,int); + void calcShineNumInOneShine(int,int); + void checkAchievementShine(int,int); + void winRace(void); + void findRaceRecord(char const*); + void incrementRaceLoseCount(int); + void setUpdateJumpingRopeScoreFlag(void); + void setVolleyballBestCount(int); + void setUpdateVolleyballScoreFlag(void); + void setAmiiboNpcTrans(sead::Vector3f const&); + void setTimeBalloonNpcTrans(sead::Vector3f const&); + void setPoetterTrans(sead::Vector3f const&); + void setShopNpcTrans(sead::Vector3f const&,char const*,int); + void setMoonRockTrans(sead::Vector3f const&); + void setMiniGameInfo(sead::Vector3f const&,char const*); + void calcMiniGameNum(void); + void showExplainCheckpointFlag(void); + void calcShopNum(void); + void talkKakku(void); + void talkWorldTravelingPeach(void); + void talkCollectBgmNpc(void); + void noFirstNetwork(void); + void calcIsGetMainShineAll(al::IUseSceneObjHolder const*); + void calcIsGetShineAllInWorld(int); + void tryFindLinkedShineIndex(al::ActorInitInfo const&,al::IUseSceneObjHolder const*); + void tryFindLinkedShineIndex(al::ActorInitInfo const&,int,al::IUseSceneObjHolder const*); + void tryFindLinkedShineIndexByLinkName(al::IUseSceneObjHolder const*,al::ActorInitInfo const&,char const*); + void calcLinkedShineNum(al::ActorInitInfo const&); + void tryFindShineIndex(al::ActorInitInfo const&); + void tryFindShineIndex(char const*,char const*); + void disableHintById(int); + void enableHintById(int); + void setHintTrans(int,sead::Vector3f const&); + void resetHintTrans(int); + void registerShineInfo(ShineInfo const*,sead::Vector3f const&); + void calcRestShineInStageWithWorldProgress(char const*); + // void calcGetShineNumByObjectNameOrOptionalId(char const*, GameDataFile::CountType); + void calcGetShineNumByObjectNameWithWorldId(char const*,int); + void calcAllShineNumByObjectNameOrOptionalId(char const*); + void calcGetShineNumByStageName(char const*); + void tryFindAndInitShineInfoByOptionalId(ShineInfo *,char const*); + void tryFindUniqueId(ShineInfo const*); + void findUnlockShineNumCurrentWorld(bool *); + void trySetCollectedBgm(char const*,char const*); + // void setGotShine(GameDataFile::HintInfo const*); + // void tryWriteByByaml(al::ByamlWriter *); + + int getTotalShineNum(void); + void getCollectBgmByIndex(int); + u8 getMainScenarioNoCurrent(void) const; + int getStartShineNextIndex(void); + void getTokimekiMayorNpcFavorabilityRating(void); + void getShopNpcIconNumMax(void); + void getShopNpcTrans(int); + void getPoetterTrans(void); + void getTimeBalloonNpcTrans(void); + void getMiniGameNumMax(void); + void getRaceLoseCount(int); + int getWorldTotalShineNum(int); + int getWorldWarpHoleThroughNumMax(void); + void getCheckpointObjIdInWorld(int); + void getCheckpointNumMaxInWorld(void); + void getPlayerThrowCapCount(void); + void getPlayerJumpCount(void); + void getGrowFlowerGrowLevel(al::PlacementId const*); + void getGrowFlowerTime(al::PlacementId const*); + void getWorldTravelingStatus(void); + void getCoinCollectNum(void); + void getKeyNum(void); + int getPayShineNum(int); + int getShineNum(void); + void getAchievement(char const*); + void getPlayerStartId(void); + void getStageNameNext(void); + void getStageNameCurrent(void); + void getPlayerHitPointData(void); + void getLastUpdateTime(void); + void getPlayTimeTotal(void); + void getMainScenarioNo(int); + void getCollectedBgmMaxNum(void); + int getScenarioNo(void) const; + void getMiniGameName(int); + void getWorldTotalShineNumMax(int); + void getCheckpointTransInWorld(char const*); + void getCoinCollectGotNum(void); + void getTotalPayShineNum(void); + void getShineNum(int); + int getScenarioNo(int worldIndex) const; + void getCollectedBgmNum(void); + void getScenarioNoPlacement(void); + void getMiniGameTrans(int); + void getCoinCollectGotNum(int); + void getTotalShopShineNum(void); + + void setGotShine(int); + void setMainScenarioNo(int); + void setStartShine(ShineInfo const*); + void setKidsMode(bool); + void setTokimekiMayorNpcFavorabilityRating(int); + void setFlagOnTalkMessageInfo(int); + void setJangoTrans(sead::Vector3f const&); + void setJumpingRopeBestCount(int); + void setGrowFlowerTime(al::PlacementId const*,al::PlacementId const*,ulong); + void setSaveObjS32(al::PlacementId const*,int); + void setStartedObj(al::PlacementId const*); + void setGotShine(ShineInfo const*); + void setMissRestartInfo(al::PlacementInfo const&); + void setCheckpointId(al::PlacementId const*); + void setActivateHome(void); + void setOriginalHintTrans(int); + + bool isUnlockedWorld(int) const; + bool isAlreadyGoWorld(int) const; + bool isFirstTimeNextWorld(void) const; + bool isGotShine(ShineInfo const*) const; + bool isGotShine(int) const; + bool isStartedObj(char const*,char const*) const; + bool isAnswerCorrectSphinxQuizAll(int) const; + bool isTalkAlreadyLocalLanguage(void) const; + bool isBuyItem(char const*,sead::FixedSafeString<64> const*) const; + bool isOpenShineName(int,int) const; + bool isExistPoetter(void) const; + bool isAlreadyShowExplainCheckpointFlag(void) const; + bool isFlagOnTalkMessageInfo(int) const; + bool isTalkKakku(void) const; + bool isNextMainShine(struct QuestInfo const*) const; + bool isNextMainShine(int) const; + bool isMainShine(int) const; + bool isLatestGetMainShine(ShineInfo const*) const; + bool isEnableOpenMoonRock(int) const; + bool isCollectedBgm(char const*,char const*) const; + bool isPlayScenarioCamera(struct QuestInfo const*) const; + bool isFirstNetwork(void) const; + bool isTalkCollectBgmNpc(void) const; + bool isTalkWorldTravelingPeach(void) const; + bool isClearWorldMainScenario(int) const; + bool isShopSellout(int) const; + bool isExistTimeBalloonNpc(void) const; + bool isExistJango(void) const; + bool isGotShine(int,int) const; + bool isExistInHackDictionary(char const*) const; + // bool isBuyItem(ShopItem::ItemInfo const*) const; + bool isUnlockAchievementShineName(void) const; + bool isOpenMoonRock(int) const; + bool isEnableUnlockHint(void) const; + bool isGotCheckpoint(al::PlacementId *) const; + bool isGotCheckpointInWorld(int) const; + bool isPlayDemoPlayerDownForBattleKoopaAfter(void) const; + bool isUsedGrowFlowerSeed(al::PlacementId const*) const; + bool isStartWorldTravelingPeach(void) const; + bool isFirstWorldTravelingStatus(void) const; + bool isAnswerCorrectSphinxQuiz(int) const; + bool isPayCoinToSphinx(void) const; + bool isGotCoinCollect(al::PlacementId const*) const; + bool isExistSessionMember(struct SessionMusicianType const&) const; + bool isStartedObj(al::PlacementId const*,char const*) const; + bool isPayShineAllInAllWorld(void) const; + bool isUseMissRestartInfo(void) const; + bool isGoToCeremonyFromInsideHomeShip(void) const; + bool isRaceStart(void) const; + bool isGameClear(void) const; + bool isEmpty(void) const; + bool isKidsMode(void) const; + + undefined padding[0x5C8]; + UniqObjInfo** mUniqueInfo; // 0x5C8 + void *unkPtr1; // 0x5D0 + void *unkPtr2; // 0x5D8 + void *unkPtr3; // 0x5E0 + void* unkPtr4; // 0x5E8 + void* unkPtr5; // 0x5F0 + bool unkBool1; // 0x5F8 + bool unkBool2; // 0x5F9 + bool mIsCapEnable; // 0x5FA +}; + \ No newline at end of file diff --git a/include/game/GameData/GameDataFunction.h b/include/game/GameData/GameDataFunction.h new file mode 100644 index 0000000..3641870 --- /dev/null +++ b/include/game/GameData/GameDataFunction.h @@ -0,0 +1,141 @@ +/** + * @file GameDataFunction.h + * @brief Holds static functions for getting / storage save data. + */ + +#pragma once + +#include +#include "GameDataHolderAccessor.h" +#include "GameDataHolderWriter.h" +#include "al/area/ChangeStageInfo.h" +#include "game/GameData/GameDataFile.h" + +class GameDataFunction +{ +public: + + // sets the current worn costume + static void wearCostume(GameDataHolderWriter, char const *); + + // sets the current worn cap + static void wearCap(GameDataHolderWriter, char const*); + + // remove cappy + static void disableCapByPlacement(al::LiveActor const*); + + // restarts current stage + static void restartStage(GameDataHolderWriter); + + // restarts current stage + static void missAndRestartStage(GameDataHolderWriter); + + // attempts to change the current stage the player is in + static bool tryChangeNextStage(GameDataHolderWriter, ChangeStageInfo const *); + + // gets prev save file's current world id + static s32 getPrevWorldId(GameDataHolderAccessor); + // gets current save file's current world id + static s32 getCurrentWorldId(GameDataHolderAccessor); + // gets next save file's current world id + static s32 getNextWorldId(GameDataHolderAccessor); + + // gets current save file's current stage scenario no + static u8 getScenarioNo(al::LiveActor const*); + + static s32 calcNextScenarioNo(GameDataHolderAccessor); + // gets the current scenario No of the specified kingdom + static s32 getWorldScenarioNo(GameDataHolderAccessor, int); + + static char* getCurrentStageName(GameDataHolderAccessor); + + static char* getMainStageName(GameDataHolderAccessor, int); + + static char* getNextStageName(GameDataHolderAccessor); + + static s32 getCurrentShineNum(GameDataHolderAccessor); + + // gets total moons collected on a specified save file (-1 for current save) + static s32 getTotalShineNum(GameDataHolderAccessor, int); + + // gets the total amount of moons available in a kingdom + static s32 getWorldTotalShineNum(GameDataHolderAccessor, int); + + // checks save file if shine is collected in kingdom index + static bool isGotShine(GameDataHolderAccessor, int, int); + + // checks save file if shine is collected by shine index only (0 through 725) + static bool isGotShine(GameDataHolderAccessor, int); + + // Gets Index for X Kingdom + static s32 getWorldIndexWaterfall(void); + static s32 getWorldIndexMoon(void); + + // gets the current level of the Odyssey + static int getHomeLevel(GameDataHolderAccessor); + + // checks if cappy is enabled + static bool isEnableCap(GameDataHolderAccessor); + // enables cappy if not enabled already + static void enableCap(GameDataHolderWriter); + + // kills the player + static void killPlayer(GameDataHolderWriter); + + // damages the player + static void damagePlayer(GameDataHolderWriter); + + // upgrades the odyssey + static void upHomeLevel(GameDataHolderWriter); + + // Saves shine if obtained + static void setGotShine(GameDataHolderWriter, ShineInfo const*); + + //unlocks a kingdom based off index + static void unlockWorld(GameDataHolderWriter, int); + //sets the scenario of the specified kingdom + static void setMainScenarioNo(GameDataHolderWriter, int scenarioNo); + + // checks if the opening cutscene needs to play + static bool isPlayDemoOpening(GameDataHolderAccessor); + + // checks if odyssey is/needs a repair + static bool isRepairHome(GameDataHolderAccessor); + static void repairHome(GameDataHolderWriter); + + // checks if odyssey is crashed + static bool isCrashHome(GameDataHolderAccessor); + static void crashHome(GameDataHolderWriter); + + // checks if odyssey is activated + static bool isActivateHome(GameDataHolderAccessor); + static void activateHome(GameDataHolderWriter); + + // checks if the odyssey has launched for the first time. + static bool isLaunchHome(GameDataHolderAccessor); + static void launchHome(GameDataHolderWriter); + + static bool isHomeShipStage(GameDataHolder const *); + + // used during the event that enables the odyssey to be used (enables the globe for the odyssey) + static void talkCapNearHomeInWaterfall(al::LiveActor const*); + + // gives the player a life up heart + static void getLifeMaxUpItem(al::LiveActor const *); + + // gets current coin count + static s32 getCoinNum(GameDataHolderAccessor); + + // gets current purple coin count + static s32 getCoinCollectNum(GameDataHolderAccessor); + + // saves an objects Stage Name, Object ID, and custom value to the save file + static void saveObjS32(GameDataHolderWriter, al::PlacementId const*, int); + + // gets the value stored in the unique obj info that matches placement id and curstage + static bool tryFindSaveObjS32Value(int *value, GameDataHolderAccessor accessor, al::PlacementId const* objId); + + // subtracts the supplied int value from the current coin count + static void subCoin(GameDataHolderWriter, int value); + +}; \ No newline at end of file diff --git a/include/game/GameData/GameDataHolder.h b/include/game/GameData/GameDataHolder.h new file mode 100644 index 0000000..7d98138 --- /dev/null +++ b/include/game/GameData/GameDataHolder.h @@ -0,0 +1,111 @@ +/** + * @file GameDataHolder.h + * @brief Holds scenario / game data. + */ + +#pragma once + +#include "basis/seadTypes.h" +#include "game/GameData/GameDataFile.h" +#include "game/GameData/GameDataHolderBase.h" +#include "game/WorldList/WorldList.h" + +class GameDataHolder : public al::GameDataHolderBase +{ +public: + // GameDataHolder(al::MessageSystem const *); + GameDataHolder(); + + virtual ~GameDataHolder(); + + virtual char* getSceneObjName() const; + // virtual al::MessageSystem* getMessageSystem() const; + + void setPlayingFileId(s32 file); + void intitalizeData(); + void initialzeDataCommon(); + void resetTempSaveData(bool); + void initializeDataId(s32); + void readByamlData(s32, char const *); + s32 tryFindEmptyFileId() const; + + bool isRequireSave() const; + void setRequireSave(); + void setRequireSaveFalse(); + void setRequireSaveFrame(); + void updateRequireSaveFrame(); + bool isInvalidSaveForMoonGet() const; + void invalidateSaveForMoonGet(); + void validateSaveForMoonGet(); + void setLanguage(char const *); + char* getLanguage() const; + + void resetLocationName(); + void changeNextStageWithDemoWorldWarp(char const *); + bool tryChangeNextStageWithWorldWarpHole(char const *); + void returnPrevStage(); + char* getNextStageName() const; + char* getNextStageName(s32 idx) const; + GameDataFile* getGameDataFile(s32 idx) const; + // u64 getNextPlayerStartId() const; + char* getCurrentStageName() const; + char* tryGetCurrentStageName() const; + char* getCurrentStageName(s32 idx) const; + // void setCheckpointId(al::PlacementId const *); + char* tryGetRestartPointIdString() const; + void endStage(); + void startStage(char const *, s32); + // void onObjNoWriteSaveData(al::PlacementId const *); + // void offObjNoWriteSaveData(al::PlacementId const *); + // bool isOnObjNoWriteSaveData(al::PlacementId const *) const; + // void onObjNoWriteSaveDataResetMiniGame(al::PlacementId const*); + // void offObjNoWriteSaveDataResetMiniGame(al::PlacementId const *); + // bool isOnObjNoWriteSaveDataResetMiniGame(al::PlacementId const *) const; + // void onObjNoWriteSaveDataInSameScenario(al::PlacementId const *); + // bool isOnObjNoWriteSaveDataInSameScenario(al::PlacementId const *) const; + void writeTempSaveDataToHash(char const *, bool); + + void resetMiniGameData(); + s32 getPlayingFileId() const; + + s32 findUnlockShineNum(bool *, s32) const; + s32 calcBeforePhaseWorldNumMax(s32) const; + bool isFindKoopaNext(s32) const; + bool isBossAttackedHomeNext(s32) const; + void playScenarioStartCamera(s32); + bool isPlayAlreadyScenarioStartCamera() const; + + s32 getShineAnimFrame(s32) const; + s32 getCoinCollectNumMax(s32) const; + + void readFromSaveDataBufferCommonFileOnlyLanguage(); + void readFromSaveDataBuffer(const char *bufferName); + + void changeNextStage(struct ChangeStageInfo const*, int); + + int findUseScenarioNo(char const*); + + // unsigned char padding_20[0x20 - sizeof(al::ISceneObj)]; + // GameDataFile* mGameDataFile; + + int padding; // 0x10 + GameDataFile** mDataFileArr; // 0x18 + GameDataFile* mGameDataFile; // 0x20 + u64 _28; + u64 _30; + u64* _38; // SaveDataAccessSequence* + u32 _40; + u32 mRequireSaveFrame; // _44 + bool mIsInvalidSaveForMoonGet; // _48 + bool mChangeStageRelated; // _49 + u8 _4A; + u8 _4B; + u32 _4C; + sead::BufferedSafeString mLanguage; // _50 + u8 _58[0x90-0x68]; + sead::Heap* _90; + u8 _98[0xB9-0xA0]; + u64* _B8; // TempSaveData* + u8 _C0[0x1A0-0xD0]; + WorldList* mWorldList; // 0x190 +}; \ No newline at end of file diff --git a/include/game/GameData/GameDataHolderAccessor.h b/include/game/GameData/GameDataHolderAccessor.h new file mode 100644 index 0000000..60d1740 --- /dev/null +++ b/include/game/GameData/GameDataHolderAccessor.h @@ -0,0 +1,21 @@ +/** + * @file GameDataHolderAccessor.h + * @brief Wrapper class for GameDataHolder. + */ + +#pragma once + +#include "al/scene/SceneObjHolder.h" +#include "GameDataHolderWriter.h" + +// declaring this here because slappin it into util.hpp causes circular dependency issues +namespace al { + al::ISceneObj *getSceneObj(al::IUseSceneObjHolder const *holder, int index); +} + +class GameDataHolderAccessor : public GameDataHolderWriter +{ + public: + GameDataHolderAccessor(al::IUseSceneObjHolder const *IUseObjHolder) {mData = (GameDataHolder*)al::getSceneObj(IUseObjHolder, 18);} + GameDataHolderAccessor(al::SceneObjHolder const *objHolder) {mData = (GameDataHolder*)objHolder->getObj(18); } +}; \ No newline at end of file diff --git a/include/game/GameData/GameDataHolderBase.h b/include/game/GameData/GameDataHolderBase.h new file mode 100644 index 0000000..44e3ec8 --- /dev/null +++ b/include/game/GameData/GameDataHolderBase.h @@ -0,0 +1,12 @@ +#pragma once + +#include "al/hio/HioNode.h" +#include "al/message/IUseMessageSystem.h" +#include "al/scene/ISceneObj.h" + +namespace al { + class GameDataHolderBase : public ISceneObj, HioNode, IUseMessageSystem { + public: + + }; +} \ No newline at end of file diff --git a/include/game/GameData/GameDataHolderWriter.h b/include/game/GameData/GameDataHolderWriter.h new file mode 100644 index 0000000..970c5ca --- /dev/null +++ b/include/game/GameData/GameDataHolderWriter.h @@ -0,0 +1,8 @@ +#pragma once + +#include "GameDataHolder.h" + +class GameDataHolderWriter { +public: + GameDataHolder *mData; +}; \ No newline at end of file diff --git a/include/game/GameData/GameProgressData.h b/include/game/GameData/GameProgressData.h new file mode 100644 index 0000000..ae57936 --- /dev/null +++ b/include/game/GameData/GameProgressData.h @@ -0,0 +1,8 @@ +#pragma once + +class GameProgressData { + public: + int getHomeLevel(void) const; + void upHomeLevel(void); + void talkCapNearHomeInWaterfall(void); +}; \ No newline at end of file diff --git a/include/game/GameData/SaveDataAccessFunction.h b/include/game/GameData/SaveDataAccessFunction.h new file mode 100644 index 0000000..120ab6f --- /dev/null +++ b/include/game/GameData/SaveDataAccessFunction.h @@ -0,0 +1,10 @@ +#pragma once + +#include "game/GameData/GameDataHolder.h" + +namespace SaveDataAccessFunction { + + void startSaveDataInitSync(GameDataHolder *); + + void startSaveDataReadSync(GameDataHolder *); +} diff --git a/include/game/GameData/UniqueObjInfo.h b/include/game/GameData/UniqueObjInfo.h new file mode 100644 index 0000000..a9ce422 --- /dev/null +++ b/include/game/GameData/UniqueObjInfo.h @@ -0,0 +1,14 @@ +#pragma once + +#include "sead/prim/seadSafeString.hpp" +#include "types.h" + +class UniqObjInfo { + public: + bool isEqual(char const *, char const *); + void set(); + + undefined structSize[0x138]; +}; + +static_assert(sizeof(UniqObjInfo) == 0x138); \ No newline at end of file diff --git a/include/game/HakoniwaSequence/HakoniwaSequence.h b/include/game/HakoniwaSequence/HakoniwaSequence.h new file mode 100644 index 0000000..fe673cc --- /dev/null +++ b/include/game/HakoniwaSequence/HakoniwaSequence.h @@ -0,0 +1,101 @@ +#pragma once + +#include "types.h" + +#include "al/scene/Scene.h" +#include "al/audio/AudioDirector.h" +#include "al/layout/LayoutKit.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/sequence/Sequence.h" +#include "al/sequence/SequenceInitInfo.h" +#include "al/gamepad/util.h" + +#include "game/StageScene/StageScene.h" +#include "game/WorldList/WorldResourceLoader.h" +#include "game/GameData/GameDataHolderAccessor.h" + +#include "HakoniwaStateDeleteScene.h" +#include "HakoniwaStateDemoOpening.h" +#include "HakoniwaStateDemoEnding.h" +#include "HakoniwaStateDemoWorldWarp.h" +#include "HakoniwaStateSimpleDemo.h" +#include "HakoniwaStateBootLoadData.h" + +namespace al +{ + class WipeHolder; + class ScreenCaptureExecutor; + class BootLayout; +} // namespace al + +class HakoniwaSequence : public al::Sequence { + public: + + HakoniwaSequence(const char *); + bool isDisposable(void); + void updatePadSystem(void); + void destroySceneHeap(bool); + void init(al::SequenceInitInfo const &); + void initSystem(void); + void update(void); + bool isEnableSave(void); + void drawMain(void); + al::Scene *getCurrentScene(void) const; // {return this->curScene} + + void* qword20; + void* qword28; + void* qword30; + void* qword38; + void* qword40; + void* qword48; + void* qword50; + void* qword58; + void* qword60; + void* qword68; + void* qword70; + void* qword78; + void* qword80; + void* qword88; + al::AudioDirector *mAudioDirector; // 0x90 + void *qword98; + void *qwordA0; + void *qwordA8; + al::Scene *curScene; // 0xB0 + GameDataHolderAccessor mGameDataHolder; // 0xB8 + al::GamePadSystem *mGamepadSys; // 0xC0 + HakoniwaStateDemoOpening *mDemoOpening; // 0xC8 + HakoniwaStateDemoEnding *mDemoEnding; // 0xD0 + HakoniwaStateDemoWorldWarp *mDemoWorldWarp; // 0xD8 + HakoniwaStateSimpleDemo *mSimpleDemo; // 0xE0 + HakoniwaStateBootLoadData *mBootLoadData; // 0xE8 + HakoniwaStateDeleteScene *mDeleteScene; // 0xF0 + al::LayoutKit* mLytKit; // 0xF8 + + // al::initSceneCreator(al::IUseSceneCreator *,al::SequenceInitInfo const&,al::GameDataHolderBase *,al::AudioDirector *,al::ScreenCaptureExecutor *,alSceneFunction::SceneFactory *) .text 00000000009F2270 0000007C 00000050 FFFFFFFFFFFFFFF8 R . . . B . . + + + // undefined * * field_0x0; + // undefined padding_120[0x120]; + // al::Scene * curScene; + // undefined padding_8[0x8]; + // al::AudioDirector * field_0x90; + // undefined padding_24[0x24]; + // StageScene * stageScene; + // GameDataHolderAccessor *gameDataHolder; + // undefined padding_024[0x24]; + // HakoniwaStateDemoWorldWarp * stateDemoWorldWarp; + // undefined padding_192[0x192]; + // int nextScenarioNo; + // undefined padding_12[0x12]; + // al::WipeHolder * field_0x1b0; + // undefined padding_0024[0x24]; + // long * field_0x1d0; + // undefined padding_48[0x48]; + // WorldResourceLoader * worldResourceLoader; + // undefined padding_0x16[0x16]; + // undefined * field_0x220; + // undefined padding_0x144[0x144]; + // undefined * field_0x2b8; + // undefined padding_0x160[0x160]; + // undefined8 field_0x360; +}; \ No newline at end of file diff --git a/include/game/HakoniwaSequence/HakoniwaStateBootLoadData.h b/include/game/HakoniwaSequence/HakoniwaStateBootLoadData.h new file mode 100644 index 0000000..e6c8b3d --- /dev/null +++ b/include/game/HakoniwaSequence/HakoniwaStateBootLoadData.h @@ -0,0 +1,9 @@ +#pragma once + +class HakoniwaSequence; +#include "game/WorldList/WorldResourceLoader.h" + +class HakoniwaStateBootLoadData { + public: + +}; \ No newline at end of file diff --git a/include/game/HakoniwaSequence/HakoniwaStateDeleteScene.h b/include/game/HakoniwaSequence/HakoniwaStateDeleteScene.h new file mode 100644 index 0000000..e792cbf --- /dev/null +++ b/include/game/HakoniwaSequence/HakoniwaStateDeleteScene.h @@ -0,0 +1,8 @@ +#pragma once + +class HakoniwaSequence; + +class HakoniwaStateDeleteScene { + public: + +}; \ No newline at end of file diff --git a/include/game/HakoniwaSequence/HakoniwaStateDemoEnding.h b/include/game/HakoniwaSequence/HakoniwaStateDemoEnding.h new file mode 100644 index 0000000..a673936 --- /dev/null +++ b/include/game/HakoniwaSequence/HakoniwaStateDemoEnding.h @@ -0,0 +1,9 @@ +#pragma once + +class HakoniwaSequence; +#include "game/WorldList/WorldResourceLoader.h" + +class HakoniwaStateDemoEnding { + public: + // HakoniwaStateDemoOpening(HakoniwaSequence *, al::WipeHolder *, al::ScreenCaptureExecutor *, WorldResourceLoader *, BootLayout *, const al::LayoutInitInfo *, HakoniwaStateDeleteScene *, al::AsyncFunctorThread *, LoadLayoutCtrl *) +}; \ No newline at end of file diff --git a/include/game/HakoniwaSequence/HakoniwaStateDemoOpening.h b/include/game/HakoniwaSequence/HakoniwaStateDemoOpening.h new file mode 100644 index 0000000..d27fed7 --- /dev/null +++ b/include/game/HakoniwaSequence/HakoniwaStateDemoOpening.h @@ -0,0 +1,9 @@ +#pragma once + +class HakoniwaSequence; +#include "game/WorldList/WorldResourceLoader.h" + +class HakoniwaStateDemoOpening { + public: + // HakoniwaStateDemoOpening(HakoniwaSequence *, al::WipeHolder *, al::ScreenCaptureExecutor *, WorldResourceLoader *, BootLayout *, const al::LayoutInitInfo *, HakoniwaStateDeleteScene *, al::AsyncFunctorThread *, LoadLayoutCtrl *) +}; \ No newline at end of file diff --git a/include/game/HakoniwaSequence/HakoniwaStateDemoWorldWarp.h b/include/game/HakoniwaSequence/HakoniwaStateDemoWorldWarp.h new file mode 100644 index 0000000..babcf51 --- /dev/null +++ b/include/game/HakoniwaSequence/HakoniwaStateDemoWorldWarp.h @@ -0,0 +1,9 @@ +#pragma once + +class HakoniwaSequence; +#include "game/WorldList/WorldResourceLoader.h" + +class HakoniwaStateDemoWorldWarp { + public: + +}; \ No newline at end of file diff --git a/include/game/HakoniwaSequence/HakoniwaStateSimpleDemo.h b/include/game/HakoniwaSequence/HakoniwaStateSimpleDemo.h new file mode 100644 index 0000000..223ffca --- /dev/null +++ b/include/game/HakoniwaSequence/HakoniwaStateSimpleDemo.h @@ -0,0 +1,9 @@ +#pragma once + +class HakoniwaSequence; +#include "game/WorldList/WorldResourceLoader.h" + +class HakoniwaStateSimpleDemo { + public: + +}; \ No newline at end of file diff --git a/include/game/Info/QuestInfo.h b/include/game/Info/QuestInfo.h new file mode 100644 index 0000000..5fab185 --- /dev/null +++ b/include/game/Info/QuestInfo.h @@ -0,0 +1,39 @@ +#pragma once + +#include "al/actor/ActorInitInfo.h" +#include "al/actor/Placement.h" +#include "al/scene/SceneObjHolder.h" +#include "math/seadVector.h" +#include "prim/seadSafeString.h" + +class QuestInfo : public al::IUseSceneObjHolder { +public: + + QuestInfo(void); + + virtual al::SceneObjHolder* getSceneObjHolder(void) const override { return mSceneObjHolder; }; + + void setStageName(char const*); + void setLabel(char const*); + void isEqual(QuestInfo const*); + + void init(al::PlacementInfo const&,al::SceneObjHolder *); + void init(al::PlacementInfo const&,al::ActorInitInfo const&); + void init(al::ActorInitInfo const&); + + void end(void); + void copy(QuestInfo const*); + void clear(void); + + int mQuestID = -1; //0x8 + sead::Vector3f mShineTrans = sead::Vector3f::zero; // 0xC + bool mIsMainQuest; // 0x18 + al::SceneObjHolder *mSceneObjHolder; // 0x20 + sead::FixedSafeString<0x80> mLabel; // 0x28 + sead::FixedSafeString<0x80> mStageName; // 0xC0 + bool mIsSingle; // 0x158 + sead::FixedSafeString<0x80> mObjID; // 0x160 + sead::FixedSafeString<0x80> mPlacementStageName; // 0x1F8 +}; + +static_assert(sizeof(QuestInfo) == 0x290, "Quest Info Size"); \ No newline at end of file diff --git a/include/game/Info/QuestInfoHolder.h b/include/game/Info/QuestInfoHolder.h new file mode 100644 index 0000000..5e76eda --- /dev/null +++ b/include/game/Info/QuestInfoHolder.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include "QuestInfo.h" +#include "al/actor/Placement.h" +#include "al/hio/HioNode.h" +#include "al/scene/ISceneObj.h" +#include "al/scene/SceneObjHolder.h" +#include "al/string/StringTmp.h" +#include "container/seadPtrArray.h" + +class QuestInfoHolder : public al::ISceneObj, public al::HioNode { +public: + QuestInfoHolder(int count); + // QuestInfoHolder(int count) { + // QuestInfo* infoHolder = new QuestInfo[count](); + // for (int i = 0; i < count; i++) { + // infoHolder[i] = QuestInfo(); + // } + + // mQuestArr = &infoHolder; + // }; + + virtual const char *getSceneObjName(void) override; + virtual void initAfterPlacementSceneObj(al::ActorInitInfo const&) override; + + void validateQuest(QuestInfo const*); + void updateActiveList(int); + void tryFindQuest(al::PlacementInfo const&, al::SceneObjHolder *); + void tryFindQuest(QuestInfo const*); + void registerQuestInfo(QuestInfo const*); + void isActiveQuest(QuestInfo const*); + void invalidateQuest(QuestInfo const*); + void initSceneObjHolder(al::SceneObjHolder *); + void initAfterPlacementQuestObj(int); + void getQuestNum(int) const; + void getActiveQuestStageName(al::IUseSceneObjHolder const*) const; + al::StringTmp<0x80> getActiveQuestLabel(void) const; + void finalizeForScene(void); + void clearMainQuest(void); + void clearAll(void); + + int mMaxQuests; // 0x8 + QuestInfo **mQuestArr; // 0x10 + int unkInt1 = 0; // 0x18 + int unkInt2 = -1; // 0x1C + int unkInt3 = -1; // 0x20 + int mActiveQuestNo = -1; // 0x24 + QuestInfo** mActiveQuestList = nullptr; // 0x28 + int mActiveQuestCount = 0; // 0x30 + bool unkBool = 0; // 0x34 + void *unkPtr2 = nullptr; // 0x38 +}; + +static_assert(sizeof(QuestInfoHolder) == 0x40, "QuestInfoHolder Size"); \ No newline at end of file diff --git a/include/game/Info/ShineInfo.h b/include/game/Info/ShineInfo.h new file mode 100644 index 0000000..1ea023c --- /dev/null +++ b/include/game/Info/ShineInfo.h @@ -0,0 +1,39 @@ +#pragma once + +#include "sead/prim/seadSafeString.hpp" + +#include "al/LiveActor/LiveActor.h" +#include "al/util.hpp" + +struct ShineData; // unused class identical to this one (maybe) +struct QuestInfo; +struct UniqObjInfo; + +namespace al { + class IUseMessageSystem; +} + +class ShineInfo { + public: + ShineInfo(void); + + void clear(void); + void init(const char *, const char *, const char *, QuestInfo const *info, int shineId); + void initForHintPhoto(const al::LiveActor *, const al::IUseMessageSystem *, const al::ActorInitInfo &); + bool isEmpty(void) const; + bool isEqual(const ShineInfo *) const; + bool isEqual(const ShineData *) const; + void writeShineData(ShineData *) const; + void readShineData(ShineData *); + void readShineData(const UniqObjInfo *); + void copyShineInfo(const ShineInfo *); + + sead::FixedSafeString<0x80> stageName = sead::FixedSafeString<0x80>(); // 0x0 (Size: 0x98) + sead::FixedSafeString<0x80> objectId = sead::FixedSafeString<0x80>(); // 0xA0 + sead::FixedSafeString<0x80> scenObjId = sead::FixedSafeString<0x80>(); // 0x138 + + int shineId; // 1C8 + const QuestInfo *curQuest; // 1D0 + bool unkA; // 0x1D8 + unsigned long timeDay; // 0x1E0 +}; \ No newline at end of file diff --git a/include/game/InformationMovie/InformationMovieUpdater.h b/include/game/InformationMovie/InformationMovieUpdater.h new file mode 100644 index 0000000..4fae900 --- /dev/null +++ b/include/game/InformationMovie/InformationMovieUpdater.h @@ -0,0 +1,11 @@ +/** + * @file InformationMoviePlayer.h + * @brief unknown. + */ + +#pragma once + +class InformationMoviePlayer +{ + +}; \ No newline at end of file diff --git a/include/game/Input/InputSeparator.h b/include/game/Input/InputSeparator.h new file mode 100644 index 0000000..c248f50 --- /dev/null +++ b/include/game/Input/InputSeparator.h @@ -0,0 +1,37 @@ +#pragma once + +#include "al/scene/SceneObjHolder.h" + +class InputSeparator { +public: + InputSeparator(al::IUseSceneObjHolder const*,bool isNoSnapshot); + void checkDominant(bool); + void reset(void); + void update(void); + void updateForSnapShotMode(void); + + bool isHoldUiDown(void); + bool isHoldUiLeft(void); + bool isHoldUiRight(void); + bool isHoldUiUp(void); + bool isRepeatUiDown(void); + bool isRepeatUiLeft(void); + bool isRepeatUiRight(void); + bool isRepeatUiUp(void); + bool isTriggerDecrementPostProcessingFilterPreset(void); + bool isTriggerIncrementPostProcessingFilterPreset(void); + bool isTriggerSnapShotMode(void); + bool isTriggerUiDown(void); + bool isTriggerUiLeft(void); + bool isTriggerUiRight(void); + bool isTriggerUiUp(void); + + al::IUseSceneObjHolder* mSceneObjHolder; // 0x0 + bool unkBool; // 0x8 + bool unkBool2; // 0x9 + int unkInt; // 0xC + int unkInt2; // 0x10 + +}; + +static_assert(sizeof(InputSeparator) == 0x18, "InputSeparator size"); \ No newline at end of file diff --git a/include/game/Interfaces/IUseDimension.h b/include/game/Interfaces/IUseDimension.h new file mode 100644 index 0000000..0793ce1 --- /dev/null +++ b/include/game/Interfaces/IUseDimension.h @@ -0,0 +1,13 @@ +#pragma once +/** + * @file PlayerActorBase.h + * @brief Interfaces for Classes that use Dimensions +* Vtable loc: +*/ + +#include "al/actor/ActorDimensionKeeper.h" + +class IUseDimension { + public: + virtual ActorDimensionKeeper *getActorDimensionKeeper() const = 0; +}; \ No newline at end of file diff --git a/include/game/Interfaces/IUsePlayerCollision.h b/include/game/Interfaces/IUsePlayerCollision.h new file mode 100644 index 0000000..8f3eda4 --- /dev/null +++ b/include/game/Interfaces/IUsePlayerCollision.h @@ -0,0 +1,6 @@ +#pragma once + +class IUsePlayerCollision { + public: + +}; \ No newline at end of file diff --git a/include/game/Interfaces/IUsePlayerHack.h b/include/game/Interfaces/IUsePlayerHack.h new file mode 100644 index 0000000..5ecaa73 --- /dev/null +++ b/include/game/Interfaces/IUsePlayerHack.h @@ -0,0 +1,10 @@ +#pragma once +/** + * @file PlayerActorBase.h + * @brief Interfaces for Classes that use PlayerHack (PlayerActorBase) +* Vtable loc: +*/ + +class IUsePlayerHack { + +}; \ No newline at end of file diff --git a/include/game/Layouts/CoinCounter.h b/include/game/Layouts/CoinCounter.h new file mode 100644 index 0000000..2956385 --- /dev/null +++ b/include/game/Layouts/CoinCounter.h @@ -0,0 +1,29 @@ +#pragma once + +/* +* VTable Loc: 1CC3170 +*/ + +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" + +class CoinCounter : public al::LayoutActor { + public: + CoinCounter(char const*,al::LayoutInitInfo const&,bool); + void kill(void); + bool isWait(void) const; + bool tryStart(void); + void updateCountImmidiate(void); + bool tryEnd(void); + void startCountAnim(int); + void getCountTotalFromData(void); + void exeAppear(void); + void exeWait(void); + bool tryUpdateCount(void); + void exeEnd(void); + void exeAdd(void); + void exeSub(void); + void exeCountAnimAdd(void); + void exeCountAnimSub(void); + void getCountFromData(void); +}; \ No newline at end of file diff --git a/include/game/Layouts/CommonVerticalList.h b/include/game/Layouts/CommonVerticalList.h new file mode 100644 index 0000000..0035603 --- /dev/null +++ b/include/game/Layouts/CommonVerticalList.h @@ -0,0 +1,95 @@ +#pragma once + +#include "al/layout/LayoutActor.h" +#include "al/nerve/NerveExecutor.h" +#include "nn/ui2d/Texture.h" +#include "prim/seadSafeString.h" + +struct RollPartsData { + +}; + +class CommonVerticalList : public al::NerveExecutor { +public: + CommonVerticalList(al::LayoutActor*, al::LayoutInitInfo const&, bool); + ~CommonVerticalList(); + + void activate(void); + void addGroupAnimData(sead::FixedSafeString<64> const*,char const*); + void addStringData(sead::WFixedSafeString<512> const*,char const*); + void appearCursor(void); + void calcAnimRate(void); + void calcCursorPos(sead::Vector2f *); + void deactivate(void); + void decide(void); + void down(void); + void endCursor(void); + void getListPartsNum(void); + void getParts(int); + void getRollPartsSelected(int); + void getSelectedParts(void); + void hideAll(void); + void hideCursor(void); + void initData(int); + void initDataNoResetSelected(int); + void initDataWithIdx(int,int,int); + void jumpBottom(void); + void jumpTop(void); + void pageDown(void); + void pageUp(void); + void reject(void); + void rollLeft(void); + void rollRight(void); + void setEnableData(bool const*); + void setImageData(nn::ui2d::TextureInfo **,char const*); + void setRollPartsData(RollPartsData *); + void setRollPartsSelected(int,int); + void setSelectedIdx(int,int); + void startLoopActionAll(char const*,char const*); + void up(void); + void update(void); + void updateCursorPos(void); + void updateParts(void); + + bool isActive(void) const; + bool isDeactive(void) const; + bool isDecideEnd(void) const; + bool isRejectEnd(void) const; + + void exeActive(void); + void exeDeactive(void); + void exeDecide(void); + void exeDecideEnd(void); + void exeReject(void); + void exeRejectEnd(void); + + al::LayoutActor *mRootActor; // 0x10 + void *unkPtr1; // 0x18 + void *mListPartsArr; // 0x20 + struct CursorParts *mCursorParts; // 0x28 + struct ScrollBarParts *mScrollBarParts; // 0x30 + int mListPartsNum; // 0x38 + int mCurSelected; // 0x3C + int mIdx; // 0x40 + void *unkPtr2; // 0x48 + void *unkPtr3; // 0x50 + sead::Vector2f mCursorPos; // 0x58 + void *unkPtr4; // 0x60 + void *unkPtr5; // 0x68 + sead::WFixedSafeString<0x200> **mStringDataArr; // 0x70 + sead::FixedSafeString<0x90> **mPaneNameList; // 0x78 + void *unkPtr8; // 0x80 + void *unkPtr9; // 0x88 + const bool *mIsEnableData; // 0x90 + int mStringDataCount; // 0x98 + int unkInt2; // 0x9C + void *unkPtr12; // 0xA0 + void *unkPtr13; // 0xA8 + void *unkPtr14; // 0xB0 + void *unkPtr15; // 0xB8 + void* RollPartsArr; // 0xC0 + void* unkPtrX; // 0xC8 + +}; + +static_assert(sizeof(CommonVerticalList) == 0xD0, "CommonVerticalList size"); \ No newline at end of file diff --git a/include/game/Layouts/MapLayout.h b/include/game/Layouts/MapLayout.h new file mode 100644 index 0000000..844b510 --- /dev/null +++ b/include/game/Layouts/MapLayout.h @@ -0,0 +1,58 @@ +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" +#include "sead/math/seadMatrix.h" +#include "sead/math/seadVector.h" + +class MapIconLayout{}; +class IconType {}; +class MapIconInfo {}; + +struct MapLayout : public al::LayoutActor { + MapLayout(const char* name, const al::LayoutInitInfo& initInfo); + void changePrintWorld(int); + void loadTexture(); + void reset(); + void appear(); + void moveFocusLayout(const sead::Vector3f&, const sead::Vector2f&); + void updateST(); + void addAmiiboHint(); + void appearAmiiboHint(); + void end(); + void updatePlayerPosLayout(); + void appearWithHint(); + void appearWithMoonRockDemo(); + void appearCollectionList(); + bool isEnd(); + bool isEnableCheckpointWarp(); + void changeOut(); + void changeIn(); + void control(); + void updateLine(al::LayoutActor*); + void appearParts(); + void startNumberAction(); + void calcSeaOfTreeIconPos(sead::Vector3f*); + void setLocalTransAndAppear(MapIconLayout*, MapIconInfo*, const sead::Vector3f&, IconType, bool); + void calcMapTransAndAppear(MapIconLayout*, MapIconInfo*, const sead::Vector3f&, IconType, bool); + void scroll(const sead::Vector2f&); + void addSize(const sead::Vector2f&); + bool isAppear(); + sead::Matrix44f* getViewProjMtx(); + sead::Matrix44f* getProjMtx(); + void updateIconLine(al::LayoutActor*, const sead::Vector3f&, const sead::Vector2f&); + void focusIcon(const MapIconInfo*); + void lostFocusIcon(MapIconLayout*); + void tryCalcNorthDir(sead::Vector3f*); + const char* getSceneObjName() { + return "マップレイアウト"; + } + + void exeAppear(); + void exeWait(); + void exeHintInitWait(); + void exeHintAppear(); + void exeHintDecideIconAppear(); + void exeHintDecideIconWait(); + void exeHintPressDecide(); + void exeEnd(); + void exeChangeOut(); +}; diff --git a/include/game/Layouts/MapMini.h b/include/game/Layouts/MapMini.h new file mode 100644 index 0000000..e6a606c --- /dev/null +++ b/include/game/Layouts/MapMini.h @@ -0,0 +1,16 @@ +#pragma once + +#include "al/layout/LayoutActor.h" +class MapMini : public al::LayoutActor { +public: + MapMini(al::LayoutInitInfo const&,al::PlayerHolder const*); + void appearSlideIn(void); + void end(void); + void calcNearHintTrans(void); + + bool isEnd(void) const; + + void exeAppear(void); + void exeWait(void); + void exeEnd(void); +}; \ No newline at end of file diff --git a/include/game/Layouts/SimpleLayoutMenu.h b/include/game/Layouts/SimpleLayoutMenu.h new file mode 100644 index 0000000..feeb2f9 --- /dev/null +++ b/include/game/Layouts/SimpleLayoutMenu.h @@ -0,0 +1,20 @@ +#pragma once + +#include "al/layout/LayoutActor.h" + +class SimpleLayoutMenu : public al::LayoutActor { +public: + SimpleLayoutMenu(al::LayoutActor *,char const*,char const*,al::LayoutInitInfo const&,char const*); + SimpleLayoutMenu(char const*,char const*,al::LayoutInitInfo const&,char const*,bool); + + void exeAppear(void); + void exeEnd(void); + void exeEndWait(void); + void exeWait(void); + void isAppearOrWait(void); + void isEndWait(void); + void isWait(void); + void startAppear(char const*); + void startEnd(char const*); + +}; diff --git a/include/game/Player/Actions/PlayerActionGroundMoveControl.h b/include/game/Player/Actions/PlayerActionGroundMoveControl.h new file mode 100644 index 0000000..bdd807b --- /dev/null +++ b/include/game/Player/Actions/PlayerActionGroundMoveControl.h @@ -0,0 +1,73 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "game/Interfaces/IUsePlayerCollision.h" +#include "game/Player/PlayerConst.h" +#include "game/Player/PlayerInput.h" + +class PlayerActionGroundMoveControl { +public: + PlayerActionGroundMoveControl(al::LiveActor *,PlayerConst const*,PlayerInput const*,IUsePlayerCollision const*); + void initDash(struct IJudge *,float,int); + void setupDash(float,int); + void setup(float maxSpeed, float minSpeed, int normalAccelFrame, int stickOnBrakeFrame, int normalBrakeFrame, float gravityMove, float breakSpeed, int breakOnCounterBorder); + void appear(void); + void reset(sead::Vector3 const&); + void calcInitBrakeOnCounter(void); + void update(void); + void updateSkateMove(void); + void updateNormalMove(void); + void updateNormalAndSnap(sead::Vector3 *); + void calcTurnTiltRate(void); + void calcMoveInput(sead::Vector3 *,sead::Vector3 const&); + void isActiveSquatBrake(void); + void updateHillAffect(sead::Vector3 const&,sead::Vector3 const&,bool); + void calcMaxSpeed(float); + void calcAccelRate(float); + void updatePoseUpFront(sead::Vector3 const&, sead::Vector3 const&, float); + + void *qword0; + PlayerConst *mPlayerConst; + void *qword10; + void *qword18; + void *qword20; + bool byte28; + int dword2C; + int dword30; + int dword34; + float mMaxSpeed; + float mMinSpeed; + void *qword40; + int mNormalAccelFrame; + int mStickOnBrakeFrame; + int mNormalBrakeFrame; + float mGravityMove; + float mBreakSpeed; + int mBreakOnCounterBorder; + bool gap60[8]; + void *qword68; + void *qword70; + bool byte78; + bool gap79[3]; + int dword7C; + bool byte80; + bool gap81[3]; + void *qword84; + int dword8C; + void *qword90; + int dword98; + bool byte9C; + bool gap9D[3]; + int dwordA0; + bool byteA4; + bool gapA5[3]; + void *qwordA8; + void *qwordB0; + int dwordB8; + s16 wordBC; + bool gapBE[2]; + int dwordC0; + bool byteC4; + bool gapC5[15]; + s16 wordD4; +}; \ No newline at end of file diff --git a/include/game/Player/Attacks/PlayerSpinCapAttack.h b/include/game/Player/Attacks/PlayerSpinCapAttack.h new file mode 100644 index 0000000..605d27a --- /dev/null +++ b/include/game/Player/Attacks/PlayerSpinCapAttack.h @@ -0,0 +1,47 @@ +#pragma once + +#include "sead/math/seadVector.h" + +#include "al/sensor/HitSensor.h" + +#include "game/Player/HackCap.h" +#include "game/Player/PlayerConst.h" +#include "game/Player/PlayerInput.h" +#include "game/Player/PlayerAnimator.h" + +class PlayerTrigger; +class PlayerJudgePreInputCapThrow; +class PlayerCounterAfterCapCatch; + +class PlayerSpinCapAttack { + public: + PlayerSpinCapAttack(HackCap *, const PlayerConst *, const PlayerTrigger *, const PlayerInput *, const PlayerCounterAfterCapCatch *, const PlayerJudgePreInputCapThrow *); + void clearAttackInfo(void); + void setupAttackInfo(void); + void startCapSpinAttack(PlayerAnimator *, PlayerInput const *); + void startCapSpinAttackAir(PlayerAnimator *, PlayerInput const *); + void startCapSpinAttackSwim(PlayerAnimator *, PlayerInput const *); + void startSpinSeparate(PlayerAnimator *, PlayerInput const *); + void startSpinSeparateSwim(PlayerAnimator *, PlayerInput const *); + void startSpinSeparateSwimSurface(PlayerAnimator *, PlayerInput const *); + void startCapThrow(sead::Vector3f const & front, sead::Vector3f const & up, float speed, bool, sead::Vector3f const& unused); + void attackSpinMsg(al::HitSensor *, al::HitSensor *); + bool tryCancelCapState(PlayerAnimator *); + bool tryStartCapSpinGroundMiss(PlayerAnimator *); + bool tryStartCapSpinAirMiss(PlayerAnimator *); + bool isCapSpinAttack(void) const; + bool isValidAttackSensor(const PlayerAnimator *) const; + bool isEnablePlaySpinCapMiss(const PlayerAnimator *) const; + bool isSeparateSingleSpin(void) const; + bool isThrowSwingRightDir(void) const; + int getThrowFrameGround(void) const; + int getThrowFrameAir(void) const; + int getThrowFrameSwim(void) const; + + HackCap *mHackCap; // 0x0 + PlayerConst *mPlayerConst; // 0x8 + PlayerTrigger *mPlayerTrigger; // 0x10 + PlayerInput *mPlayerInput; // 0x18 + PlayerCounterAfterCapCatch *mCounterAfterCapCatch; // 0x20 + PlayerJudgePreInputCapThrow *jPreInputCapThrow; // 0x28 +}; \ No newline at end of file diff --git a/include/game/Player/CapFunction.h b/include/game/Player/CapFunction.h new file mode 100644 index 0000000..bc77a8f --- /dev/null +++ b/include/game/Player/CapFunction.h @@ -0,0 +1,10 @@ +#pragma once + +#include "HackCap.h" +#include "PlayerAnimator.h" + +class CapFunction +{ + public: + static void putOnCapPlayer(HackCap *, PlayerAnimator *); +}; \ No newline at end of file diff --git a/include/game/Player/HackCap.h b/include/game/Player/HackCap.h new file mode 100644 index 0000000..eb7fa57 --- /dev/null +++ b/include/game/Player/HackCap.h @@ -0,0 +1,206 @@ +#pragma once +/** + * @file HackCap.h + * @brief Main Class for HackCap (Cappy) +* Vtable loc: 1D75520 +*/ + +#include "al/LiveActor/LiveActor.h" +#include "al/sensor/HitSensor.h" +#include "al/sensor/SensorMsg.h" + +#include "game/Player/PlayerInput.h" +#include "game/Interfaces/IUsePlayerCollision.h" + +#include "HackCapThrowParam.h" +#include "HackCap/HackCapJointControlKeeper.h" + +class PlayerWallActionHistory; +class PlayerCapActionHistory; +class PlayerEyeSensorHitHolder; +class IUsePlayerHeightCheck; +class PlayerWetControl; +class PlayerJointControlKeeper; +class HackCapJudgePreInputSeparateThrow; +class HackCapJudgePreInputSeparateJump; +class PlayerSeparateCapFlag; + +class CapTargetInfo; + +class PlayerActorHakoniwa; // use a stub instead of the actual class file + + +#define HACKSIZE sizeof(al::LiveActor) + +class HackCap : public al::LiveActor { + public: + + HackCap(al::LiveActor const*,char const*,PlayerInput const*, struct PlayerAreaChecker const*, PlayerWallActionHistory const*, PlayerCapActionHistory const*, PlayerEyeSensorHitHolder const*,PlayerSeparateCapFlag const*,IUsePlayerCollision const*, IUsePlayerHeightCheck const*, PlayerWetControl const*, PlayerJointControlKeeper const*, HackCapJudgePreInputSeparateThrow *, HackCapJudgePreInputSeparateJump *); + + enum SwingHandType { + Left, + Right + }; + + void init(al::ActorInitInfo const&); + void hide(bool); + void movement(void); + void updateShadowMaskOffset(void); + void control(void); + void updateTargetLayout(void); + void updateCollider(void); + void updateFrameOutLayout(void); + void attackSpin(al::HitSensor *,al::HitSensor *,float); + void prepareLockOn(al::HitSensor *); + void sendMsgStartHack(al::HitSensor *); + void receiveRequestTransferHack(al::HitSensor *,al::HitSensor *); + void startThrowSeparatePlayHack(al::HitSensor *, sead::Vector3f const&, sead::Vector3f const&,float); + void startHack(void); + void emitHackStartEffect(void); + void noticeHackMarioEnter(void); + void noticeHackDemoPuppetableEnd(void); + void recordHack(void); + void addHackStartDemo(void); + void addLockOnKeepDemo(void); + void syncHackDamageVisibility(bool); + void endHack(void); + void startSpinAttack(char const*); + void startThrow(bool, sead::Vector3f const&, sead::Vector3f const&,float, sead::Vector2f const&, sead::Vector2f const&, sead::Vector3f const&,bool, sead::Vector3f const&, HackCap::SwingHandType,bool,float,int); + void startThrowSeparatePlay( sead::Vector3f const&, sead::Vector3f const&,float,bool); + void startThrowSeparatePlayJump( sead::Vector3f const&, sead::Vector3f const&,float); + void startCatch(char const*,bool, sead::Vector3f const&); + void forcePutOn(void); + void forceHack(al::HitSensor *, CapTargetInfo const*); // :eyes: + void resetLockOnParam(void); + void setupStartLockOn(void); + void cancelCapState(void); + void requestReturn(bool *); + void tryReturn(bool,bool *); + void updateCapPose(void); + void followTarget(void); + void syncPuppetSilhouette(void); + void recordCapJump(PlayerWallActionHistory *); + void getFlyingSpeedMax(void); + void getThrowSpeed(void); + void requestLockOnHitReaction(CapTargetInfo const*,char const*); + void startPuppet(void); + void endPuppet(void); + void hidePuppetCap(void); + void showPuppetCap(void); + void hidePuppetCapSilhouette(void); + void showPuppetCapSilhouette(void); + void startPuppetCheckpointWarp(void); + void startHackShineGetDemo(void); + void endHackThrowAndReturnHack(void); + void endHackShineGetDemo(void); + void calcHackFollowTrans( sead::Vector3f *,bool); + void makeFollowMtx(sead::Matrix34 *); + void updateCapEyeShowHide(bool,int); + void activateInvincibleEffect(void); + void syncInvincibleEffect(bool); + void updateSeparateMode(PlayerSeparateCapFlag const*); + void startRescuePlayer(void); + void prepareCooperateThrow(void); + void requestForceFollowSeparateHide(void); + void calcSeparateHideSpeedH(sead::Vector3f const&); + void updateModelAlphaForSnapShot(void); + void getPadRumblePort(void); + void updateThrowJoint(void); + void setupThrowStart(void); + void getThrowHeight(void); + void checkEnableThrowStartSpace(sead::Vector3f *, sead::Vector3f *, sead::Vector3f *, sead::Vector3f const&,float,float,bool, sead::Vector3f const&); + void updateWaterArea(void); + void getThrowRange(void); + void getThrowBrakeTime(void); + void startThrowCapEyeThrowAction(void); + void tryCollideReflectReaction(void); + void tryCollideWallReaction(void); + void changeThrowParamInWater(int,bool); + void addCurveOffset(void); + void tryAppendAttack(void); + void tryCollideWallReactionSpiral(void); + void endThrowSpiral(void); + void tryCollideWallReactionReflect(void); + void tryCollideWallReactionRollingGround(void); + void rollingGround(void); + void tryChangeSeparateThrow(void); + void getThrowBackSpeed(void); + void updateLavaSurfaceMove(void); + void tryCollideWallReactionStay(void); + void getThrowStayTime(void); + void getThrowStayTimeMax(void); + void getThrowSpeedAppend(void); + void getThrowRangeAppend(void); + void tryCollideWallLockOn(void); + void endHackThrowAndReturnHackOrHide(void); + void clearThrowType(void); + void calcReturnTargetPos( sead::Vector3f *); + void attackSensor(al::HitSensor *,al::HitSensor *); + void stayRollingOrReflect(void); + bool receiveMsg(al::SensorMsg const*,al::HitSensor *,al::HitSensor *); + void endMove(void); + void prepareTransferLockOn(al::HitSensor *); + void collideThrowStartArrow(al::HitSensor *, sead::Vector3f const&, sead::Vector3f const&, sead::Vector3f const&); + void trySendAttackCollideAndReaction(bool *); + void stayWallHit(void); + void endHackThrow(void); + + bool isFlying(void) const; + bool isNoPutOnHide(void) const; + bool isEnableThrow(void) const; + bool isEnableSpinAttack(void) const; + bool isSpinAttack(void) const; + bool isEnableRescuePlayer(void) const; + bool isRescuePlayer(void) const; + bool isEnableHackThrow(bool *) const; + bool isSeparateHipDropLand(void) const; + bool isSeparateHide(void) const; + bool isSeparateThrowFlying(void) const; + bool isEnableThrowSeparate(void) const; + bool isHoldInputKeepLockOn(void) const; + bool isRequestableReturn(void) const; + bool isLockOnEnableHackTarget(void) const; + bool isWaitHackLockOn(void) const; + bool isCatched(void) const; + bool isHide(void) const; + bool isPutOn(void) const; + bool isLockOnInterpolate(void) const; + bool isEnablePreInput(void) const; + bool isForceCapTouchJump(void) const; + bool isHackInvalidSeparatePlay(void) const; + bool isHoldSpinCapStay(void) const; + bool isThrowTypeSpiral(void) const; + bool isThrowTypeRolling(void) const; + bool isEnableHackThrowAutoCatch(void) const; + bool isEnableCapTouchJumpInput(void) const; + + void exeLockOn(void); + void exeHack(void); + void exeSpinAttack(void); + void exeCatch(void); + void exeTrample(void); + void exeTrampleLockOn(void); + void exeRescue(void); + void exeHide(void); + void exeThrowStart(void); + void exeThrow(void); + void exeThrowBrake(void); + void exeThrowSpiral(void); + void exeThrowTornado(void); + void exeThrowRolling(void); + void exeThrowRollingBrake(void); + void exeThrowStay(void); + void exeThrowAppend(void); + void exeRebound(void); + void exeReturn(void); + void exeBlow(void); + + void *unkPtr1; // 0x108 + void *unkPtr2; // 0x110 + al::LiveActor *mLockOnEyes; // 0x118 + al::LiveActor *mCapEyes; // 0x120 + PlayerActorHakoniwa *mPlayerActor; // 0x128 + unsigned char padding_220[0x220-0x130]; + HackCapThrowParam throwParam; // 0x220 + HackCapJointControlKeeper *mJointKeeper; // 0x2E0 +}; \ No newline at end of file diff --git a/include/game/Player/HackCap/CapTargetInfo.h b/include/game/Player/HackCap/CapTargetInfo.h new file mode 100644 index 0000000..8bcb0df --- /dev/null +++ b/include/game/Player/HackCap/CapTargetInfo.h @@ -0,0 +1,6 @@ +#pragma once + +class CapTargetInfo { + public: + +}; \ No newline at end of file diff --git a/include/game/Player/HackCap/HackCapJointControlKeeper.h b/include/game/Player/HackCap/HackCapJointControlKeeper.h new file mode 100644 index 0000000..43e8545 --- /dev/null +++ b/include/game/Player/HackCap/HackCapJointControlKeeper.h @@ -0,0 +1,17 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "sead/math/seadQuat.h" + +class HackCapJointControlKeeper { + public: + HackCapJointControlKeeper(void); + void initCapJointControl(al::LiveActor *); + void initDisplayCapJointControl(al::LiveActor *); + void updateRotateThrowZ(float,float); + void updateRotateStayZ(float); + void updateRotateY(float); + + sead::Vector3f mJointRot = sead::Vector3f(); + float mSkew = 0; +}; diff --git a/include/game/Player/HackCapThrowParam.h b/include/game/Player/HackCapThrowParam.h new file mode 100644 index 0000000..11bc3e4 --- /dev/null +++ b/include/game/Player/HackCapThrowParam.h @@ -0,0 +1,34 @@ +#pragma once + +#include "types.h" +#include "al/LiveActor/LiveActor.h" + +class HackCapThrowParam { // stores parameters used in calculating the way HackCap is thrown + public: + HackCapThrowParam(al::LiveActor *); + + f32* mHackThrowHeight; // 投げる高さ + f32* mMaxVel; // 最高速度 + f32* mConstThrowSpeed; // 連続投げ速度 + s32* mBreakTime; // ブレーキ時間 + f32* mMaxDist; // 到達距離 + s32* mEndpointStopTime; // 端点停止時間 + s32* mMaxEndpointStopTime; // 最大端点停止時間 + f32* mReturnStrength; // 戻り強さ + f32* mMaxRetSpeed; // 戻り最高速度 + f32* mTurnAngleLimit; // ターン限界角度 + f32* mWaterMaxSpeed; // [水中]最高速度 + f32* mWaterDist; // [水中]到達距離 + s32* mWaterBreakTime; // [水中]ブレーキ時間 + f32* mWaterMaxRetSpeed; // [水中]戻り最高速度 + f32* mTornadoDist; // [竜巻投げ]到達距離 + f32* mTornadoMaxDist; // [竜巻投げ]最高到達距離 + s32* mTornadoReflectTime; // [竜巻投げ]反射時間 + f32* mRollSpeed; // [転がし投げ]速度 + f32* mRollDistTop; // [転がし投げ]到達距離[上] + f32* mRollDistBottom; // [転がし投げ]到達距離[下] + s32* mRollBrakeTimeTop; // [転がし投げ]ブレーキ時間[上] + s32* mRollBrakeTimeBottom; // [転がし投げ]ブレーキ時間[下] + f32* mRollGroundGroundedPoseTrack; // [転がし投げ]姿勢追従[接地] + f32* mRollGroundAerialPoseTrack; // [転がし投げ]姿勢追従[空中] +}; \ No newline at end of file diff --git a/include/game/Player/PlayerActorBase.h b/include/game/Player/PlayerActorBase.h new file mode 100644 index 0000000..01f0baf --- /dev/null +++ b/include/game/Player/PlayerActorBase.h @@ -0,0 +1,17 @@ +#pragma once +/** + * @file PlayerActorBase.h + * @brief base class for PlayerActor +* Vtable loc: 1D77980 +*/ + +#include "PlayerHackKeeper.h" +#include "al/LiveActor/LiveActor.h" +#include "game/Interfaces/IUsePlayerHack.h" + +class PlayerActorBase : public al::LiveActor , public IUsePlayerHack { + public: + PlayerHackKeeper *getPlayerHackKeeper() const; + void movement(void); + int getPortNo(); +}; \ No newline at end of file diff --git a/include/game/Player/PlayerActorHakoniwa.h b/include/game/Player/PlayerActorHakoniwa.h new file mode 100644 index 0000000..02458ae --- /dev/null +++ b/include/game/Player/PlayerActorHakoniwa.h @@ -0,0 +1,74 @@ +#pragma once +/** + * @file PlayerActorBase.h + * @brief Main Class for the PlayerActor (Mario) + * Player Pose: TQGMSV +* Vtable loc: 1D780C0 +*/ + +#include "game/Interfaces/IUseDimension.h" +#include "al/actor/ActorDimensionKeeper.h" +#include "al/actor/ActorInitInfo.h" +#include "PlayerActorBase.h" +#include "PlayerPuppet.h" +#include "PlayerInput.h" +#include "PlayerAnimator.h" +#include "HackCap.h" +#include "PlayerModelKeeper.h" +#include "PlayerColliderHakoniwa.h" +#include "PlayerConst.h" +#include "PlayerHackKeeper.h" +#include "PlayerInfo.h" +#include "PlayerModelChangerHakoniwa.h" +#include "PlayerFormSensorCollisionArranger.h" +#include "PlayerInitInfo.h" + +#include "Attacks/PlayerSpinCapAttack.h" + +#define PACTORSIZE 0xC8 + +class PlayerActorHakoniwa : public PlayerActorBase , public IUseDimension { + public: + int *getPortNo(void) const; + PlayerHackKeeper *getPlayerHackKeeper() const; + void attackSensor(al::HitSensor *target, al::HitSensor *source); + void startDemoPuppetable(void); + void startPlayerPuppet(void); + void initPlayer(al::ActorInitInfo const&, PlayerInitInfo const&); + + unsigned char padding[0x18]; // 0x108 + PlayerInfo *mPlayerInfo; // 0x128 + PlayerConst *mPlayerConst; // 0x130 + PlayerInput *mPlayerInput; //0x138 + unsigned char padding_148[0x8]; // PlayerTrigger + HackCap *mHackCap; // 0x148 + ActorDimensionKeeper *mDimKeeper; // 0x150 + PlayerModelKeeper *mPlayerModelKeeper; // 0x158 + PlayerModelChangerHakoniwa *mModelChanger; // 0x160 + PlayerAnimator *mPlayerAnimator; // 0x168 + PlayerColliderHakoniwa *mPlayerCollider; // 0x170 + PlayerPuppet *mPlayerPuppet; // 0x178 + // 0x180 PlayerAreaChecker + // 0x188 WaterSurfaceFinder + // 0x190 unk + // 0x198 unk + // 0x1A0 unk + // 0x1A8 unk + // 0x1B0 unk + // 0x1B8 unk + // 0x1C0 unk + // 0x1C8 unk + // 0x1D0 unk + // 0x1D8 unk + // 0x1E0 unk + // 0x1E8 unk + // 0x1F0 unk + // 0x1F8 PlayerBindKeeper + unsigned char padding_208[0x208 - 0x180]; + PlayerHackKeeper *mHackKeeper; // 0x208 + PlayerFormSensorCollisionArranger *mCollArranger; // 0x210 + void *unkPtr2; // 0x218 + void *unkPtr3; // 0x220 + PlayerSpinCapAttack *mSpinCapAttack; // 0x228 + +}; \ No newline at end of file diff --git a/include/game/Player/PlayerAnimControlRun.h b/include/game/Player/PlayerAnimControlRun.h new file mode 100644 index 0000000..4464b60 --- /dev/null +++ b/include/game/Player/PlayerAnimControlRun.h @@ -0,0 +1,29 @@ +#pragma once + +#include "sead/math/seadVector.h" +#include "PlayerAnimator.h" +#include "PlayerConst.h" + +class IJudge; + +class PlayerEffect; + +// 0x40 in size +class PlayerAnimControlRun { + public: + PlayerAnimControlRun(PlayerAnimator *, PlayerConst const*, IJudge const*, PlayerEffect *, bool isMoon); + void reset(float, bool); + bool isAnimDashFast(void); + void update(float, sead::Vector3f const &); + + PlayerAnimator *mPlayerAnimator; // 0x0 + PlayerEffect *mPlayerEffect; // 0x8 + PlayerConst *mPlayerConst; // 0x10 + IJudge *mJudge; // 0x18 + bool unk1; // 0x20 + float unk2; // 0x24 + int unk3; // 0x28 + bool isMoveNormal; // 0x30 inverse of isMoon arg + undefined8 unk; // 0x32(?) + char *animName; // 0x38 +}; \ No newline at end of file diff --git a/include/game/Player/PlayerAnimFrameCtrl.h b/include/game/Player/PlayerAnimFrameCtrl.h new file mode 100644 index 0000000..c480d20 --- /dev/null +++ b/include/game/Player/PlayerAnimFrameCtrl.h @@ -0,0 +1,9 @@ +#pragma once +#include "al/LiveActor/LiveActor.h" +#include "sead/prim/seadSafeString.hpp" + +class PlayerAnimFrameCtrl { + public: + const char *getActionName(void) const; + void startAction(al::LiveActor *actor, sead::SafeString const &actionName); +}; \ No newline at end of file diff --git a/include/game/Player/PlayerAnimator.h b/include/game/Player/PlayerAnimator.h new file mode 100644 index 0000000..5341038 --- /dev/null +++ b/include/game/Player/PlayerAnimator.h @@ -0,0 +1,50 @@ +#pragma once + +#include "PlayerAnimFrameCtrl.h" +#include "PlayerModelHolder.h" +#include "sead/prim/seadSafeString.hpp" + +class PlayerAnimator { + public: + void startAnim(const sead::SafeString &animName); + void startSubAnim(const sead::SafeString &animName); + void startSubAnimOnlyAir(const sead::SafeString &animName); + void startUpperBodyAnimAndHeadVisKeep(const sead::SafeString &animName); + void startAnimDead(void); // chooses one of the 5 death animations and starts that animation + void endSubAnim(void); + + void updateAnimFrame(void); + void clearUpperBodyAnim(void); + + bool isAnim(const sead::SafeString &animName) const; + bool isSubAnim(sead::SafeString const &subAnimName) const; + bool isSubAnimEnd(void) const; + bool isUpperBodyAnimAttached(void) const; + + float getAnimFrame() const; + float getAnimFrameMax() const; + float getAnimFrameRate() const; + float getSubAnimFrame() const; + float getSubAnimFrameMax() const; + float getBlendWeight(int index); + + void setAnimRate(float); + void setAnimRateCommon(float); + void setAnimFrame(float); + void setAnimFrameCommon(float); + void setSubAnimFrame(float); + void setSubAnimRate(float); + void setBlendWeight(float,float,float,float,float,float); + void setModelAlpha(float); + void setPartsAnimRate(float, char const*); + void setPartsAnimFrame(float, char const*); + + + PlayerModelHolder *mModelHolder; // 0x0 + al::LiveActor *mPlayerDeco; // 0x8 + void *unkPtr; // 0x10 + PlayerAnimFrameCtrl *mAnimFrameCtrl; // 0x18 + sead::SafeString curAnim; // 0x20 + unsigned char padding_78[0x78 - 0x30]; + sead::SafeString curSubAnim; //0x78 +}; \ No newline at end of file diff --git a/include/game/Player/PlayerCameraTarget.h b/include/game/Player/PlayerCameraTarget.h new file mode 100644 index 0000000..996828d --- /dev/null +++ b/include/game/Player/PlayerCameraTarget.h @@ -0,0 +1,12 @@ +#pragma once + +#include "al/actor/ActorCameraTarget.h" + +class PlayerCameraTarget : public al::ActorCameraTarget +{ + public: + PlayerCameraTarget(al::LiveActor const *player); + + float unk1; + float unk2; +}; \ No newline at end of file diff --git a/include/game/Player/PlayerCollider.h b/include/game/Player/PlayerCollider.h new file mode 100644 index 0000000..cc0ef88 --- /dev/null +++ b/include/game/Player/PlayerCollider.h @@ -0,0 +1,7 @@ +#pragma once + +class PlayerCollider { + public: + void calcBoundingRadius(float *); + void setCollisionShapeScale(float); +}; \ No newline at end of file diff --git a/include/game/Player/PlayerColliderHakoniwa.h b/include/game/Player/PlayerColliderHakoniwa.h new file mode 100644 index 0000000..eb74486 --- /dev/null +++ b/include/game/Player/PlayerColliderHakoniwa.h @@ -0,0 +1,18 @@ +#pragma once + +#include "types.h" +#include "PlayerCollider.h" +#include "game/Interfaces/IUsePlayerCollision.h" + +class PlayerColliderHakoniwa : public IUsePlayerCollision { + public: + f32 getColliderRadius() const; + f32 getColliderDiskHalfHeight() const; + f32 getSafetyCeilSpace() const; + f32 getCeilCheckHeight() const; + f32 getGroundHeight() const; + f32 getShadowDropHeight() const; + f32 getFallDistance() const; + + PlayerCollider *getPlayerCollider() const; +}; \ No newline at end of file diff --git a/include/game/Player/PlayerConst.h b/include/game/Player/PlayerConst.h new file mode 100644 index 0000000..1acf0f2 --- /dev/null +++ b/include/game/Player/PlayerConst.h @@ -0,0 +1,1231 @@ +#pragma once + +#include "types.h" + +class PlayerConst { // 0x9A8 i think is PlayerConst's size, which is every single entry plus some extra space for something + public: // note: these functions are in the order that the addresses are in, so doing a bit of messing around with these declarations should also allow for the full header to be decompiled + virtual float getGravity(void) const; + virtual float getFrictionAttack(void) const; + virtual float getPushPower(void) const; + virtual float getWaitPoseDegreeMax(void) const; + virtual float getHillPoseDegreeMax(void) const; + virtual float getTiltPoseDegreeMax(void) const; + virtual float getSlerpQuatRate(void) const; + virtual float getSlerpQuatRateWait(void) const; + virtual float getSlerpQuatGrav(void) const; + virtual int getPreInputFrameCapThrow(void) const; + virtual int getEnableActionFrameCapCatch(void) const; + virtual float getJumpPowerCapCatch(void) const; + virtual float getJumpGravityCapCatch(void) const; + virtual int getRunTimeContinuousThrow(void) const; + virtual float getRunSpeedMaxContinuousThrow(void) const; + virtual int getRunAccelFrameContinuousThrow(void) const; + virtual float getSeparateCheckHeight(void) const; + virtual float getSeparateOffsetLerpRate(void) const; + virtual float getSeparateEnableThrowHeight(void) const; + virtual float getTall(void) const; + virtual float getCollisionRadius(void) const; + virtual float getCollisionRadiusSquat(void) const; + virtual float getCollisionRadiusStand(void) const; + virtual float getCollisionSmallStepHeight(void) const; + virtual float getCollisionResetLimit(void) const; + virtual float getReflectCeilingPower(void) const; + virtual float getReflectTossPower(void) const; + virtual float getReflectUpperPunchScaleH(void) const; + virtual float getCollisionHitDownAngleH(void) const; + virtual float getCollisionHitDownEscapeAngleV(void) const; + virtual float getShadowDropHeightScale(void) const; + virtual float getShadowDropNormalAdd(void) const; + virtual float getShadowDropLengthMin(void) const; + virtual float getShadowDropLengthMax(void) const; + virtual float getShadowDropLengthExtend(void) const; + virtual float getGravityDamage(void) const; + virtual float getHopPowerDamage(void) const; + virtual float getPushPowerDamage(void) const; + virtual int getDamageCancelFrame(void) const; + virtual int getDamageInvalidCount(void) const; + virtual int getDamageInvalidCountRecovery(void) const; + virtual int getDamageInvalidCountAbyss(void) const; + virtual float getNormalMinSpeed2D(void) const; + virtual float getNormalMaxSpeed2D(void) const; + virtual float getDashMaxSpeed2D(void) const; + virtual int getNormalAccelFrame2D(void) const; + virtual int getDashAccelFrame2D(void) const; + virtual int getNormalDashAnimFrame2D(void) const; + virtual int getNormalBrakeFrame2D(void) const; + virtual int getStickOnBrakeFrame2D(void) const; + virtual int getBrakeTurnStartFrame2D(void) const; + virtual float getTurnEndSpeedRate2D(void) const; + virtual float getJumpPowerMin2DArea(void) const; + virtual float getJumpPowerMax2DArea(void) const; + virtual float getJumpPowerMinBorder2D(void) const; + virtual float getJumpPowerMaxBorder2D(void) const; + virtual float getGravityMove(void) const; + virtual float getNormalMaxSpeed(void) const; + virtual float getNormalMinSpeed(void) const; + virtual int getNormalAccelFrame(void) const; + virtual float getRunAccelAverageScale(void) const; + virtual int getNormalBrakeFram(void) const; + virtual float getDashJudgeSpeed(void) const; + virtual int getStickOnBrakeFrame(void) const; + virtual int getNormalDashAnimFrame(void) const; + virtual float getRunAfterTurnSpeedMax(void) const; + virtual float getRunAfterTurnScale(void) const; + virtual int getRunAfterTurnFrame(void) const; + virtual int getBrakeTurnStartFrame(void) const; + virtual float getBrakeOnSpeedRate(void) const; + virtual int getBrakeOnCounterBorder(void) const; + virtual int getWallPushFrame(void) const; + virtual int getRunDeepDownFrame(void) const; + virtual int getRunDeepDownMargine(void) const; + virtual int getQuickTurnJumpFrame(void) const; + virtual int getRoundAccelFrame(void) const; + virtual int getRoundBrakeFrame(void) const; + virtual float getRoundFastDegree(void) const; + virtual int getRoundAccelFrameFast(void) const; + virtual float getRoundMinDegree(void) const; + virtual int getRoundBrakeFrameForce(void) const; + virtual float getRoundFastDegreeForce(void) const; + virtual float getRoundLimitDegreeForce(void) const; + virtual float getRoundLimitDegreeForceFast(void) const; + virtual int getRoundAccelFrameForceFast(void) const; + virtual float getRoundLimitDegreeMin(void) const; + virtual float getRoundLimitDegree(void) const; + virtual int getIceAccelFrame(void) const; + virtual int getIceBrakeFrame(void) const; + virtual int getIceBrakeFrameHigh(void) const; + virtual int getIceBrakeFrameWall(void) const; + virtual int getIceRoundAccelFrame(void) const; + virtual int getIceRoundAccelFrameFast(void) const; + virtual int getIceRoundBrakeFrame(void) const; + virtual float getIceRoundFastDegree(void) const; + virtual float getIceRoundMinDegree(void) const; + virtual float getIceRoundLimitDegree(void) const; + virtual float getHillAddSpeed(void) const; + virtual float getHillSubSpeed(void) const; + virtual int getHillAccelAddFrame(void) const; + virtual int getHillAccelSubFrame(void) const; + virtual float getHillAccelSubAngleMin(void) const; + virtual float getHillAccelSubAngleMax(void) const; + virtual float getStandAngleMin(void) const; + virtual float getStandAngleMax(void) const; + virtual float getHillAngleSpeedMin(void) const; + virtual float getHillAngleSpeedMax(void) const; + virtual int getSpinCapThrowFrame(void) const; + virtual int getSpinCapThrowFrameAir(void) const; + virtual int getSpinCapThrowFrameSwim(void) const; + virtual int getSpinCapThrowFrameSwing(void) const; + virtual int getSpinCapThrowFrameContinuous(void) const; + virtual int getSpinAttackFrame(void) const; + virtual int getSpinBrakeFrame(void) const; + virtual float getSpinAirJumpPower(void) const; + virtual float getSpinAirSpeedMax(void) const; + virtual float getGravitySpinAir(void) const; + virtual float getSlerpQuatRateSpinAir(void) const; + virtual float getSpinBrakeRate(void) const; + virtual float getSpinBrakeSideAccel(void) const; + virtual float getSpinBrakeSideBrakeRate(void) const; + virtual float getSpinBrakeSideMaxSpeedRate(void) const; + virtual float getSpinRoundLimitDegree(void) const; + virtual float getDamageFireJumpPower1st(void) const; + virtual float getDamageFireJumpPower2nd(void) const; + virtual float getDamageFireJumpMoveSpeed(void) const; + virtual float getDamageFireCeilHitSpeed(void) const; + virtual float getDamageFireGravity(void) const; + virtual int getDamageFireNoGravityFrame(void) const; + virtual int getDamageFireRunTime(void) const; + virtual float getDamageFireRunSpeed(void) const; + virtual float getDamageFireRunBrakeFrame(void) const; + virtual int getSandSinkDeadTime(void) const; + virtual int getSandSinkBrakeHeightH(void) const; + virtual int getSandSinkBrakeHeightV(void) const; + virtual float getSandSinkHeight(void) const; + virtual float getSandSinkCapThrow(void) const; + virtual float getSandSinkBrakeMinH(void) const; + virtual float getSandSinkBrakeMaxH(void) const; + virtual float getSandSinkBrakeMinV(void) const; + virtual float getSandSinkBrakeMaxV(void) const; + virtual float getSlopeSlideAngleStart(void) const; + virtual float getSlopeSlideAngleEnd(void) const; + virtual float getSlopeSlideAccel(void) const; + virtual float getSlopeSlideBrake(void) const; + virtual float getSlopeSlideMaxSpeed(void) const; + virtual float getSlopeSlideSpeedEnd(void) const; + virtual float getSlopeSlideSideAccel(void) const; + virtual float getSlopeSlideSideBrake(void) const; + virtual float getSlopeSlideSideMaxSpeed(void) const; + virtual float getSlopeTurnDegree(void) const; + virtual int getSlideInvalidFrame(void) const; + virtual int getSlopeForceFrame(void) const; + virtual float getSlopeSlideForceSideAccel(void) const; + virtual float getSlopeSlideForceSideBrake(void) const; + virtual float getSlopeSlideForceSideMaxSpeed(void) const; + virtual float getSlopeSlideForceTurnDegree(void) const; + virtual float getSlopeRollingSpeedStart(void) const; + virtual float getSlopeRollingSpeedBoost(void) const; + virtual float getSlopeRollingMaxSpeed(void) const; + virtual int getSlopeRollingFrameMinBoost(void) const; + virtual int getSlopeRollingFrameMin(void) const; + virtual float getSlopeRollingStartJumpPower(void) const; + virtual float getSlopeRollingStartSlideSpeed(void) const; + virtual float getSlopeRollingAccel(void) const; + virtual float getSlopeRollingBrake(void) const; + virtual float getSlopeRollingAgainst(void) const; + virtual float getSlopeRollingAnglePowerMax(void) const; + virtual float getSlopeRollingSpeedEnd(void) const; + virtual float getSlopeRollingSideAccel(void) const; + virtual float getSlopeRollingSideBrake(void) const; + virtual float getSlopeRollingSideMaxSpeed(void) const; + virtual int getSlopeRollingUnRollFrame(void) const; + virtual float getSlopeRollingEndBrake(void) const; + virtual float getSlopeRollingEndBrakeEndSpeed(void) const; + virtual float getSlopeRollingReStartAccel(void) const; + virtual float getSlopeRollingReStartMaxAdd(void) const; + virtual int getSlopeRollingReStarterval(void) const; + virtual int getSlopeRollingReStartSwing(void) const; + virtual int getSlopeRollingReStartCharge(void) const; + virtual int getSlopeRollingReStartForce(void) const; + virtual float getSlopeRollingAccelOnSkate(void) const; + virtual float getSlopeRollingSideAccelOnSkate(void) const; + virtual float getSlopeRollingBrakeOnSkate(void) const; + virtual int getExtendFrame(void) const; + virtual float getJumpInertiaRate(void) const; + virtual float getJumpPowerMin(void) const; + virtual float getJumpPowerMax(void) const; + virtual float getJumpGravity(void) const; + virtual float getJumpBaseSpeedMax(void) const; + virtual float getJumpMoveSpeedMin(void) const; + virtual float getJumpMoveSpeedMax(void) const; + virtual float getJumpAccelFront(void) const; + virtual float getJumpAccelBack(void) const; + virtual float getJumpAccelTurn(void) const; + virtual float getJumpTurnAngleStart(void) const; + virtual float getJumpTurnAngleLimit(void) const; + virtual float getJumpTurnAngleFast(void) const; + virtual float getJumpTurnAngleFastLimit(void) const; + virtual int getJumpTurnAccelFrame(void) const; + virtual int getJumpTurnAccelFrameFast(void) const; + virtual int getJumpTurnBrakeFrame(void) const; + virtual float getTrampleGravity(void) const; + virtual float getTrampleJumpPower(void) const; + virtual float getTrampleHighGravity(void) const; + virtual float getTrampleHighJumpPower(void) const; + virtual float getTrampleGravity2D(void) const; + virtual float getTrampleJumpPower2D(void) const; + virtual float getTrampleHighGravity2D(void) const; + virtual float getTrampleHighJumpPower2D(void) const; + virtual float getTrampleHipDropGravity(void) const; + virtual float getTrampleHipDropJumpPower(void) const; + virtual float getTrampleRisingBrakeVelH(void) const; + virtual float getTrampleJumpCodePower(void) const; + virtual float getTrampleJumpCodePowerSmall(void) const; + virtual float getCapLeapFrogJumpGravity(void) const; + virtual float getCapLeapFrogJumpPower(void) const; + virtual float getCapLeapFrogJumpPowerAir(void) const; + virtual float getObjLeapFrogJumpPower(void) const; + virtual float getObjLeapFrogJumpPowerHigh(void) const; + virtual float getCapHeadSpringJumpGravity(void) const; + virtual float getCapHeadSpringJumpPower(void) const; + virtual float getCapHeadSpringJumpGravityHigh(void) const; + virtual float getCapHeadSpringJumpPowerHigh(void) const; + virtual float getCapHeadSpringSpeedMax(void) const; + virtual int getContinuousJumpPreInputFrame(void) const; + virtual int getContinuousJumpCount(void) const; + virtual int getContinuousJumpTimer(void) const; + virtual float getContinuousJumpPowerMin(void) const; + virtual float getJumpPowerMax2nd(void) const; + virtual float getJumpGravity2nd(void) const; + virtual float getJumpPowerMax3rd(void) const; + virtual float getJumpGravity3rd(void) const; + virtual float getSpinFlowerJumpGravity(void) const; + virtual float getSpinFlowerJumpFallSpeedMax(void) const; + virtual float getSpinFlowerJumpMovePower(void) const; + virtual float getSpinFlowerJumpVelMax(void) const; + virtual int getSpinFlowerJumpStayFrame(void) const; + virtual float getSpinFlowerJumpStaySpeedMax(void) const; + virtual float getSpinFlowerJumpNoInputBrake(void) const; + virtual float getSpinFlowerJumpDownFallInitSpeed(void) const; + virtual float getSpinFlowerJumpDownFallPower(void) const; + virtual float getSpinFlowerJumpDownFallSpeedMax(void) const; + virtual float getJumpGravityForceRun(void) const; + virtual float getJumpPowerForceRun(void) const; + virtual float getCapCatchPopPower(void) const; + virtual float getCapCatchPopGravity(void) const; + virtual float getSquatJumpGravity(void) const; + virtual float getSquatJumpPower(void) const; + virtual float getSquatJumpBackPower(void) const; + virtual float getSquatJumpMovePowerFront(void) const; + virtual float getSquatJumpMovePowerSide(void) const; + virtual float getSquatJumpMoveSpeedMax(void) const; + virtual float getTurnJumpGravity(void) const; + virtual float getTurnJumpPower(void) const; + virtual float getTurnJumpVelH(void) const; + virtual float getTurnJumpBrake(void) const; + virtual float getTurnJumpAccel(void) const; + virtual float getTurnJumpSideAccel(void) const; + virtual float getLongJumpAccel(void) const; + virtual float getLongJumpBrake(void) const; + virtual float getLongJumpSideAccel(void) const; + virtual float getLongJumpGravity(void) const; + virtual float getLongJumpJumpPow(void) const; + virtual float getLongJumpMovePow(void) const; + virtual float getLongJumpInitSpeed(void) const; + virtual float getLongJumpSpeed(void) const; + virtual float getLongJumpSpeedMin(void) const; + virtual int getContinuousLongJumpCount(void) const; + virtual int getContinuousLongJumpTimer(void) const; + virtual float getGravityAir(void) const; + virtual float getFrictionAir(void) const; + virtual float getFallSpeedMax(void) const; + virtual float getLongFallDistance(void) const; + virtual float getFallWallScaleVelocity(void) const; + virtual int getDownFallFrameMin(void) const; + virtual float getGravityWallSlide(void) const; + virtual float getWallHeightLowLimit(void) const; + virtual float getWallKeepDegree(void) const; + virtual int getWallKeepFrame(void) const; + virtual float getWallJumpGravity(void) const; + virtual float getWallJumpHSpeed(void) const; + virtual float getWallJumpPower(void) const; + virtual int getWallJumpInvalidateInputFrame(void) const; + virtual int getWallInhibitAfterPunch(void) const; + virtual float getWallFollowAngleH(void) const; + virtual float getWallFollowAngleV(void) const; + virtual float getWallCatchDegree(void) const; + virtual float getWallCatchHeightEdgeTop(void) const; + virtual float getWallCatchHeightBottom(void) const; + virtual float getWallCatchKeepDegree(void) const; + virtual float getWallCatchMoveDegree(void) const; + virtual float getWallCatchMoveSpeed(void) const; + virtual float getWallCatchMoveHeightRange(void) const; + virtual int getWallCatchMoveerpolate(void) const; + virtual int getWallCatchMoveFrame(void) const; + virtual int getWallCatchMoveFrameFast(void) const; + virtual int getWallCatchMoveFrameSwing(void) const; + virtual float getWallCatchInputRepeatAngle(void) const; + virtual float getWallClimbDegree(void) const; + virtual int getWallClimbJumpStartFrame(void) const; + virtual int getWallClimbJumpEndFrame(void) const; + virtual int getWallClimbStartFrame(void) const; + virtual float getWallClimbGravity(void) const; + virtual float getWallFallJumpSpeed(void) const; + virtual float getWallClimbJumpSpeedV(void) const; + virtual float getWallClimbJumpSpeedH(void) const; + virtual float getWallClimbJumpGravity(void) const; + virtual int getWallClimbJumpInvalidFrame(void) const; + virtual float getWallCatchHipLocalOffset(void) const; + virtual float getWallCatchHipStability(void) const; + virtual float getWallCatchHipFriction(void) const; + virtual float getWallCatchHipLimitDegree(void) const; + virtual float getWallCatchStainAreaOffset(void) const; + virtual float getGrabCeilRange(void) const; + virtual float getGrabCeilBodyRadius(void) const; + virtual float getGrabCeilLeaveSpeedMin(void) const; + virtual float getGrabCeilLeavePopPower(void) const; + virtual float getGrabCeilLeavePopGravity(void) const; + virtual float getGrabCeilSwingStartOffset(void) const; + virtual float getGrabCeilReverseInputBorder(void) const; + virtual float getGrabCeilInputPowerBorder(void) const; + virtual float getGrabCeilSwingWaitEnergy(void) const; + virtual float getGrabCeilEnableJumpEnergy(void) const; + virtual float getGrabCeilEnableJumpEnergyMax(void) const; + virtual float getGrabCeilJumpForceAngle(void) const; + virtual float getGrabCeilJumpPower(void) const; + virtual float getGrabCeilJumpMoveMin(void) const; + virtual float getGrabCeilJumpMoveMax(void) const; + virtual float getGrabCeilJumpGravity(void) const; + virtual int getGrabCeilJumpInvalidFrame(void) const; + virtual int getGrabCeilEnableNextFrame(void) const; + virtual int getGrabCeilEnableFallSnapFrame(void) const; + virtual int getPoleClimbPreInputSwing(void) const; + virtual float getPoleClimbInputRepeatAngle(void) const; + virtual float getPoleClimbInputDegreeMove(void) const; + virtual float getPoleClimbCatchRange(void) const; + virtual float getPoleClimbCatchRangeMin(void) const; + virtual float getPoleClimbCatchRangeMax(void) const; + virtual float getPoleClimbJointAngleMin(void) const; + virtual float getPoleClimbJointAngleMax(void) const; + virtual float getPoleClimbJointRangeMin(void) const; + virtual float getPoleClimbJointRangeMax(void) const; + virtual float getPoleClimbMoveWallDegree(void) const; + virtual float getPoleClimbUpMargine(void) const; + virtual float getPoleClimbUpSpeed(void) const; + virtual int getPoleClimbUpFrame(void) const; + virtual int getPoleClimbUpFrameFast(void) const; + virtual int getPoleClimbUpFrameSwing(void) const; + virtual float getPoleClimbDownSpeed(void) const; + virtual float getPoleClimbDownSpeedFast(void) const; + virtual float getPoleClimbDownSpeedSwing(void) const; + virtual int getPoleClimbDownFrame(void) const; + virtual int getPoleClimbDownKeepTime(void) const; + virtual float getPoleClimbTurnDist(void) const; + virtual int getPoleClimbTurnFrame(void) const; + virtual int getPoleClimbTurnStopFrame(void) const; + virtual int getPoleTopStartFrame(void) const; + virtual int getPoleTopEndFrame(void) const; + virtual float getPoleTopTurnSpeed(void) const; + virtual float getPoleTopEndUnderOffsetY(void) const; + virtual int getGroundSpinFrame(void) const; + virtual float getGroundSpinMoveSpeedMax(void) const; + virtual float getGroundSpinAccelRate(void) const; + virtual float getGroundSpinBrakeRate(void) const; + virtual float getSpinJumpGravity(void) const; + virtual float getSpinJumpPower(void) const; + virtual float getSpinJumpMoveSpeedMax(void) const; + virtual float getSpinJumpDownFallInitSpeed(void) const; + virtual float getSpinJumpDownFallPower(void) const; + virtual float getSpinJumpDownFallSpeedMax(void) const; + virtual float getSquatBrakeEndSpeed(void) const; + virtual float getSquatAccelRate(void) const; + virtual float getSquatBrakeRate(void) const; + virtual float getSquatBrakeRateOnSkate(void) const; + virtual float getSquatBrakeSideAccel(void) const; + virtual float getSquatBrakeSideRate(void) const; + virtual float getSquatBrakeSideAccelOnSkate(void) const; + virtual float getSquatBrakeSideRateOnSkate(void) const; + virtual float getSquatBrakeSideMaxSpeedRate(void) const; + virtual float getSquatWalkSpeed(void) const; + virtual float getSquatWalkTurnSpeed(void) const; + virtual int getSquatWalkTurnFrame(void) const; + virtual float getSquatJumpCeilSlideSpeed2D(void) const; + virtual float getHipDropSpeed(void) const; + virtual float getHipDropGravity(void) const; + virtual float getHipDropSpeedMax(void) const; + virtual int getHipDropLandCancelFrame(void) const; + virtual float getHipDropHeight(void) const; + virtual int getHipDropMsgerval(void) const; + virtual float getJumpHipDropPower(void) const; + virtual int getJumpHipDropPermitBeginFrame(void) const; + virtual int getJumpHipDropPermitEndFrame(void) const; + virtual float getHeadSlidingSpeed(void) const; + virtual float getHeadSlidingSpeedMin(void) const; + virtual float getHeadSlidingBrake(void) const; + virtual float getHeadSlidingSideAccel(void) const; + virtual float getHeadSlidingJump(void) const; + virtual float getHeadSlidingGravityAir(void) const; + virtual float getSwimCenterOffset(void) const; + virtual float getSwimWallCatchOffset(void) const; + virtual float getSwimRisePower(void) const; + virtual float getSwimRiseSpeedMax(void) const; + virtual int getSwimRiseFrame(void) const; + virtual float getSwimGravity(void) const; + virtual float getSwimGravityWalk(void) const; + virtual float getSwimFallSpeedMax(void) const; + virtual float getSwimFloorAccelH(void) const; + virtual float getSwimFloorSpeedMaxH(void) const; + virtual float getSwimHighAccelH(void) const; + virtual float getSwimHighSpeedMaxH(void) const; + virtual float getSwimLowAccelH(void) const; + virtual float getSwimLowSpeedMaxH(void) const; + virtual float getSwimBrakeRateH(void) const; + virtual float getSwimWallHitSpeedMinH(void) const; + virtual int getSwimHighAccelPermitFrame(void) const; + virtual float getSwimFlowFieldBlend(void) const; + virtual float getSwimWalkAnimMinRate(void) const; + virtual float getSwimWalkAnimMaxRate(void) const; + virtual float getSwimWalkMaxSpeed(void) const; + virtual float getSwimSpinCapUpPower(void) const; + virtual float getSwimSpinCapUpSpeedMax(void) const; + virtual float getSwimRotStartAngle(void) const; + virtual float getSwimRotFastAngle(void) const; + virtual int getSwimRotAccelFrame(void) const; + virtual int getSwimRotAccelFrameFast(void) const; + virtual int getSwimRotBrakeFrame(void) const; + virtual float getSwimRotSpeedChangeStart(void) const; + virtual float getSwimRotSpeedForward(void) const; + virtual float getSwimRotSpeedMax(void) const; + virtual float getSwimSurfaceAccelH(void) const; + virtual float getSwimSurfaceSpeedMaxH(void) const; + virtual int getSwimSurfaceSpinCapFrame(void) const; + virtual float getSwimSurfaceSpinCapSpeedMaxH(void) const; + virtual float getSwimSurfaceStartDist(void) const; + virtual float getSwimSurfaceEndDist(void) const; + virtual float getSwimSurfaceGravity(void) const; + virtual float getSwimSurfaceBaseHeight(void) const; + virtual float getSwimSurfaceSpring(void) const; + virtual float getSwimSurfaceDamper(void) const; + virtual int getSwimSurfaceDamperStart(void) const; + virtual int getSwimSurfaceDamperFrame(void) const; + virtual float getSwimSurfaceEnableJumpHeight(void) const; + virtual int getSwimSurfacePreInputJumpFrame(void) const; + virtual float getSwimSurfaceMoveSpring(void) const; + virtual float getSwimSurfaceMoveDamper(void) const; + virtual float getSwimSurfaceMoveBaseHeight(void) const; + virtual float getSwimRunSurfaceBaseHeight(void) const; + virtual float getSwimRunSurfaceApproachRate(void) const; + virtual float getSwimRunSurfaceApproachLimit(void) const; + virtual float getSwimRunSurfaceBrakeBorder(void) const; + virtual float getSwimRunSurfaceBrakeH(void) const; + virtual float getSwimRunSurfaceApproachBorderMax(void) const; + virtual float getSwimRunSurfaceApproachBorderMin(void) const; + virtual float getSwimRunSurfaceApproachRateMin(void) const; + virtual float getSwimFallInSpeed(void) const; + virtual float getSwimFallInBrakeH(void) const; + virtual float getSwimFallInBrakeV(void) const; + virtual float getSwimHeadInBrakeH(void) const; + virtual float getSwimHeadInBrakeV(void) const; + virtual float getSwimHeadInRisePower(void) const; + virtual float getSwimHeadInRiseSpeedMax(void) const; + virtual float getSwimHeadInSurfaceHeight(void) const; + virtual int getSwimFallInForceSurfaceFrame(void) const; + virtual int getSwimFallInvalidJumpFrame(void) const; + virtual float getSwimDiveStartSpeed(void) const; + virtual float getSwimDiveBrake(void) const; + virtual float getSwimDiveEndSpeed(void) const; + virtual int getSwimDiveLandCount(void) const; + virtual int getSwimDiveLandCancelFrame(void) const; + virtual int getSwimDiveNoBrakeFrame(void) const; + virtual int getSwimDiveButtonValidFrame(void) const; + virtual int getSwimDiveEndFrame(void) const; + virtual float getSwimDiveInBrakeH(void) const; + virtual float getSwimDiveInBrakeV(void) const; + virtual float getSwimDiveInRisePower(void) const; + virtual float getSwimDiveInRiseSpeedMax(void) const; + virtual float getSwimDiveInSurfaceHeight(void) const; + virtual int getSwimDiveInKeepFrame(void) const; + virtual int getSwimHeadSlidingFrame(void) const; + virtual int getSwimHeadSlidingBrakeFrame(void) const; + virtual float getSwimHeadSlidingSpeed(void) const; + virtual float getSwimHeadSlidingSpeedEnd(void) const; + virtual float getSwimHeadSlidingBrake(void) const; + virtual float getSwimHeadSlidingSideAccel(void) const; + virtual float getSwimHeadSlidingJump(void) const; + virtual float getSwimHeadSlidingGravity(void) const; + virtual int getSwimHeadSlidingEndBrakeFrame(void) const; + virtual int getSwimHeadSlidingEndSpeedMin(void) const; + virtual float getSwimJumpHipDropSpeed(void) const; + virtual float getSwimJumpHipDropBrakeV(void) const; + virtual float getSwimJumpHipDropBrakeVCeiling(void) const; + virtual float getSwimJumpHipDropGravity(void) const; + virtual float getSwimJumpHipDropCancelSpeed(void) const; + virtual float getSwimJumpHipDropAccelH(void) const; + virtual float getSwimJumpHipDropMoveSpeedH(void) const; + virtual float getSwimJumpHipDropPopSpeed(void) const; + virtual float getSwimJumpHipDropPopJumpAdd(void) const; + virtual float getSwimTramplePower(void) const; + virtual float getDiveTramplePower(void) const; + virtual int getDiveTrampleCancelFrame(void) const; + virtual float getDamageSwimPushPower(void) const; + virtual float getDamageSwimGravity(void) const; + virtual int getDamageSwimCancelFrame(void) const; + virtual float getDamageSwimSurfaceGravity(void) const; + virtual float getDamageSwimSurfaceHopPower(void) const; + virtual float getDamageSwimSurfacePushPower(void) const; + virtual float getDamageSwimSurfaceLandSpeed(void) const; + virtual float getDamageSwimSurfaceLandBrake(void) const; + virtual float getDamageSwimSurfaceLandEndSpeed(void) const; + virtual int getDamageSwimSurfaceCancelFrame(void) const; + virtual float getDamageSwimBrakeRateGround(void) const; + virtual int getOxygenReduceFrame(void) const; + virtual int getOxygenNoReduceFrame(void) const; + virtual int getOxygenRecoveryFrame(void) const; + virtual int getOxygenDamageerval(void) const; + virtual int getIceWaterDamageerval(void) const; + virtual int getIceWaterRecoveryFrame(void) const; + virtual float getMoveAnimSpeedMax(void) const; + virtual float getAnimFrameRateSpeedMin(void) const; + virtual float getRunBorderSpeed(void) const; + virtual float getRunBlendRange(void) const; + virtual float getDashBorderSpeed(void) const; + virtual float getDashBlendRange(void) const; + virtual float getDashFastBorderSpeed(void) const; + virtual float getDashFastBlendRange(void) const; + virtual float getAnimFrameRateSpeedMax(void) const; + virtual float getAnimFrameRateRunStart(void) const; + virtual float getAnimFrameRateMinRun(void) const; + virtual float getAnimFrameRateMaxRun(void) const; + virtual float getAnimFrameRateMaxDash(void) const; + virtual float getAnimFrameRateMaxDashFast(void) const; + virtual float getRunStartPlayFrameScale(void) const; + virtual int getRunStartBlendFrame(void) const; + virtual float getDamageFireRunAnimRate(void) const; + virtual float getRunSkateAnimSpeedOffset(void) const; + virtual float getAnimFrameRateRange2D(void) const; + virtual float getAnimFrameRateMinRun2D(void) const; + virtual float getAnimFrameRateMaxRun2D(void) const; + virtual int getIKBlendFrameRun(void) const; + virtual float getIKBlendRateRunMin(void) const; + virtual float getIKBlendRateRunMax(void) const; + virtual float getRollingAnimBorderSpeedMin(void) const; + virtual float getRollingAnimBorderSpeedMax(void) const; + virtual float getRollingAnimFrameRateMin(void) const; + virtual float getRollingAnimFrameRateMax(void) const; + virtual int getSwimPaddleAnimerval(void) const; + virtual int getSwimPaddleAnimRateervalMax(void) const; + virtual int getSwimPaddleAnimRateervalMin(void) const; + virtual float getSwimPaddleAnimMaxRate(void) const; + virtual float getSwimBentForwardMax(void) const; + virtual float getSwimBentForwardBlendRate(void) const; + virtual float getSwimBentSideMax(void) const; + virtual float getSwimBentSpineMax(void) const; + virtual float getSwimBentSideBlendRate(void) const; + virtual float getSwimBentFrontMax(void) const; + virtual float getSwimBentFrontBlendRate(void) const; + virtual float getSwimWalkAnimSpeedMax(void) const; + virtual float getSwimWalkAnimSpeedMin(void) const; + virtual float getSwimWalkAnimFrameRateMax(void) const; + virtual float getSwimWalkAnimFrameRateMin(void) const; + virtual float getSandSinkBorderMin(void) const; + virtual float getSandSinkBorderMax(void) const; + virtual float getSandSinkBorderRateMin(void) const; + virtual float getSandSinkBorderRateMax(void) const; + virtual float getSandSinkFrameRateMin(void) const; + virtual float getSandSinkFrameRateMax(void) const; + virtual int getLookAtEyeKeepFrame(void) const; + virtual int getLookAtEyeKeepFrameInSight(void) const; + virtual int getLookAtEyeKeepFrameWait(void) const; + virtual float getLookAtEyeDistance(void) const; + virtual float getLookAtEyeAngleMinH(void) const; + virtual float getLookAtEyeAngleMinInSightH(void) const; + virtual float getLookAtEyeAngleMaxH(void) const; + virtual float getLookAtEyeAngleMinV(void) const; + virtual float getLookAtEyeAngleMinInSightV(void) const; + virtual float getLookAtEyeAngleMaxV(void) const; + virtual float getTiltEyeBorderStart(void) const; + virtual float getTiltEyeBorderEnd(void) const; + virtual float getTiltEyeAngleScale(void) const; + virtual float getCenterTiltRateMax(void) const; + virtual float getNoseChildLocalOffset(void) const; + virtual float getNoseStability(void) const; + virtual float getNoseFriction(void) const; + virtual float getNoseLimitDegree(void) const; + virtual float getMustacheChildLocalOffset(void) const; + virtual float getMustacheStability(void) const; + virtual float getMustacheFriction(void) const; + virtual float getMustacheLimitDegree(void) const; + virtual int getCaperpolateFrame(void) const; + virtual float getCapChildLocalOffset(void) const; + virtual float getCapStability(void) const; + virtual float getCapFriction(void) const; + virtual float getCapLimitDegree(void) const; + virtual float getCapTransStability(void) const; + virtual float getCapTransFriction(void) const; + virtual float getCapTransLimit(void) const; + virtual int getCapManHeroEyesWaitAppearFrame(void) const; + virtual int getDeadWipeStartDamage(void) const; + virtual int getDeadWipeWaitDamage(void) const; + virtual int getDeadWipeStartAbyss(void) const; + virtual int getDeadWipeWaitAbyss(void) const; + virtual int getDeadWipeStartAbyssWithCapMsg(void) const; + virtual int getDeadWipeWaitAbyssWithCapMsg(void) const; + virtual int getDeadWipeStartPress(void) const; + virtual int getDeadWipeWaitPress(void) const; + virtual int getDeadWipeStartSandSink(void) const; + virtual int getDeadWipeWaitSandSink(void) const; + // private: + float mGravity; + float mFrictionAttack; + float mPushPower; + float mWaitPoseDegreeMax; + float mHillPoseDegreeMax; + float mTiltPoseDegreeMax; + float mSlerpQuatRate; + float mSlerpQuatRateWait; + float mSlerpQuatGrav; + int mPreInputFrameCapThrow; + int mEnableActionFrameCapCatch; + float mJumpPowerCapCatch; + float mJumpGravityCapCatch; + int mRunTimeContinuousThrow; + float mRunSpeedMaxContinuousThrow; + int mRunAccelFrameContinuousThrow; + float mSeparateCheckHeight; + float mSeparateOffsetLerpRate; + float mSeparateEnableThrowHeight; + float mTall; + float mCollisionRadius; + float mCollisionRadiusSquat; + float mCollisionRadiusStand; + float mCollisionSmallStepHeight; + float mCollisionResetLimit; + float mReflectCeilingPower; + float mReflectTossPower; + float mReflectUpperPunchScaleH; + float mCollisionHitDownAngleH; + float mCollisionHitDownEscapeAngleV; + float mShadowDropHeightScale; + float mShadowDropNormalAdd; + float mShadowDropLengthMin; + float mShadowDropLengthMax; + float mShadowDropLengthExtend; + float mGravityDamage; + float mHopPowerDamage; + float mPushPowerDamage; + int mDamageCancelFrame; + int mDamageInvalidCount; + int mDamageInvalidCountRecovery; + int mDamageInvalidCountAbyss; + float mNormalMinSpeed2D; + float mNormalMaxSpeed2D; + float mDashMaxSpeed2D; + int mNormalAccelFrame2D; + int mDashAccelFrame2D; + int mNormalDashAnimFrame2D; + int mNormalBrakeFrame2D; + int mStickOnBrakeFrame2D; + int mBrakeTurnStartFrame2D; + float mTurnEndSpeedRate2D; + float mJumpPowerMin2DArea; + float mJumpPowerMax2DArea; + float mJumpPowerMinBorder2D; + float mJumpPowerMaxBorder2D; + float mGravityMove; + float mNormalMaxSpeed; + float mNormalMinSpeed; + int mNormalAccelFrame; + float mRunAccelAverageScale; + int mNormalBrakeFrame; + float mDashJudgeSpeed; + int mStickOnBrakeFrame; + int mNormalDashAnimFrame; + float mRunAfterTurnSpeedMax; + float mRunAfterTurnScale; + int mRunAfterTurnFrame; + int mBrakeTurnStartFrame; + float mBrakeOnSpeedRate; + int mBrakeOnCounterBorder; + int mWallPushFrame; + int mRunDeepDownFrame; + int mRunDeepDownMargine; + int mQuickTurnJumpFrame; + int mRoundAccelFrame; + int mRoundBrakeFrame; + float mRoundFastDegree; + int mRoundAccelFrameFast; + float mRoundMinDegree; + int mRoundBrakeFrameForce; + float mRoundFastDegreeForce; + float mRoundLimitDegreeForce; + float mRoundLimitDegreeForceFast; + int mRoundAccelFrameForceFast; + float mRoundLimitDegreeMin; + float mRoundLimitDegree; + int mIceAccelFrame; + int mIceBrakeFrame; + int mIceBrakeFrameHigh; + int mIceBrakeFrameWall; + int mIceRoundAccelFrame; + int mIceRoundAccelFrameFast; + int mIceRoundBrakeFrame; + float mIceRoundFastDegree; + float mIceRoundMinDegree; + float mIceRoundLimitDegree; + float mHillAddSpeed; + float mHillSubSpeed; + int mHillAccelAddFrame; + int mHillAccelSubFrame; + float mHillAccelSubAngleMin; + float mHillAccelSubAngleMax; + float mStandAngleMin; + float mStandAngleMax; + float mHillAngleSpeedMin; + float mHillAngleSpeedMax; + int mSpinCapThrowFrame; + int mSpinCapThrowFrameAir; + int mSpinCapThrowFrameSwim; + int mSpinCapThrowFrameSwing; + int mSpinCapThrowFrameContinuous; + int mSpinAttackFrame; + int mSpinBrakeFrame; + float mSpinAirJumpPower; + float mSpinAirSpeedMax; + float mGravitySpinAir; + float mSlerpQuatRateSpinAir; + float mSpinBrakeRate; + float mSpinBrakeSideAccel; + float mSpinBrakeSideBrakeRate; + float mSpinBrakeSideMaxSpeedRate; + float mSpinRoundLimitDegree; + float mDamageFireJumpPower1st; + float mDamageFireJumpPower2nd; + float mDamageFireJumpMoveSpeed; + float mDamageFireCeilHitSpeed; + float mDamageFireGravity; + int mDamageFireNoGravityFrame; + int mDamageFireRunTime; + float mDamageFireRunSpeed; + float mDamageFireRunBrakeFrame; + int mSandSinkDeadTime; + int mSandSinkBrakeHeightH; + int mSandSinkBrakeHeightV; + float mSandSinkHeight; + float mSandSinkCapThrow; + float mSandSinkBrakeMinH; + float mSandSinkBrakeMaxH; + float mSandSinkBrakeMinV; + float mSandSinkBrakeMaxV; + float mSlopeSlideAngleStart; + float mSlopeSlideAngleEnd; + float mSlopeSlideAccel; + float mSlopeSlideBrake; + float mSlopeSlideMaxSpeed; + float mSlopeSlideSpeedEnd; + float mSlopeSlideSideAccel; + float mSlopeSlideSideBrake; + float mSlopeSlideSideMaxSpeed; + float mSlopeTurnDegree; + int mSlideInvalidFrame; + int mSlopeForceFrame; + float mSlopeSlideForceSideAccel; + float mSlopeSlideForceSideBrake; + float mSlopeSlideForceSideMaxSpeed; + float mSlopeSlideForceTurnDegree; + float mSlopeRollingSpeedStart; + float mSlopeRollingSpeedBoost; + float mSlopeRollingMaxSpeed; + int mSlopeRollingFrameMinBoost; + int mSlopeRollingFrameMin; + float mSlopeRollingStartJumpPower; + float mSlopeRollingStartSlideSpeed; + float mSlopeRollingAccel; + float mSlopeRollingBrake; + float mSlopeRollingAgainst; + float mSlopeRollingAnglePowerMax; + float mSlopeRollingSpeedEnd; + float mSlopeRollingSideAccel; + float mSlopeRollingSideBrake; + float mSlopeRollingSideMaxSpeed; + int mSlopeRollingUnRollFrame; + float mSlopeRollingEndBrake; + float mSlopeRollingEndBrakeEndSpeed; + float mSlopeRollingReStartAccel; + float mSlopeRollingReStartMaxAdd; + int mSlopeRollingReStartinterval; + int mSlopeRollingReStartSwing; + int mSlopeRollingReStartCharge; + int mSlopeRollingReStartForce; + float mSlopeRollingAccelOnSkate; + float mSlopeRollingSideAccelOnSkate; + float mSlopeRollingBrakeOnSkate; + int mExtendFrame; + float mJumpInertiaRate; + float mJumpPowerMin; + float mJumpPowerMax; + float mJumpGravity; + float mJumpBaseSpeedMax; + float mJumpMoveSpeedMin; + float mJumpMoveSpeedMax; + float mJumpAccelFront; + float mJumpAccelBack; + float mJumpAccelTurn; + float mJumpTurnAngleStart; + float mJumpTurnAngleLimit; + float mJumpTurnAngleFast; + float mJumpTurnAngleFastLimit; + int mJumpTurnAccelFrame; + int mJumpTurnAccelFrameFast; + int mJumpTurnBrakeFrame; + float mTrampleGravity; + float mTrampleJumpPower; + float mTrampleHighGravity; + float mTrampleHighJumpPower; + float mTrampleGravity2D; + float mTrampleJumpPower2D; + float mTrampleHighGravity2D; + float mTrampleHighJumpPower2D; + float mTrampleHipDropGravity; + float mTrampleHipDropJumpPower; + float mTrampleRisingBrakeVelH; + float mTrampleJumpCodePower; + float mTrampleJumpCodePowerSmall; + float mCapLeapFrogJumpGravity; + float mCapLeapFrogJumpPower; + float mCapLeapFrogJumpPowerAir; + float mObjLeapFrogJumpPower; + float mObjLeapFrogJumpPowerHigh; + float mCapHeadSpringJumpGravity; + float mCapHeadSpringJumpPower; + float mCapHeadSpringJumpGravityHigh; + float mCapHeadSpringJumpPowerHigh; + float mCapHeadSpringSpeedMax; + int mContinuousJumpPreInputFrame; + int mContinuousJumpCount; + int mContinuousJumpTimer; + float mContinuousJumpPowerMin; + float mJumpPowerMax2nd; + float mJumpGravity2nd; + float mJumpPowerMax3rd; + float mJumpGravity3rd; + float mSpinFlowerJumpGravity; + float mSpinFlowerJumpFallSpeedMax; + float mSpinFlowerJumpMovePower; + float mSpinFlowerJumpVelMax; + int mSpinFlowerJumpStayFrame; + float mSpinFlowerJumpStaySpeedMax; + float mSpinFlowerJumpNoInputBrake; + float mSpinFlowerJumpDownFallInitSpeed; + float mSpinFlowerJumpDownFallPower; + float mSpinFlowerJumpDownFallSpeedMax; + float mJumpGravityForceRun; + float mJumpPowerForceRun; + float mCapCatchPopPower; + float mCapCatchPopGravity; + float mSquatJumpGravity; + float mSquatJumpPower; + float mSquatJumpBackPower; + float mSquatJumpMovePowerFront; + float mSquatJumpMovePowerSide; + float mSquatJumpMoveSpeedMax; + float mTurnJumpGravity; + float mTurnJumpPower; + float mTurnJumpVelH; + float mTurnJumpBrake; + float mTurnJumpAccel; + float mTurnJumpSideAccel; + float mLongJumpAccel; + float mLongJumpBrake; + float mLongJumpSideAccel; + float mLongJumpGravity; + float mLongJumpJumpPow; + float mLongJumpMovePow; + float mLongJumpInitSpeed; + float mLongJumpSpeed; + float mLongJumpSpeedMin; + int mContinuousLongJumpCount; + int mContinuousLongJumpTimer; + float mGravityAir; + float mFrictionAir; + float mFallSpeedMax; + float mLongFallDistance; + float mFallWallScaleVelocity; + int mDownFallFrameMin; + float mGravityWallSlide; + float mWallHeightLowLimit; + float mWallKeepDegree; + int mWallKeepFrame; + float mWallJumpGravity; + float mWallJumpHSpeed; + float mWallJumpPower; + int mWallJumpInvalidateInputFrame; + int mWallInhibitAfterPunch; + float mWallFollowAngleH; + float mWallFollowAngleV; + float mWallCatchDegree; + float mWallCatchHeightEdgeTop; + float mWallCatchHeightBottom; + float mWallCatchKeepDegree; + float mWallCatchMoveDegree; + float mWallCatchMoveSpeed; + float mWallCatchMoveHeightRange; + int mWallCatchMoveinterpolate; + int mWallCatchMoveFrame; + int mWallCatchMoveFrameFast; + int mWallCatchMoveFrameSwing; + float mWallCatchInputRepeatAngle; + float mWallClimbDegree; + int mWallClimbJumpStartFrame; + int mWallClimbJumpEndFrame; + int mWallClimbStartFrame; + float mWallClimbGravity; + float mWallFallJumpSpeed; + float mWallClimbJumpSpeedV; + float mWallClimbJumpSpeedH; + float mWallClimbJumpGravity; + int mWallClimbJumpInvalidFrame; + float mWallCatchHipLocalOffset; + float mWallCatchHipStability; + float mWallCatchHipFriction; + float mWallCatchHipLimitDegree; + float mWallCatchStainAreaOffset; + float mGrabCeilRange; + float mGrabCeilBodyRadius; + float mGrabCeilLeaveSpeedMin; + float mGrabCeilLeavePopPower; + float mGrabCeilLeavePopGravity; + float mGrabCeilSwingStartOffset; + float mGrabCeilReverseInputBorder; + float mGrabCeilInputPowerBorder; + float mGrabCeilSwingWaitEnergy; + float mGrabCeilEnableJumpEnergy; + float mGrabCeilEnableJumpEnergyMax; + float mGrabCeilJumpForceAngle; + float mGrabCeilJumpPower; + float mGrabCeilJumpMoveMin; + float mGrabCeilJumpMoveMax; + float mGrabCeilJumpGravity; + int mGrabCeilJumpInvalidFrame; + int mGrabCeilEnableNextFrame; + int mGrabCeilEnableFallSnapFrame; + int mPoleClimbPreInputSwing; + float mPoleClimbInputRepeatAngle; + float mPoleClimbInputDegreeMove; + float mPoleClimbCatchRange; + float mPoleClimbCatchRangeMin; + float mPoleClimbCatchRangeMax; + float mPoleClimbJointAngleMin; + float mPoleClimbJointAngleMax; + float mPoleClimbJointRangeMin; + float mPoleClimbJointRangeMax; + float mPoleClimbMoveWallDegree; + float mPoleClimbUpMargine; + float mPoleClimbUpSpeed; + int mPoleClimbUpFrame; + int mPoleClimbUpFrameFast; + int mPoleClimbUpFrameSwing; + float mPoleClimbDownSpeed; + float mPoleClimbDownSpeedFast; + float mPoleClimbDownSpeedSwing; + int mPoleClimbDownFrame; + int mPoleClimbDownKeepTime; + float mPoleClimbTurnDist; + int mPoleClimbTurnFrame; + int mPoleClimbTurnStopFrame; + int mPoleTopStartFrame; + int mPoleTopEndFrame; + float mPoleTopTurnSpeed; + float mPoleTopEndUnderOffsetY; + int mGroundSpinFrame; + float mGroundSpinMoveSpeedMax; + float mGroundSpinAccelRate; + float mGroundSpinBrakeRate; + float mSpinJumpGravity; + float mSpinJumpPower; + float mSpinJumpMoveSpeedMax; + float mSpinJumpDownFallInitSpeed; + float mSpinJumpDownFallPower; + float mSpinJumpDownFallSpeedMax; + float mSquatBrakeEndSpeed; + float mSquatAccelRate; + float mSquatBrakeRate; + float mSquatBrakeRateOnSkate; + float mSquatBrakeSideAccel; + float mSquatBrakeSideRate; + float mSquatBrakeSideAccelOnSkate; + float mSquatBrakeSideRateOnSkate; + float mSquatBrakeSideMaxSpeedRate; + float mSquatWalkSpeed; + float mSquatWalkTurnSpeed; + int mSquatWalkTurnFrame; + float mSquatJumpCeilSlideSpeed2D; + float mHipDropSpeed; + float mHipDropGravity; + float mHipDropSpeedMax; + int mHipDropLandCancelFrame; + float mHipDropHeight; + int mHipDropMsginterval; + float mJumpHipDropPower; + int mJumpHipDropPermitBeginFrame; + int mJumpHipDropPermitEndFrame; + float mHeadSlidingSpeed; + float mHeadSlidingSpeedMin; + float mHeadSlidingBrake; + float mHeadSlidingSideAccel; + float mHeadSlidingJump; + float mHeadSlidingGravityAir; + float mSwimCenterOffset; + float mSwimWallCatchOffset; + float mSwimRisePower; + float mSwimRiseSpeedMax; + int mSwimRiseFrame; + float mSwimGravity; + float mSwimGravityWalk; + float mSwimFallSpeedMax; + float mSwimFloorAccelH; + float mSwimFloorSpeedMaxH; + float mSwimHighAccelH; + float mSwimHighSpeedMaxH; + float mSwimLowAccelH; + float mSwimLowSpeedMaxH; + float mSwimBrakeRateH; + float mSwimWallHitSpeedMinH; + int mSwimHighAccelPermitFrame; + float mSwimFlowFieldBlend; + float mSwimWalkAnimMinRate; + float mSwimWalkAnimMaxRate; + float mSwimWalkMaxSpeed; + float mSwimSpinCapUpPower; + float mSwimSpinCapUpSpeedMax; + float mSwimRotStartAngle; + float mSwimRotFastAngle; + int mSwimRotAccelFrame; + int mSwimRotAccelFrameFast; + int mSwimRotBrakeFrame; + float mSwimRotSpeedChangeStart; + float mSwimRotSpeedForward; + float mSwimRotSpeedMax; + float mSwimSurfaceAccelH; + float mSwimSurfaceSpeedMaxH; + int mSwimSurfaceSpinCapFrame; + float mSwimSurfaceSpinCapSpeedMaxH; + float mSwimSurfaceStartDist; + float mSwimSurfaceEndDist; + float mSwimSurfaceGravity; + float mSwimSurfaceBaseHeight; + float mSwimSurfaceSpring; + float mSwimSurfaceDamper; + int mSwimSurfaceDamperStart; + int mSwimSurfaceDamperFrame; + float mSwimSurfaceEnableJumpHeight; + int mSwimSurfacePreInputJumpFrame; + float mSwimSurfaceMoveSpring; + float mSwimSurfaceMoveDamper; + float mSwimSurfaceMoveBaseHeight; + float mSwimRunSurfaceBaseHeight; + float mSwimRunSurfaceApproachRate; + float mSwimRunSurfaceApproachLimit; + float mSwimRunSurfaceBrakeBorder; + float mSwimRunSurfaceBrakeH; + float mSwimRunSurfaceApproachBorderMax; + float mSwimRunSurfaceApproachBorderMin; + float mSwimRunSurfaceApproachRateMin; + float mSwimFallInSpeed; + float mSwimFallInBrakeH; + float mSwimFallInBrakeV; + float mSwimHeadInBrakeH; + float mSwimHeadInBrakeV; + float mSwimHeadInRisePower; + float mSwimHeadInRiseSpeedMax; + float mSwimHeadInSurfaceHeight; + int mSwimFallInForceSurfaceFrame; + int mSwimFallInvalidJumpFrame; + float mSwimDiveStartSpeed; + float mSwimDiveBrake; + float mSwimDiveEndSpeed; + int mSwimDiveLandCount; + int mSwimDiveLandCancelFrame; + int mSwimDiveNoBrakeFrame; + int mSwimDiveButtonValidFrame; + int mSwimDiveEndFrame; + float mSwimDiveInBrakeH; + float mSwimDiveInBrakeV; + float mSwimDiveInRisePower; + float mSwimDiveInRiseSpeedMax; + float mSwimDiveInSurfaceHeight; + int mSwimDiveInKeepFrame; + int mSwimHeadSlidingFrame; + int mSwimHeadSlidingBrakeFrame; + float mSwimHeadSlidingSpeed; + float mSwimHeadSlidingSpeedEnd; + float mSwimHeadSlidingBrake; + float mSwimHeadSlidingSideAccel; + float mSwimHeadSlidingJump; + float mSwimHeadSlidingGravity; + int mSwimHeadSlidingEndBrakeFrame; + int mSwimHeadSlidingEndSpeedMin; + float mSwimJumpHipDropSpeed; + float mSwimJumpHipDropBrakeV; + float mSwimJumpHipDropBrakeVCeiling; + float mSwimJumpHipDropGravity; + float mSwimJumpHipDropCancelSpeed; + float mSwimJumpHipDropAccelH; + float mSwimJumpHipDropMoveSpeedH; + float mSwimJumpHipDropPopSpeed; + float mSwimJumpHipDropPopJumpAdd; + float mSwimTramplePower; + float mDiveTramplePower; + int mDiveTrampleCancelFrame; + float mDamageSwimPushPower; + float mDamageSwimGravity; + int mDamageSwimCancelFrame; + float mDamageSwimSurfaceGravity; + float mDamageSwimSurfaceHopPower; + float mDamageSwimSurfacePushPower; + float mDamageSwimSurfaceLandSpeed; + float mDamageSwimSurfaceLandBrake; + float mDamageSwimSurfaceLandEndSpeed; + int mDamageSwimSurfaceCancelFrame; + float mDamageSwimBrakeRateGround; + int mOxygenReduceFrame; + int mOxygenNoReduceFrame; + int mOxygenRecoveryFrame; + int mOxygenDamageinterval; + int mIceWaterDamageinterval; + int mIceWaterRecoveryFrame; + float mMoveAnimSpeedMax; + float mAnimFrameRateSpeedMin; + float mRunBorderSpeed; + float mRunBlendRange; + float mDashBorderSpeed; + float mDashBlendRange; + float mDashFastBorderSpeed; + float mDashFastBlendRange; + float mAnimFrameRateSpeedMax; + float mAnimFrameRateRunStart; + float mAnimFrameRateMinRun; + float mAnimFrameRateMaxRun; + float mAnimFrameRateMaxDash; + float mAnimFrameRateMaxDashFast; + float mRunStartPlayFrameScale; + int mRunStartBlendFrame; + float mDamageFireRunAnimRate; + float mRunSkateAnimSpeedOffset; + float mAnimFrameRateRange2D; + float mAnimFrameRateMinRun2D; + float mAnimFrameRateMaxRun2D; + int mIKBlendFrameRun; + float mIKBlendRateRunMin; + float mIKBlendRateRunMax; + float mRollingAnimBorderSpeedMin; + float mRollingAnimBorderSpeedMax; + float mRollingAnimFrameRateMin; + float mRollingAnimFrameRateMax; + int mSwimPaddleAniminterval; + int mSwimPaddleAnimRateintervalMax; + int mSwimPaddleAnimRateintervalMin; + float mSwimPaddleAnimMaxRate; + float mSwimBentForwardMax; + float mSwimBentForwardBlendRate; + float mSwimBentSideMax; + float mSwimBentSpineMax; + float mSwimBentSideBlendRate; + float mSwimBentFrontMax; + float mSwimBentFrontBlendRate; + float mSwimWalkAnimSpeedMax; + float mSwimWalkAnimSpeedMin; + float mSwimWalkAnimFrameRateMax; + float mSwimWalkAnimFrameRateMin; + float mSandSinkBorderMin; + float mSandSinkBorderMax; + float mSandSinkBorderRateMin; + float mSandSinkBorderRateMax; + float mSandSinkFrameRateMin; + float mSandSinkFrameRateMax; + int mLookAtEyeKeepFrame; + int mLookAtEyeKeepFrameInSight; + int mLookAtEyeKeepFrameWait; + float mLookAtEyeDistance; + float mLookAtEyeAngleMinH; + float mLookAtEyeAngleMinInSightH; + float mLookAtEyeAngleMaxH; + float mLookAtEyeAngleMinV; + float mLookAtEyeAngleMinInSightV; + float mLookAtEyeAngleMaxV; + float mTiltEyeBorderStart; + float mTiltEyeBorderEnd; + float mTiltEyeAngleScale; + float mCenterTiltRateMax; + float mNoseChildLocalOffset; + float mNoseStability; + float mNoseFriction; + float mNoseLimitDegree; + float mMustacheChildLocalOffset; + float mMustacheStability; + float mMustacheFriction; + float mMustacheLimitDegree; + int mCapinterpolateFrame; + float mCapChildLocalOffset; + float mCapStability; + float mCapFriction; + float mCapLimitDegree; + float mCapTransStability; + float mCapTransFriction; + float mCapTransLimit; + int mCapManHeroEyesWaitAppearFrame; + int mDeadWipeStartDamage; + int mDeadWipeWaitDamage; + int mDeadWipeStartAbyss; + int mDeadWipeWaitAbyss; + int mDeadWipeStartAbyssWithCapMsg; + int mDeadWipeWaitAbyssWithCapMsg; + int mDeadWipeStartPress; + int mDeadWipeWaitPress; + int mDeadWipeStartSandSink; + int mDeadWipeWaitSandSink; + int mDeadWipeStartNoOxygen; + int mDeadWipeWaitNoOxygen; + int mDeadWipeStartIceWater; + int mDeadWipeWaitIceWater; + float mCoinDashSpeed; + float mCoinDashSpeedLimit; + float mAdditionalSpeedLimit; +}; \ No newline at end of file diff --git a/include/game/Player/PlayerCostumeInfo.h b/include/game/Player/PlayerCostumeInfo.h new file mode 100644 index 0000000..4e7b240 --- /dev/null +++ b/include/game/Player/PlayerCostumeInfo.h @@ -0,0 +1,78 @@ +#pragma once + +struct PlayerBodyCostumeInfo { + PlayerBodyCostumeInfo() = default; + PlayerBodyCostumeInfo(const char *name) { + costumeName = name; + }; + const char *costumeName; + int mWarmLevel = 0; + bool mIsIgnoreTemperature = false; + bool mIsUseHeadSuffix = false; + bool mIsBigEar = false; + bool mIsHideHeadHair = false; + bool mIsUseBodyHair = false; + bool mIsExistHairNoCap = false; + bool mIsUseShortHead = false; + bool mIsNoPairHead = false; + bool mIsMario64 = false; + bool mIsHidePainNose = false; + bool mIsUseBeard = false; + bool mIsUseEarringPeach = false; + bool mIsUseEarringLink = false; +}; + +struct PlayerHeadCostumeInfo { + + PlayerHeadCostumeInfo() = default; + PlayerHeadCostumeInfo(const char *name) { + costumeName = name; + }; + + const char *costumeName; + bool mIsFullFace = false; + bool mIsShrinkNose = false; + bool mIsPreventHead = false; + bool mIsEnableBigEar = false; + bool mIsEnableHairNoCap = false; + bool mIsMario64 = false; + bool mIsHaveShort = false; + bool mIsHideBeard = false; + bool mIsHideEarringPeach = false; + bool mIsHideEarringLink = false; + bool mIsUseStrap = false; + bool mIsInvisibleHead = false; +}; + +class PlayerCostumeInfo { + public: + PlayerCostumeInfo(void) { + mBodyInfo = {0}; + mHeadInfo = {0}; + }; + + void init(PlayerBodyCostumeInfo const *body, PlayerHeadCostumeInfo const *head) { + mBodyInfo = body; + mHeadInfo = head; + }; + + bool isEnableBigEar(void) const; + bool isEnableHairNoCap(void) const; + bool isEnableCostume2D(void) const; + bool isNeedShrinkNose(void) const; + bool isNeedBodyHair(void) const; + bool isNeedSyncBodyHair(void) const; + bool isNeedFullFaceAnim(void) const; + bool isHidePainNose(void) const; + bool isEnableEarring(void) const; + bool isSyncFaceBeard(void) const; + bool isSyncStrap(void) const; + bool isFollowJoeStrap(void) const; + bool isPreventHeadPain(void) const; + bool isInvisibleHead(void) const; + + int calcWarmLevel(int level); + + const PlayerBodyCostumeInfo *mBodyInfo; + const PlayerHeadCostumeInfo *mHeadInfo; +}; \ No newline at end of file diff --git a/include/game/Player/PlayerFactory.h b/include/game/Player/PlayerFactory.h new file mode 100644 index 0000000..e991df3 --- /dev/null +++ b/include/game/Player/PlayerFactory.h @@ -0,0 +1,25 @@ +#pragma once + +#include "PlayerActorBase.h" +#include "PlayerActorHakoniwa.h" +#include "YukimaruRacePlayer.h" +#include "al/factory/Factory.h" + +template +PlayerActorBase* createPlayerFunction(const char *name); + +typedef PlayerActorBase* (*CreateHakoniwa)(const char* name); + +static al::NameToCreator playerEntries[] = { + {"PlayerActorHakoniwa", &createPlayerFunction}, + {"YukimaruRacePlayer", &createPlayerFunction} +}; + +class PlayerFactory : public al::Factory { + public: + PlayerFactory() { + this->factoryName = "プレイヤー生成"; + this->actorTable = playerEntries; + this->factoryCount = sizeof(playerEntries)/sizeof(playerEntries[0]); + }; +}; diff --git a/include/game/Player/PlayerFormSensorCollisionArranger.h b/include/game/Player/PlayerFormSensorCollisionArranger.h new file mode 100644 index 0000000..34e892e --- /dev/null +++ b/include/game/Player/PlayerFormSensorCollisionArranger.h @@ -0,0 +1,39 @@ +#pragma once + +#include "sead/math/seadVector.h" +#include "al/LiveActor/LiveActor.h" +#include "PlayerColliderHakoniwa.h" +#include "PlayerHackKeeper.h" + +class IPlayerModelChanger; + +class PlayerFormSensorCollisionArranger { + public: + PlayerFormSensorCollisionArranger(al::LiveActor *, PlayerColliderHakoniwa *, IPlayerModelChanger const*, PlayerHackKeeper const*); + void setFormModel3D(void); + void setFormModel2D(void); + void setFormActionSquat(void); + void setFormActionStandup(void); + void setFormActionWallGrab(sead::Vector3f const&); + void setFormActionGrabCeil(sead::Vector3f const&); + void setFormActionPoleClimb(sead::Vector3f const&); + void setFormActionSwim(void); + void setFormActionHack(void); + void setFormActionBind(bool); + void setFormActionRecovery(void); + void setFormActionAbyss(sead::Vector3f const&); + void setFormActionDead(void); + void setFormAttackSensorNone(void); + void setFormAttackSensorSpin(void); + void setFormAttackSensorTornado(void); + void setCollisionShapeOffsetGround(float); + const char *getHeadSensorName(void) const; + sead::Vector3f *getHeadPos(void) const; + float getHeadRadius(void) const; + sead::Vector3f *getBodyPos(void) const; + bool isEnableSafetyPointForm(void) const; + void update(void); + void syncForm(void); + void validateAttackSensor(void); + +}; \ No newline at end of file diff --git a/include/game/Player/PlayerFunction.h b/include/game/Player/PlayerFunction.h new file mode 100644 index 0000000..16c03f1 --- /dev/null +++ b/include/game/Player/PlayerFunction.h @@ -0,0 +1,37 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +#include "game/Player/PlayerJointControlPartsDynamics.h" +#include "game/Player/PlayerConst.h" + +#include "PlayerCostumeInfo.h" + +namespace al { + class Resource; +} + +class PlayerFunction +{ + public: + static int getPlayerInputPort(const al::LiveActor *); + static bool tryActivateAmiiboPreventDamage(const al::LiveActor *); + static bool isPlayerDeadStatus(const al::LiveActor *player); + static void syncBodyHairVisibility(al::LiveActor *, al::LiveActor *); + static void syncMarioFaceBeardVisibility(al::LiveActor *, al::LiveActor *); + static void syncMarioHeadStrapVisibility(al::LiveActor *); + static bool isNeedHairControl(PlayerBodyCostumeInfo const *, const char *); + static bool isInvisibleCap(PlayerCostumeInfo const *); + static void hideHairVisibility(al::LiveActor *); + + static PlayerConst *createMarioConst(char const *); + static void createCapModelName(sead::BufferedSafeStringBase *, char const *); + + static void initMarioModelActor2D(al::LiveActor *actor, al::ActorInitInfo const &initInfo, char const *model2DName, bool isInvisCap); + static al::Resource *initCapModelActor(al::LiveActor *, al::ActorInitInfo const &, char const *); + static al::Resource *initCapModelActorDemo(al::LiveActor *, al::ActorInitInfo const &, char const *); + static PlayerCostumeInfo *initMarioModelActor(al::LiveActor *player, const al::ActorInitInfo &initInfo, const char *modelName, const char *capType, al::AudioKeeper *keeper, bool isCloset); + // joint, init, pconst, nosescale, earscale + static PlayerCostumeInfo *initMarioModelActorDemo(PlayerJointControlPartsDynamics **jointCtrlPtr, al::LiveActor *player, const al::ActorInitInfo &initInfo, char const *bodyName, char const *capName, PlayerConst const *pConst,sead::Vector3f *noseScale, sead::Vector3f *earScale, bool isCloset); + static PlayerCostumeInfo *initMarioModelCommon(al::LiveActor *player, const al::ActorInitInfo &initInfo, char const *bodyName, char const *capName, int subActorNum, bool isDemo, al::AudioKeeper *audioKeeper, bool guessIsChromaKey, bool isCloset); +}; \ No newline at end of file diff --git a/include/game/Player/PlayerHackKeeper.h b/include/game/Player/PlayerHackKeeper.h new file mode 100644 index 0000000..dc46000 --- /dev/null +++ b/include/game/Player/PlayerHackKeeper.h @@ -0,0 +1,76 @@ +/** + * @file PlayerHackKeeper.h + * @brief Contains info on the current hack (capture) + */ + +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "game/Interfaces/IUsePlayerHack.h" +#include "game/Player/PlayerInput.h" +#include "game/Player/HackCap.h" +#include "game/Player/PlayerCollider.h" +#include "game/Player/HackCap/CapTargetInfo.h" + +struct HackEndParam; +struct PlayerRecoverySafetyPoint; +struct PlayerDamageKeeper; +struct IPlayerModelChanger; +struct IUsePlayerHeightCheck; + +class PlayerHackKeeper +{ + public: + PlayerHackKeeper(al::LiveActor *,HackCap *,PlayerRecoverySafetyPoint *,PlayerInput const*,sead::Matrix34f const*,PlayerDamageKeeper const*,IPlayerModelChanger const*,IUsePlayerHeightCheck const*); + void createHackModel(al::ActorInitInfo const&); + void startHack(al::HitSensor *,al::HitSensor *,al::LiveActor *); + void setupHack(al::HitSensor *,al::HitSensor *,al::LiveActor *); + void endHack(HackEndParam const*); + void endHackStartDemo(al::LiveActor *); + void startHackStartDemo(al::LiveActor *); + void startHackStartDemoPuppetable(al::LiveActor *); + void addHackStartDemo(al::LiveActor *); + void appearHackDemoModel(sead::Matrix34f const&,float); + void updateHackDemoModel(sead::Matrix34f const&,float); + void deleteHackDemoModelEffect(void); + void killHackDemoModel(void); + bool isActiveHackStartDemo(void) const; + void recordHack(void); + void cancelHackArea(void); + void cancelHack(void); + void cancelForceRecovery(void); + void tryEscapeHack(void); + void sendTransferHack(void); + void sendMarioDemo(void); + void forceKillHack(void); + void sendMarioDead(void); + void sendMarioInWater(void); + void sendMarioDeathArea(void); + void sendMsgEnableMapCheckPointWarp(void); + void sendMsgSelfCeilingCheckMiss(void); + void receiveRequestTransferHack(al::HitSensor *); + void requestDamage(void); + void receiveRequestDamage(void); + void sendSyncDamageVisibility(void); + void pushWorldEndBorder(sead::Vector3f const&); + const char *getCurrentHackName(void) const; + PlayerCollider *getPlayerCollision(void); + float getHackGuideHeight(void); + bool isHackGuideEnable(void) const; + float getHackStayGravityMargine(void); + void *getCollisionPartsFilter(void); + bool isHackGroupTalkScare(void) const; + bool isHackNoCollisionMsg(void) const; + bool isHackNoSeparateCameraInput(void) const; + bool isHackUsePlayerCollision(void) const; + bool isHackCancelCeilingCheck(void) const; + bool isHackInvalidLifeRecovery(void) const; + void requestForceHackStageStart(al::HitSensor *,CapTargetInfo const*,al::LiveActor *); + void executeForceHackStageStart(al::HitSensor *,IUsePlayerHack *); + void startDemo(void); + void endDemo(void); + + char padding[0x68]; + al::LiveActor *currentHackActor; + // 0x98 PlayerHackStartTexKeeper +}; \ No newline at end of file diff --git a/include/game/Player/PlayerInfo.h b/include/game/Player/PlayerInfo.h new file mode 100644 index 0000000..444e318 --- /dev/null +++ b/include/game/Player/PlayerInfo.h @@ -0,0 +1,50 @@ +#pragma once + +#include "HackCap.h" +#include "PlayerAnimator.h" +#include "PlayerInput.h" +#include "al/sensor/HitSensor.h" +#include "PlayerColliderHakoniwa.h" +#include "PlayerHackKeeper.h" +#include "PlayerCostumeInfo.h" +#include "PlayerModelChangerHakoniwa.h" + +class PlayerInfo { + public: + PlayerInfo(); + + PlayerModelChangerHakoniwa *pModelChanger; // 0x0 + struct PlayerOxygen *pOxygen; // 0x8 + PlayerAnimator *mPlayerAnimator; // 0x10 + struct PlayerBindKeeper *pBindKeeper; // 0x18 + struct PlayerDamageKeeper *pDamageKeeper; // 0x20 + struct PlayerDemoActionFlag *pDemoActionFlag; // 0x28 + struct PlayerEquipmentUser *pEquipmentUser; // 0x30 + HackCap *mHackCap; // 0x38 + struct WorldEndBorderKeeper *mWorldEndBorderKeeper; // 0x40 + struct PlayerCarryKeeper *pCarryKeeper; // 0x48 + struct PlayerJointControlKeeper *pJoinControlKeeper; // 0x50 + struct PlayerCounterIceWater *pCounterIceWater; // 0x58 + struct PlayerStainControl *pStainControl; // 0x60 + struct FootPrintHolder *mFootPrintHolder; // 0x68 + al::HitSensor *mHitSensor; // 0x70 + struct PlayerFormSensorCollisionArranger *pSensorCollArranger; // 0x78 + PlayerInput *pInput; // 0x80 + PlayerColliderHakoniwa *pColliderHakoniwa; // 0x88 + struct PlayerModelHolder *pModelHolder; // 0x90 + PlayerHackKeeper *pHackKeeper; // 0x98 + struct PlayerCapManHeroEyesControl *pCapManHeroEyesCtrl; // 0xA0 + struct PlayerRecoverySafetyPoint *pRecoverySafetyPoint; // 0xA8 + PlayerCostumeInfo *pCostumeInfo; // 0xB0 + struct PlayerJudgeCameraInWater *pJudgeCameraInWater; // 0xB8 + struct PlayerJudgeTalkGround *pJudgeTalkGround; // 0xC0 + struct PlayerJudgeTalkSwim *pJudgeTalkSwim; // 0xC8 + struct PlayerJudgeDead *pJudgeDead; // 0xD0 + struct PlayerJudgeDeadWipeStart *pJudgeDeadWipeStart; // 0xD8 + struct PlayerJudgeDrawForward *pJudgeDrawForward; // 0xE0 + struct PlayerJudgeSameNerve *pJudgeSameNervePoleClimb; // 0xE8 + struct PlayerJudgeSameNerve *pJudgeSameNerveGrabCeil; // 0xF0 + struct PlayerJudgeSameNerve *pJudgeSameNerveWallCatch; // 0xF8 + struct PlayerJudgeActiveCameraSubjective *pJudgeActiveCameraSubjective; // 0x100 + // theres a lot more judges and same nerves but im too lazy to get them all +}; \ No newline at end of file diff --git a/include/game/Player/PlayerInitInfo.h b/include/game/Player/PlayerInitInfo.h new file mode 100644 index 0000000..015c699 --- /dev/null +++ b/include/game/Player/PlayerInitInfo.h @@ -0,0 +1,5 @@ +#pragma once + +class PlayerInitInfo { + +}; \ No newline at end of file diff --git a/include/game/Player/PlayerInput.h b/include/game/Player/PlayerInput.h new file mode 100644 index 0000000..6ee66da --- /dev/null +++ b/include/game/Player/PlayerInput.h @@ -0,0 +1,11 @@ +#pragma once + + + +class PlayerInput { + public: + bool isMove(void) const; + bool isMoveDeepDown(void) const; + bool isMoveDeepDownNoSnap(void) const; + bool isNoInput(void) const; +}; \ No newline at end of file diff --git a/include/game/Player/PlayerJointControlPartsDynamics.h b/include/game/Player/PlayerJointControlPartsDynamics.h new file mode 100644 index 0000000..d6df372 --- /dev/null +++ b/include/game/Player/PlayerJointControlPartsDynamics.h @@ -0,0 +1,5 @@ +#pragma once + +class PlayerJointControlPartsDynamics { + public: +}; \ No newline at end of file diff --git a/include/game/Player/PlayerModelChangerHakoniwa.h b/include/game/Player/PlayerModelChangerHakoniwa.h new file mode 100644 index 0000000..32b6dde --- /dev/null +++ b/include/game/Player/PlayerModelChangerHakoniwa.h @@ -0,0 +1,13 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "PlayerCostumeInfo.h" +#include "PlayerModelHolder.h" +#include "game/Interfaces/IUseDimension.h" + +class PlayerPainPartsKeeper; + +class PlayerModelChangerHakoniwa { + public: + PlayerModelChangerHakoniwa(al::LiveActor const *, PlayerModelHolder *, PlayerPainPartsKeeper *, PlayerCostumeInfo *, IUseDimension const *); +}; \ No newline at end of file diff --git a/include/game/Player/PlayerModelHolder.h b/include/game/Player/PlayerModelHolder.h new file mode 100644 index 0000000..ae80c2d --- /dev/null +++ b/include/game/Player/PlayerModelHolder.h @@ -0,0 +1,25 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "sead/container/seadPtrArray.h" + +class PlayerModelHolder { +public: + struct Entry { + sead::FixedSafeString<0x80> mName; + al::LiveActor* mLiveActor; + }; + + PlayerModelHolder(unsigned int); + void registerModel(al::LiveActor*, char const*); + void changeModel(char const*); + al::LiveActor* findModelActor(char const*) const; + al::LiveActor* tryFindModelActor(char const*) const; + bool isCurrentModelLabel(char const*) const; + bool isCurrentModelLabelSubString(char const*) const; + + sead::PtrArray mBuffer; + Entry* currentModel; + sead::FixedSafeString<0x80> _10; + +}; \ No newline at end of file diff --git a/include/game/Player/PlayerModelKeeper.h b/include/game/Player/PlayerModelKeeper.h new file mode 100644 index 0000000..96f2e7d --- /dev/null +++ b/include/game/Player/PlayerModelKeeper.h @@ -0,0 +1,6 @@ +#pragma once + +class PlayerModelKeeper { + public: + +}; \ No newline at end of file diff --git a/include/game/Player/PlayerPuppet.h b/include/game/Player/PlayerPuppet.h new file mode 100644 index 0000000..973ce07 --- /dev/null +++ b/include/game/Player/PlayerPuppet.h @@ -0,0 +1,18 @@ +#pragma once + +class PlayerPuppet { + public: + void hide(void); + void hideShadow(void); + void hideSilhouette(void); + + void show(void); + void showShadow(void); + void showSilhouette(void); + + void invalidateCollisionCheck(void); + void invalidateSensor(void); + + void validateCollisionCheck(void); + void validateSensor(void); +}; \ No newline at end of file diff --git a/include/game/Player/States/PlayerStateRunHakoniwa.h b/include/game/Player/States/PlayerStateRunHakoniwa.h new file mode 100644 index 0000000..8a7f04d --- /dev/null +++ b/include/game/Player/States/PlayerStateRunHakoniwa.h @@ -0,0 +1,59 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "game/Interfaces/IUsePlayerCollision.h" +#include "game/Player/Actions/PlayerActionGroundMoveControl.h" +#include "game/Player/PlayerAnimator.h" +#include "game/Player/PlayerConst.h" +#include "game/Player/PlayerInput.h" +#include "al/nerve/ActorStateBase.h" + +class PlayerStateRunHakoniwa : public al::ActorStateBase { +public: + PlayerStateRunHakoniwa(al::LiveActor *,PlayerConst const*,PlayerInput const*,IUsePlayerCollision const*, struct PlayerCounterForceRun const*, struct PlayerCounterQuickTurnJump const*, struct PlayerTrigger *, PlayerAnimator *, struct PlayerEffect *, struct PlayerJointParamCenterDynamics *, struct IJudge const*,bool); + void appear(void); + void kill(void); + void control(void); + void isEnableLookAt(void); + void isRunDashFast(void); + void tryTurnJump(sead::Vector3 *); + void getCenterTiltRateMax(void); + void getCapDynamicsRate(void); + void exePivot(void); + void exeRun(void); + void exeBrake(void); + void exeTurn(void); + void exeWallPush(void); + ~PlayerStateRunHakoniwa(); + + PlayerConst *mPlayerConst; + PlayerInput *mPlayerInput; + IUsePlayerCollision *mPlayerCollider; + PlayerCounterForceRun *mCounterForceRun; + PlayerCounterQuickTurnJump *mCounterQuickTurnJump; + PlayerTrigger *mPlayerTrigger; + PlayerAnimator *mPlayerAnimator; + struct PlayerAnimControlRun *mPlayerAnimControlRun; + struct PlayerActionPivotTurnControl *mActionPivotTurnControl; + PlayerActionGroundMoveControl *mActionGroundMoveControl; // 0x68 + bool unkBool70; + int dword74; + int dword78; + int qword7C; + int qword80; + int qword84; + int dword88; + float mMaxSpeed; + int dword90; + int unkInt94; + struct PlayerJudgeWallPush *mPlayerJudgeWallPush; + void *qwordA0; + void *qwordA8; + PlayerEffect *mPlayerEffect; + bool byteB8; + int dwordBC; + void* mJointParamCenterDynamics; + bool unkBoolC8; +}; + +static_assert(sizeof(PlayerStateRunHakoniwa) == 0xD0, "Player State Run Size"); \ No newline at end of file diff --git a/include/game/Player/YukimaruRacePlayer.h b/include/game/Player/YukimaruRacePlayer.h new file mode 100644 index 0000000..0893c7f --- /dev/null +++ b/include/game/Player/YukimaruRacePlayer.h @@ -0,0 +1,20 @@ +#pragma once + +#include "game/Interfaces/IUseDimension.h" +#include "PlayerActorBase.h" +#include "PlayerPuppet.h" +#include "PlayerInput.h" +#include "PlayerAnimator.h" +#include "HackCap.h" +#include "PlayerModelKeeper.h" +#include "PlayerColliderHakoniwa.h" +#include "PlayerConst.h" +#include "PlayerHackKeeper.h" +#include "PlayerInfo.h" +#include "PlayerInitInfo.h" + +class YukimaruRacePlayer : public PlayerActorBase , public IUseDimension { + public: + void initPlayer(al::ActorInitInfo const &, PlayerInitInfo const &); + undefined size[0x1A8]; +}; \ No newline at end of file diff --git a/include/game/StageScene/StageScene.h b/include/game/StageScene/StageScene.h new file mode 100644 index 0000000..684d4b7 --- /dev/null +++ b/include/game/StageScene/StageScene.h @@ -0,0 +1,25 @@ +#pragma once + +#include "al/scene/Scene.h" +#include "game/StageScene/StageSceneLayout.h" +#include "game/StageScene/StageSceneStatePauseMenu.h" + +#define INHERITSIZE sizeof(al::Scene) + +class StageScene : public al::Scene +{ + public: + bool isPause() const; + // 0x88 StageResourceKeeper * + // 0x90 LiveActorKit * + // 0x98 LayoutKit * + // 0xA0 SceneObjHolder * + // 0xA8 SceneStopCtrl * + + unsigned char padding_180[0x180 - INHERITSIZE]; + StageSceneStatePauseMenu *mStatePauseMenu; // 0x180 + unsigned char padding_2D0[0x148]; + GameDataHolderAccessor mHolder; // 0x2D0 + unsigned char padding_2F8[0x20]; + StageSceneLayout *stageSceneLayout; // 0x2F8 +}; diff --git a/include/game/StageScene/StageSceneLayout.h b/include/game/StageScene/StageSceneLayout.h new file mode 100644 index 0000000..9f521f2 --- /dev/null +++ b/include/game/StageScene/StageSceneLayout.h @@ -0,0 +1,82 @@ +#pragma once + +#include "game/Layouts/CoinCounter.h" +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/layout/SimpleLayoutAppearWaitEnd.h" +#include "al/nerve/NerveStateBase.h" +#include "game/Layouts/MapMini.h" + +namespace al { + class SubCameraRenderer; + class PlayerHolder; +} + +class StageSceneLayout : public al::NerveStateBase { + public: + + StageSceneLayout(char const *, al::LayoutInitInfo const&, al::PlayerHolder const*, al::SubCameraRenderer const*); + + void control(void); + void updatePlayGuideMenuText(void); + void setDirtyFlagForPlayGuideMenu(void); + void tryAppearCoinCollectCounter(void); + void endWithoutCoin(bool); + void end(void); + void appearCoinCounterForDemo(void); + void endShineChipCompleteAnim(void); + void killShineCount(void); + void appearShineCountWait(void); + void endCloset(void); + void missEnd(void); + void appearPlayGuideCamera(void); + + void updateCounterParts(void); + void updateLifeCounter(void); + void updateKidsModeLayout(void); + + void start(void); + void startActionAll(char const*); + void startOnlyCoin(bool); + void tryStartLifeDemo(void); + void startCoinCountAnim(int); + void startCoinCollectCountAnim(int); + void startShineChipCompleteAnim(void); + void tryStartDemoGetLifeMaxUpItem(bool); + void startCloset(void); + void startShineCountAnim(bool); + + void exeAppear(void); + void exeWait(void); + void exeEnd(void); + void exeEndWithoutCoin(void); + void exeCoinCountAnim(void); + void exeShineChipComplete(void); + void exeShineCountAppear(void); + + bool isEnd(void) const; + bool isWait(void) const; + bool isActive(void) const; + bool isEndLifeDemo(void) const; + bool isEndCoinCountAnim(void) const; + bool isEndShineChipCompleteAnim(void) const; + bool isEndDemoGetLifeMaxUpItem(void) const; + bool isEndShineCountAnim(void) const; + bool isActionEndAll(void) const; + + CoinCounter *mCoinCountLyt; // 0x18 + struct CounterLifeCtrl * mHealthLyt; // 0x20 + struct ShineCounter * mShineCountLyt; // 0x28 + CoinCounter * mCoinCollectLyt; // 0x30 + struct ShineChipLayoutParts * mShineChipPartsLyt; // 0x38 + struct PlayGuideCamera * mPlayGuideCamLyt; // 0x40 + struct PlayGuideBgm * mPlayGuideBgmLyt; // 0x48 + MapMini * mMapMiniLyt; // 0x50 + al::PlayerHolder *mPlayerHolder; // 0x58 + void * unkPtr; // 0x60 + al::SimpleLayoutAppearWaitEnd * mPlayGuideMenuLyt; // 0x68 + void * voidPtr; // 0x70 + al::LayoutActor * mKidsModeLyt; // 0x78 +}; + +static_assert(sizeof(StageSceneLayout) == 0x80, "StageSceneLayout size"); \ No newline at end of file diff --git a/include/game/StageScene/StageSceneStateOption.h b/include/game/StageScene/StageSceneStateOption.h new file mode 100644 index 0000000..fcf88ef --- /dev/null +++ b/include/game/StageScene/StageSceneStateOption.h @@ -0,0 +1,128 @@ +#pragma once + +#include "al/message/MessageSystem.h" +#include "al/nerve/HostStateBase.h" +#include "al/message/IUseMessageSystem.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/scene/Scene.h" +#include "al/LiveActor/LiveActor.h" +#include "al/util/GraphicsUtil.h" +#include "al/util/NerveUtil.h" +#include "game/Input/InputSeparator.h" +#include "game/Layouts/SimpleLayoutMenu.h" + +struct CommonVerticalList; + +class FooterParts { + public: + bool tryChangeTextFade(const char16_t *); +}; +class GameConfigData; + +namespace al +{ + class SimpleLayoutAppearWaitEnd; + class WindowConfirm; + class WindowConfirmData; + class KeyRepeatCtrl; +} // namespace al + +class StageSceneStateOption : public al::HostStateBase, public al::IUseMessageSystem { + public: + StageSceneStateOption(char const*,al::Scene *,al::LayoutInitInfo const&,FooterParts *,GameDataHolder *,bool); + ~StageSceneStateOption(); + + virtual al::MessageSystem* getMessageSystem(void) const override { return mMsgSystem; } + + void updateConfigDataInfo(GameConfigData const*); + void killAllLayouts(void); + void init(void); + void appear(void); + void kill(void); + void getSelectedFileId(void); + void decide(al::Nerve const*,SimpleLayoutMenu *,CommonVerticalList *); + void openConfirm(al::Nerve const*,SimpleLayoutMenu *,CommonVerticalList *); + void cancel(al::Nerve const*,SimpleLayoutMenu *,CommonVerticalList *); + void endConfig(void); + void updateSaveDataInfo(bool); + void changeNerve(al::Nerve const*, SimpleLayoutMenu*, CommonVerticalList*); + + bool isModeSelectEnd(void) const; + bool isChangeLanguage(void) const; + + void exeOptionTop(void); + void exeModeSelectSelecting(void); + void exeModeSelectSelectingByHelp(void); + void exeModeSelectConfirmYesNo(void); + void exeModeSelectConfirmEnd(void); + void exeConfig(void); + void exeDataManager(void); + void exeSaveDataSelecting(void); + void exeSaveDataConfirmYesNo(void); + void exeSaveDataSaving(void); + void exeSaveDataSaved(void); + void exeLoadDataSelecting(void); + void exeLoadDataConfirmNg(void); + void exeLoadDataConfirmYesNo(void); + void exeLoadDataSaving(void); + void exeDeleteDataSelecting(void); + void exeDeleteDataConfirmNg(void); + void exeDeleteDataConfirmYesNo(void); + void exeDeleteDataDeleting(void); + void exeDeleteDataDeleted(void); + void exeLanguageSetting(void); + void exeLanguageSettingConfirmYesNo(void); + void exeWaitEndDecideAnim(void); + void exeWaitEndDecideAnimAndAutoSave(void); + void exeWaitEndAutoSave(void); + void exeClose(void); + + void *field_0x28; + void *field_0x30; + void *field_0x38; + char *field_0x40; // stored with "RightIn" + char *field_0x48; // stored with "RightOut" + bool field_0x50; + unsigned char field_0x51[7]; + FooterParts *mFooterParts; + void *field_0x60; + SimpleLayoutMenu *field_0x68; + CommonVerticalList *field_0x70; + al::SimpleLayoutAppearWaitEnd *field_0x78; + CommonVerticalList *field_0x80; + SimpleLayoutMenu *field_0x88; + CommonVerticalList *field_0x90; + al::SimpleLayoutAppearWaitEnd *field_0x98; + FooterParts *field_0xa0; + SimpleLayoutMenu *field_0xa8; + CommonVerticalList *field_0xb0; + SimpleLayoutMenu *field_0xb8; + CommonVerticalList *field_0xc0; + SimpleLayoutMenu *field_0xc8; + void *field_0xd0; + al::WindowConfirmData *field_0xd8; + SimpleLayoutMenu *field_0xe0; + CommonVerticalList *field_0xe8; + void *field_0xf0; + void *field_0xf8; + void *field_0x100; + void *field_0x108; + void *field_0x110; + void *field_0x118; + void *field_0x120; + void *field_0x128; + void *field_0x130; + void *field_0x138; + int field_0x140; + int field_0x144; + SimpleLayoutMenu *field_0x148; + FooterParts *field_0x150; + CommonVerticalList *field_0x158; + al::WindowConfirm *field_0x160; + void *field_0x168; + al::Scene *mScene; + GameDataHolder *mDataHolder; + bool field_0x180; + al::MessageSystem *mMsgSystem; + InputSeparator *mInputSeperator; +}; \ No newline at end of file diff --git a/include/game/StageScene/StageSceneStatePauseMenu.h b/include/game/StageScene/StageSceneStatePauseMenu.h new file mode 100644 index 0000000..ecb7d05 --- /dev/null +++ b/include/game/StageScene/StageSceneStatePauseMenu.h @@ -0,0 +1,138 @@ +#pragma once + +#include "al/nerve/HostStateBase.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/layout/SimpleLayoutAppearWaitEnd.h" +#include "al/scene/Scene.h" +#include "al/LiveActor/LiveActor.h" +#include "StageSceneLayout.h" +#include "StageSceneStateOption.h" + +#include "al/util.hpp" +#include "al/util/NerveUtil.h" + +#include "game/UI/MenuSelectParts.h" +#include "logger.hpp" + +class FooterParts; +class StageSceneStateStartSeparatePlay; +class StageSceneStateEndSeparatePlay; +class SceneAudioSystemPauseController; + +namespace al +{ + class WindowConfirm; + class KeyRepeatCtrl; + class WipeSimple; +} // namespace al + +class StageSceneStatePauseMenu : public al::HostStateBase { + public: + StageSceneStatePauseMenu(char const*,al::Scene *,al::SimpleLayoutAppearWaitEnd *,GameDataHolder *,al::SceneInitInfo const&,al::ActorInitInfo const&,al::LayoutInitInfo const&,al::WindowConfirm *,StageSceneLayout *,bool,SceneAudioSystemPauseController *); + ~StageSceneStatePauseMenu(); + + void appear(void); + void kill(void); + void killPauseMenu(void); + void killMarioModel(void); + void isNeedKillHost(void); + void startNormal(void); + void startAfterTitle(void); + void killAllOptionLayout(void); + void isEndToCancel(void); + void isEndToHelp(void); + void isLoadData(void); + void getSelectedFileId(void); + void isChangeLanguage(void); + void getLanguage(void); + void isNewGame(void); + void isModeSelectEnd(void); + void checkNeedKillByHostAndEnd(void); + void startActionMario(char const*); + void getMarioActor(void); + void isDrawLayout(void); + void isDrawLayoutMain(void); + void isDrawViewRenderer(void); + void isDrawChromakey(void); + void setNormal(void); + void appearMarioModel(void); + void updatePlayerPose(void); + void changeNerveAndReturn(al::Nerve const*); + void startPauseCamera(void); + void setAfterTitle(void); + + void exeAppear(void); + void exeWait(void); + void exeFadeBeforeHelp(void); + void exeStartHelp(void); + void exeWaitDraw(void); + void exeEnd(void); + void exeStartSeparatePlay(void); + void exeEndSeparatePlay(void); + void exeOption(void); + void exeSave(void); + void exeConfirmNewGame(void); + void exeNotExistEmptyFile(void); + + void exeServerConfig(void); + + al::SimpleLayoutAppearWaitEnd *field_0x20; // 0x20 + al::SimpleLayoutAppearWaitEnd *mMenuGuide; // 0x28 + al::SimpleLayoutAppearWaitEnd *mMenuRight; // 0x30 + FooterParts *mFooterParts; // 0x38 + MenuSelectParts *mSelectParts; // 0x40 + al::WipeSimple *mWipeSimple; // 0x48 + void *field_0x50; // 0x50 + int field_0x58; + int field_0x5c; + StageSceneStateStartSeparatePlay *mStateStartSeperatePlay; + StageSceneStateEndSeparatePlay *mStateEndSeperatePlay; + StageSceneStateOption *mStateOption; + al::LiveActor *mMarioHigh; + unsigned char field_0x80[39]; + GameDataHolder *mDataHolder; + void *field_0xb0; + al::WindowConfirm *field_0xb8; + void *field_0xc0; + al::KeyRepeatCtrl *field_0xc8; + StageSceneLayout *mStageSceneLyt; + int field_0xd8; + int field_0xdc; + ulong field_0xe0; + void *field_0xe8; +}; + +namespace { + NERVE_HEADER(StageSceneStatePauseMenu, Appear) + NERVE_HEADER(StageSceneStatePauseMenu, Wait) + NERVE_HEADER(StageSceneStatePauseMenu, FadeBeforeHelp) + NERVE_HEADER(StageSceneStatePauseMenu, StartHelp) + NERVE_HEADER(StageSceneStatePauseMenu, WaitDraw) + NERVE_HEADER(StageSceneStatePauseMenu, End) + NERVE_HEADER(StageSceneStatePauseMenu, StartSeparatePlay) + NERVE_HEADER(StageSceneStatePauseMenu, EndSeparatePlay) + NERVE_HEADER(StageSceneStatePauseMenu, Option) + NERVE_HEADER(StageSceneStatePauseMenu, Save) + NERVE_HEADER(StageSceneStatePauseMenu, ConfirmNewGame) + NERVE_HEADER(StageSceneStatePauseMenu, NotExistEmptyFile) + // custom nerves + NERVE_HEADER(StageSceneStatePauseMenu, ServerConfig) +} + +namespace { + NERVE_IMPL(StageSceneStatePauseMenu, Appear) + NERVE_IMPL(StageSceneStatePauseMenu, Wait) + NERVE_IMPL(StageSceneStatePauseMenu, FadeBeforeHelp) + NERVE_IMPL(StageSceneStatePauseMenu, StartHelp) + NERVE_IMPL(StageSceneStatePauseMenu, WaitDraw) + NERVE_IMPL(StageSceneStatePauseMenu, End) + NERVE_IMPL(StageSceneStatePauseMenu, StartSeparatePlay) + NERVE_IMPL(StageSceneStatePauseMenu, EndSeparatePlay) + NERVE_IMPL(StageSceneStatePauseMenu, Option) + NERVE_IMPL(StageSceneStatePauseMenu, Save) + NERVE_IMPL(StageSceneStatePauseMenu, ConfirmNewGame) + NERVE_IMPL(StageSceneStatePauseMenu, NotExistEmptyFile) + // custom nerves + NERVE_IMPL(StageSceneStatePauseMenu, ServerConfig) +} + diff --git a/include/game/StageScene/StageSceneStateServerConfig.hpp b/include/game/StageScene/StageSceneStateServerConfig.hpp new file mode 100644 index 0000000..cbcbe19 --- /dev/null +++ b/include/game/StageScene/StageSceneStateServerConfig.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include "game/Input/InputSeparator.h" +#include "game/Layouts/CommonVerticalList.h" +#include "game/Layouts/SimpleLayoutMenu.h" +#include "al/message/MessageSystem.h" +#include "al/nerve/HostStateBase.h" +#include "al/message/IUseMessageSystem.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/scene/Scene.h" +#include "al/util/NerveUtil.h" +#include "rs/util/InputUtil.h" + +#include "al/util.hpp" + +#include "game/GameData/GameDataHolder.h" + +#include "logger.hpp" +#include "server/gamemode/GameModeConfigMenu.hpp" + +class FooterParts; + +class StageSceneStateServerConfig : public al::HostStateBase, public al::IUseMessageSystem { + public: + StageSceneStateServerConfig(const char*, al::Scene*, const al::LayoutInitInfo&, + FooterParts*, GameDataHolder*, bool); + + enum ServerConfigOption { + GAMEMODECONFIG, + GAMEMODESWITCH, + RECONNECT, + SETIP + }; + + virtual al::MessageSystem* getMessageSystem(void) const override; + virtual void init(void) override; + virtual void appear(void) override; + virtual void kill(void) override; + + void exeMainMenu(); + void exeOpenKeyboard(); + void exeRestartServer(); + void exeGamemodeConfig(); + void exeGamemodeSelect(); + + void endSubMenu(); + + private: + + inline void subMenuStart(); + inline void subMenuUpdate(); + + al::MessageSystem* mMsgSystem = nullptr; + FooterParts* mFooterParts = nullptr; + GameDataHolder* mGameDataHolder = nullptr; + + InputSeparator *mInput = nullptr; + + SimpleLayoutMenu* mCurrentMenu = nullptr; + CommonVerticalList* mCurrentList = nullptr; + // Root Page, contains buttons for gamemode config, server reconnecting, and server ip address changing + SimpleLayoutMenu* mMainOptions = nullptr; + CommonVerticalList *mMainOptionsList = nullptr; + // Sub-Page for Mode configuration, has buttons for selecting current gamemode and configuring currently selected mode (if no mode is chosen, button will not do anything) + SimpleLayoutMenu* mGamemodeConfig = nullptr; + CommonVerticalList* mGameModeConfigList = nullptr; + // Sub-Page of Mode config, used to select a gamemode for the client to use + SimpleLayoutMenu* mModeSelect = nullptr; + CommonVerticalList* mModeSelectList = nullptr; + // Controls what shows up in specific gamemode config page + GameModeConfigMenu *mGamemodeConfigMenu = nullptr; + + bool mIsDecideConfig = false; +}; + +namespace { + NERVE_HEADER(StageSceneStateServerConfig, MainMenu) + NERVE_HEADER(StageSceneStateServerConfig, OpenKeyboard) + NERVE_HEADER(StageSceneStateServerConfig, RestartServer) + NERVE_HEADER(StageSceneStateServerConfig, GamemodeConfig) + NERVE_HEADER(StageSceneStateServerConfig, GamemodeSelect) +} \ No newline at end of file diff --git a/include/game/StaticInstances.h b/include/game/StaticInstances.h new file mode 100644 index 0000000..c8e7155 --- /dev/null +++ b/include/game/StaticInstances.h @@ -0,0 +1,2 @@ +// Header file to store static instances of classes +#pragma once \ No newline at end of file diff --git a/include/game/System/Application.h b/include/game/System/Application.h new file mode 100644 index 0000000..f64124f --- /dev/null +++ b/include/game/System/Application.h @@ -0,0 +1,13 @@ +#pragma once + +#include "GameFrameWorkNx.h" + +class Application { + public: + static const Application *sInstance; + + unsigned char padding_28[0x28]; + al::GameFrameworkNx *mFramework; // 0x28 +}; + +// const Application *Application::sInstance; \ No newline at end of file diff --git a/include/game/System/GameDrawInfo.h b/include/game/System/GameDrawInfo.h new file mode 100644 index 0000000..e278d8d --- /dev/null +++ b/include/game/System/GameDrawInfo.h @@ -0,0 +1,21 @@ +#pragma once + +//#include "agl/DrawContext.h" +#include "agl/RenderBuffer.h" + +namespace agl +{ + struct DrawContext; +} // namespace agl + + +namespace al +{ + class GameDrawInfo { + public: + agl::RenderBuffer *mFirstRenderBuffer; // 0x0 + agl::RenderBuffer *mSecondRenderBuffer; // 0x8 + bool unkBool; // 0x10 + agl::DrawContext *mDrawContext; // 0x18 + }; +} // namespace al diff --git a/include/game/System/GameFrameWorkNx.h b/include/game/System/GameFrameWorkNx.h new file mode 100644 index 0000000..c3574b1 --- /dev/null +++ b/include/game/System/GameFrameWorkNx.h @@ -0,0 +1,26 @@ +#pragma once + +#include "al/debug/GpuPerf.h" +#include "agl/DrawContext.h" +#include "agl/RenderBuffer.h" +#include "sead/framework/seadGameFramework.h" +#include "al/hio/HioNode.h" + +namespace al +{ + class GameFrameworkNx : public sead::GameFrameworkNx, public al::HioNode { + public: + unsigned char padding_198[0x198]; + agl::RenderBuffer *mCurRenderBuffer; // 0x198 + unsigned char padding_210[0x208 - 0x198]; + agl::DrawContext *mDrawContext; // 0x210 + agl::RenderBuffer *mFirstRenderBuffer; // 0x218 + void *unkPtr1; // 0x220 + agl::RenderBuffer *mSecondRenderBuffer; // 0x228 + unsigned char padding_248[0x18]; + al::GpuPerf *mGpuPerf; // 0x248 + unsigned char padding_268[0x18]; + bool unkBool; // 0x268 + bool unkBool2; // 0x269 + }; +} // namespace al diff --git a/include/game/System/GameSystem.h b/include/game/System/GameSystem.h new file mode 100644 index 0000000..c699b34 --- /dev/null +++ b/include/game/System/GameSystem.h @@ -0,0 +1,12 @@ +#pragma once + +#include "GameSystemInfo.h" +#include "al/nerve/NerveExecutor.h" + +class GameSystem : public al::NerveExecutor { + public: + void init(); + void *gap; + al::GameSystemInfo* mSystemInfo; // 0x18 + // 0x78 GameConfigData +}; \ No newline at end of file diff --git a/include/game/System/GameSystemInfo.h b/include/game/System/GameSystemInfo.h new file mode 100644 index 0000000..b187ac6 --- /dev/null +++ b/include/game/System/GameSystemInfo.h @@ -0,0 +1,44 @@ +#pragma once + +#include "GameFrameWorkNx.h" +#include "GameDrawInfo.h" + +namespace aal +{ + struct IAudioFrameProcess; +} // namespace aal + +namespace al +{ + struct NetworkSystem; + struct HtmlViewer; + struct EffectSystem; + struct LayoutSystem; + struct MessageSystem; + struct GamePadSystem; + struct AudioSystem; + struct WaveVibrationHolder; // :: aal::IAudioFrameProcess; +} // namespace al + +struct ProjectNfpDirector; +struct ApplicationMessageReceiver; +struct Application; + +namespace al { + class GameSystemInfo { + public: + void *gap1; + al::EffectSystem *mEffectSys; // 0x08 + al::LayoutSystem *mLayoutSys; // 0x10 + al::MessageSystem *mMessageSys; // 0x18 + al::NetworkSystem *mNetworkSys; // 0x20 + al::AudioSystem *mAudioSys; // 0x28 + al::GamePadSystem *mGamePadSys; // 0x30 + al::GameDrawInfo *mDrawInfo; // 0x38 from Application::sInstance + 0x30 + ProjectNfpDirector *mProjNfpDirector; // 0x48 + al::HtmlViewer *mHtmlViewer; // 0x50 + ApplicationMessageReceiver *mMessageReciever; // 0x58 + al::WaveVibrationHolder *mWaveVibrationHolder; // 0x60 + void *gap2; + }; +} \ No newline at end of file diff --git a/include/game/UI/MenuSelectParts.h b/include/game/UI/MenuSelectParts.h new file mode 100644 index 0000000..84c82aa --- /dev/null +++ b/include/game/UI/MenuSelectParts.h @@ -0,0 +1,58 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" +#include "al/layout/LayoutActor.h" +#include "al/nerve/NerveExecutor.h" +#include "al/layout/KeyRepeatCtrl.h" +#include "sead/container/seadPtrArray.h" + +#include "types.h" + +class MenuSelectParts : public al::NerveExecutor { + public: + MenuSelectParts(char const*,al::LayoutActor *,al::LiveActor *,al::LayoutInitInfo const&,int); + void update(void); + void appear(int); + void startActionPartsIllustSelectIndex(void); + void appearWait(void); + void setSelectMessage(int,char16_t const*); + bool isDecideContinue(void) const; + bool isDecideEnd(void) const; + bool isSelectContinue(void) const; + bool isDecideSetting(void) const; + bool isSelectSetting(void) const; + bool isDecideSave(void) const; + bool isSelectSave(void) const; + bool isDecideSeparatePlay(void) const; + bool isSelectSeparatePlay(void) const; + bool isDecideHelp(void) const; + bool isSelectHelp(void) const; + bool isDecideNewGame(void) const; + bool isSelectNewGame(void) const; + void calcPartsIndex(int); + void exeHide(void); + void exeAppear(void); + void startActionMarioSelectIndex(void); + void exeSelect(void); + void startActionMario(al::LiveActor *,char const*); + void exeDecideParts(void); + bool isInvalidSelect(void) const; + void exeDecideInterval(void); + void exeDecideEnd(void); + ~MenuSelectParts(); + + // void *vtable; + // void *unk1; // 0x08 + al::LayoutActor *mRootLayout; // 0x10 + int mMaxSelectParts; // 0x18 + int mCursorItemIdx; // 0x1C + sead::PtrArray mSelectParts; // 0x20-0x28 + al::LayoutActor *mCursorActor; // 0x30 + void *unk3; // 0x38 + al::LiveActor *mMarioHigh; // 0x40 + al::LiveActor *mCapEyes; // 0x48 + al::KeyRepeatCtrl *mKeyRepeatCtrl; // 0x50 + bool isMenuMain; // 0x58 +}; + +// static_assert(sizeof(MenuSelectParts) == 0x60); \ No newline at end of file diff --git a/include/game/WorldList/WorldList.h b/include/game/WorldList/WorldList.h new file mode 100644 index 0000000..ebc2dc1 --- /dev/null +++ b/include/game/WorldList/WorldList.h @@ -0,0 +1,10 @@ +#pragma once + +class WorldList { + public: + const char *getWorldDevelopName(int) const; + int tryFindWorldIndexByStageName(char const *stageName) const; + int getMoonRockScenarioNo(int worldId) const; + int findUseScenarioNo(const char *stageName) const; + bool checkIsMainStage(char const *stageName) const; +}; \ No newline at end of file diff --git a/include/game/WorldList/WorldResourceLoader.h b/include/game/WorldList/WorldResourceLoader.h new file mode 100644 index 0000000..14ceaf0 --- /dev/null +++ b/include/game/WorldList/WorldResourceLoader.h @@ -0,0 +1,7 @@ +#pragma once + +class WorldResourceLoader { + public: + void loadWorldResource(int,int,bool,char const *); + bool requestLoadWorldHomeStageResource(int worldIndex, int scenario); +}; \ No newline at end of file diff --git a/include/helpers.hpp b/include/helpers.hpp new file mode 100644 index 0000000..8e3bbd4 --- /dev/null +++ b/include/helpers.hpp @@ -0,0 +1,168 @@ +#pragma once + +#include +#include +#include "types.h" + +#include "sead/math/seadVector.h" +#include "sead/math/seadQuat.hpp" + +#include "al/util.hpp" + +#include "logger.hpp" +#include "puppets/PuppetInfo.h" + +#include "game/GameData/GameDataFunction.h" + +#define RAD(deg) (deg * (M_PI / 180)) // converts Degrees to Radians +#define DEG(rad) (rad * (180 / M_PI)) // converts Radians to Degrees +#define BTOC(bool) (bool ? "True" : "False") // converts boolean to true/false char +#define ACNT(arr) (sizeof(arr) / sizeof(arr[0])) // returns size of inputted array +// used to convert macro values to strings +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +bool isPartOf(const char* w1, const char* w2); + +int indexOf(char *w1, char c1); + +void logVector(const char* vectorName, sead::Vector3f vector); + +void logQuat(const char *quatName, sead::Quatf quat); + +sead::Vector3f QuatToEuler(sead::Quatf *quat); + +float vecMagnitude(sead::Vector3f const &input); + +float quatAngle(sead::Quatf const &q1, sead::Quatf &q2); + +bool isInCostumeList(const char *costumeName); + +const char *tryGetPuppetCapName(PuppetInfo *info); +const char* tryGetPuppetBodyName(PuppetInfo* info); + +const char* tryConvertName(const char* className); + +void killMainPlayer(al::LiveActor* actor); +void killMainPlayer(PlayerActorHakoniwa* mainPlayer); + +__attribute__((used)) static const char* costumeNames[] = { + "Mario", + "Mario64", + "Mario64Metal", + "MarioAloha", + "MarioArmor", + "MarioBone", + "MarioClown", + "MarioColorClassic", + "MarioColorGold", + "MarioColorLuigi", + "MarioColorWaluigi", + "MarioColorWario", + "MarioCook", + "MarioDiddyKong", + "MarioDoctor", + "MarioExplorer", + "MarioFootball", + "MarioGolf", + "MarioGunman", + "MarioHakama", + "MarioHappi", + "MarioKing", + "MarioKoopa", + "MarioMaker", + "MarioMechanic", + "MarioNew3DS", + "MarioPainter", + "MarioPeach", + "MarioPilot", + "MarioPirate", + "MarioPoncho", + "MarioPrimitiveMan", + "MarioSailor", + "MarioScientist", + "MarioShopman", + "MarioSnowSuit", + "MarioSpaceSuit", + "MarioSuit", + "MarioSwimwear", + "MarioTailCoat", + "MarioTuxedo", + "MarioUnderwear" +}; +// full costume list from 1.3 +// attribute otherwise the build log is spammed with unused warnings +// __attribute__((used)) static const char* costumeNames[] = { +// "Mario", "Mario3D", "Mario64", "Mario64Metal", "MarioAloha", "MarioArmor", +// // "MarioArmorWestern", // DLC +// "MarioBandman", +// // "MarioBatter", // DLC +// "MarioBone", "MarioCaptain", "MarioClown", "MarioColorClassic", "MarioColorGold", +// "MarioColorLuigi", "MarioColorWaluigi", "MarioColorWario", +// // "MarioConductor", // DLC +// "MarioCook", "MarioDiddyKong", "MarioDoctor", "MarioDot", "MarioDot3d", "MarioExplorer", +// "MarioFootball", "MarioGolf", "MarioGunman", "MarioHakama", "MarioHappi", +// // "MarioHariet", // DLC +// // "MarioHigh", +// "MarioKing", "MarioKoopa", "MarioMaker", "MarioMechanic", "MarioNew3DS", "MarioPainter", +// "MarioPeach", "MarioPilot", "MarioPirate", "MarioPoncho", "MarioPrimitiveMan", "MarioRacer", +// //"MarioRango", // DLC +// //"MarioRsv", // DLC +// "MarioSailor", "MarioSanta", +// // "MarioSatellite", // DLC +// "MarioScientist", "MarioShopman", "MarioSnowSuit", "MarioSpaceSuit", +// // "MarioSpewart", // DLC +// "MarioSuit", +// // "MarioSunshine", // DLC +// "MarioSwimwear", +// // "MarioTopper", // DLC +// "MarioTuxedo", +// // "MarioZombie" // DLC +// }; + +struct HackActorName { + const char *className; + const char *hackName; +}; + +// attribute otherwise the build log is spammed with unused warnings +__attribute__((used)) static HackActorName classHackNames[] = { + {"SenobiGeneratePoint", "Senobi"}, + {"KuriboPossessed", "Kuribo"}, + {"KillerLauncher", "Killer"}, + {"KillerLauncherMagnum", "KillerMagnum"}, + {"FireBrosPossessed", "FireBros"}, + {"HammerBrosPossessed", "HammerBros"}, + {"ElectricWire", "ElectricWireMover"}, + {"TRexSleep", "TRex"}, + {"TRexPatrol", "TRex"}, + {"WanwanBig", "Wanwan"}, // FIXME: this will make chain chomp captures always be the small + // variant for syncing + {"Koopa","KoopaHack"} +}; + +struct Transform +{ + sead::Vector3f *position; + sead::Quatf *rotation; +}; + +// From Boss Room Unity Example +class VisualUtils +{ + +public: + /* + * @brief Smoothly interpolates towards the parent transform. + * @param moveTransform The transform to interpolate + * @param targetTransform The transform to interpolate towards. + * @param timeDelta Time in seconds that has elapsed, for purposes of interpolation. + * @param closingSpeed The closing speed in m/s. This is updated by SmoothMove every time it is called, and will drop to 0 whenever the moveTransform has "caught up". + * @param maxAngularSpeed The max angular speed to to rotate at, in degrees/s. + */ + static float SmoothMove(Transform moveTransform, Transform targetTransform, float timeDelta, + float closingSpeed, float maxAngularSpeed); + + constexpr static const float k_MinSmoothSpeed = 0.1f; + constexpr static const float k_TargetCatchupTime = 0.2f; +}; \ No newline at end of file diff --git a/include/layouts/HideAndSeekIcon.h b/include/layouts/HideAndSeekIcon.h new file mode 100644 index 0000000..b704023 --- /dev/null +++ b/include/layouts/HideAndSeekIcon.h @@ -0,0 +1,36 @@ +#pragma once + +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/util/NerveUtil.h" + +#include "logger.hpp" +#include "server/gamemode/GameModeTimer.hpp" + +// TODO: kill layout if going through loading zone or paused + +class HideAndSeekIcon : public al::LayoutActor { + public: + HideAndSeekIcon(const char* name, const al::LayoutInitInfo& initInfo); + + void appear() override; + + bool tryStart(); + bool tryEnd(); + + void showHiding(); + void showSeeking(); + + void exeAppear(); + void exeWait(); + void exeEnd(); + + private: + struct HideAndSeekInfo *mInfo; +}; + +namespace { + NERVE_HEADER(HideAndSeekIcon, Appear) + NERVE_HEADER(HideAndSeekIcon, Wait) + NERVE_HEADER(HideAndSeekIcon, End) +} \ No newline at end of file diff --git a/include/layouts/NameTag.h b/include/layouts/NameTag.h new file mode 100644 index 0000000..19edc2a --- /dev/null +++ b/include/layouts/NameTag.h @@ -0,0 +1,45 @@ +#pragma once + +#include "al/layout/LayoutActor.h" +#include "al/util/NerveUtil.h" +#include "al/util/LayoutUtil.h" +#include "al/util/LiveActorUtil.h" +#include "al/util/MathUtil.h" + +class PuppetActor; + +class NameTag : public al::LayoutActor { +public: + NameTag(PuppetActor*, const al::LayoutInitInfo&, float startDist, float endDist, const char *playerName); + + void appear(void) override; + void control(void) override; + void updateTrans(void); + void update(void); + void end(void); + void setText(char const*); + + bool isNearPlayerActor(float) const; + bool isVisible() const; + + const char *getCurrentState(); + + void exeAppear(void); + void exeWait(void); + void exeEnd(void); + void exeHide(void); + + PuppetActor *mPuppet; // 0x130 + const char *mPaneName; // 0x138 + float mStartDist; // 0x140 + float mEndDist; // 0x144 + float mNormalizedDist; // 0x148 + +}; + +namespace { +NERVE_HEADER(NameTag, Appear) +NERVE_HEADER(NameTag, Wait) +NERVE_HEADER(NameTag, End) +NERVE_HEADER(NameTag, Hide) +} // namespace \ No newline at end of file diff --git a/include/logger.hpp b/include/logger.hpp new file mode 100644 index 0000000..b88cbb7 --- /dev/null +++ b/include/logger.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "SocketBase.hpp" +#include "types.h" + +class Logger : public SocketBase { + public: + Logger(const char* ip, u16 port, const char* name) : SocketBase(name) { + this->init(ip, port); + }; + nn::Result init(const char* ip, u16 port) override; + + static void createInstance(); + static void setLogName(const char *name) { if(sInstance) sInstance->setName(name); } + static void log(const char* fmt, ...); + static void log(const char* fmt, va_list args); + + static void enableName() { if(sInstance) sInstance->isDisableName = false; } + static void disableName() { if(sInstance) sInstance->isDisableName = true; } + + int read(char *out); + bool pingSocket(); + + private: + static Logger* sInstance; + bool isDisableName; +}; \ No newline at end of file diff --git a/include/main.hpp b/include/main.hpp new file mode 100644 index 0000000..7b82e89 --- /dev/null +++ b/include/main.hpp @@ -0,0 +1,75 @@ +#include "al/LiveActor/LiveActor.h" +#include "al/camera/CameraTicket.h" +#include "al/util.hpp" +#include "al/layout/LayoutActor.h" +#include "al/layout/LayoutKit.h" +#include "al/gamepad/util.h" +#include "al/camera/CameraPoser.h" +#include "al/camera/alCameraPoserFunction.h" +#include "al/factory/ProjectActorFactory.h" +#include "al/area/AreaObjGroup.h" +#include "al/actor/DemoActor.h" +#include "al/area/ChangeStageInfo.h" + +#include "game/StageScene/StageScene.h" +#include "game/Layouts/CoinCounter.h" +#include "game/Player/PlayerFunction.h" +#include "game/Player/PlayerCameraTarget.h" +#include "game/Player/PlayerAnimControlRun.h" +#include "game/Player/PlayerFactory.h" +#include "game/Player/PlayerCostumeInfo.h" +#include "game/Controller/ControllerAppletFunction.h" +#include "game/GameData/GameDataHolderWriter.h" +#include "game/GameData/GameDataFunction.h" +#include "game/Actors/Shine.h" +#include "game/HakoniwaSequence/HakoniwaSequence.h" +#include "game/UI/MenuSelectParts.h" +#include "game/StageScene/StageSceneStateServerConfig.hpp" +#include "game/System/Application.h" + +#include "rs/util.hpp" +#include "rs/util/SensorUtil.h" + +#include "sead/math/seadVector.h" +#include "sead/math/seadMatrix.h" +#include "sead/prim/seadSafeString.hpp" +#include "sead/gfx/seadCamera.h" +#include "sead/basis/seadNew.h" +#include "sead/gfx/seadColor.h" +#include "types.h" + +#include "agl/DrawContext.h" +#include "agl/RenderBuffer.h" +#include "agl/utl.h" + +#include "nn/swkbd/swkbd.h" + +#include "helpers.hpp" +#include "logger.hpp" +#include "server/Client.hpp" +#include "debugMenu.hpp" +#include "Keyboard.hpp" +#include "server/DeltaTime.hpp" + +static const int playBufSize = 8; + +static bool isInGame = false; + +static bool debugMode = false; + +constexpr const char* captureNames[] = { + "AnagramAlphabetCharacter", "Byugo", "Bubble", "Bull", "Car", "ElectricWire", + "KillerLauncherMagnum", "KuriboPossessed", + "WanwanBig", // has sub-actors + "KillerLauncher", "Koopa", + "Wanwan", // has sub-actors + "Pukupuku", "PukupukuSnow", + "Gamane", // has sub-actors + "FireBrosPossessed", "PackunFire", "Frog", "Kakku", "Hosui", "HammerBrosPossessed", "Megane", + "KaronWing", "KuriboWing", "PackunPoison", "Radicon", "Tank", "Tsukkun", "TRex", "TRexSleep", + "TRexPatrol", + // "Yukimaru", (is a player actor) + "Imomu", "SenobiGeneratePoint" + // "HackFork", + // "Yoshi" (is a player actor) +}; \ No newline at end of file diff --git a/include/nn.h b/include/nn.h new file mode 100644 index 0000000..fcd8d4a --- /dev/null +++ b/include/nn.h @@ -0,0 +1,12 @@ + +#pragma once + +#include "nn/result.h" +#include "nn/util.h" +#include "nn/os.h" +#include "nn/fs.h" +#include "nn/diag.h" +#include "nn/nifm.h" +#include "nn/settings.h" +#include "nn/err.h" +#include "nn/socket.h" \ No newline at end of file diff --git a/include/nn/account.h b/include/nn/account.h new file mode 100644 index 0000000..1f05d2f --- /dev/null +++ b/include/nn/account.h @@ -0,0 +1,82 @@ +/** + * @file account.h + * @brief Account service implementation. + */ + +#pragma once + +#include "os.h" +#include "types.h" + +#include "logger.hpp" + +namespace nn +{ + namespace account + { + // typedef char Nickname[0x21]; + // typedef u64 Uid[0x2]; + + struct Nickname { + char name[0x21] = {}; + }; + struct Uid { + char data[0x10] = {}; + + bool operator==(const Uid &rhs) const { + return memcmp(data, rhs.data, 0x10) == 0; + } + + Uid& operator=(const Uid& other) + { + memcpy(this->data, other.data, 0x10); + return *this; + } + + inline void print() { + Logger::log("Player ID: 0x"); + Logger::disableName(); + for (size_t i = 0; i < 0x10; i++) { Logger::log("%02X", data[i]); } + Logger::log("\n"); + Logger::enableName(); + } + + inline void print(const char *prefix) { + Logger::log("%s: 0x", prefix); + Logger::disableName(); + for (size_t i = 0; i < 0x10; i++) { Logger::log("%02X", data[i]); } + Logger::log("\n"); + Logger::enableName(); + } + }; + + typedef u64 NetworkServiceAccountId; + + class AsyncContext; + struct UserHandle; + + void Initialize(); + Result ListAllUsers(s32 *, nn::account::Uid *, s32 numUsers); + Result OpenUser(nn::account::UserHandle *, nn::account::Uid const &); + Result IsNetworkServiceAccountAvailable(bool *out, nn::account::UserHandle const &); + void CloseUser(nn::account::UserHandle const &); + + Result EnsureNetworkServiceAccountAvailable(nn::account::UserHandle const &userHandle); + Result EnsureNetworkServiceAccountIdTokenCacheAsync(nn::account::AsyncContext *, nn::account::UserHandle const &); + Result LoadNetworkServiceAccountIdTokenCache(u64 *, char *, u64, nn::account::UserHandle const &); + + Result GetLastOpenedUser(nn::account::Uid *); + Result GetNickname(nn::account::Nickname *nickname, nn::account::Uid const &userID); + + class AsyncContext + { + public: + AsyncContext(); + + Result HasDone(bool *); + Result GetResult(); + Result Cancel(); + Result GetSystemEvent(nn::os::SystemEvent *); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk.h b/include/nn/atk.h new file mode 100644 index 0000000..0c6c310 --- /dev/null +++ b/include/nn/atk.h @@ -0,0 +1,21 @@ +#pragma once + +#include "types.h" + +namespace nn { namespace atk { + +class SoundArchive +{ +public: + const char* GetItemLabel(u32 id) const; + u32 GetItemId(char const* label) const; +}; + +class SoundActor // Inherits SoundStartable, size: 0x7C +{ +public: + virtual ~SoundActor(); + u8 data[0x7C-0x4]; +}; + +} } diff --git a/include/nn/atk/SoundArchivePlayer.h b/include/nn/atk/SoundArchivePlayer.h new file mode 100644 index 0000000..d35bff7 --- /dev/null +++ b/include/nn/atk/SoundArchivePlayer.h @@ -0,0 +1,40 @@ +/** + * @file SoundArchivePlayer.h + * @brief Basic sound player from a sound archive. + */ + +#pragma once + +#include "detail/AdvancedWaveSoundRuntime.h" +#include "detail/SequenceSoundRuntime.h" +#include "detail/SoundArchiveManager.h" +#include "detail/StreamSoundRuntime.h" +#include "detail/WaveSoundRuntime.h" + +namespace nn +{ + namespace atk + { + class SoundArchivePlayer + { + public: + SoundArchivePlayer(); + + virtual ~SoundArchivePlayer(); + + bool IsAvailable() const; + void Finalize(); + void StopAllSound(s32, bool); + void DisposeInstances(); + + nn::atk::detail::SoundArchiveManager mArchiveManager; // _8 + nn::atk::detail::SequenceSoundRuntime mSeqSoundRuntime; // _50 + nn::atk::detail::WaveSoundRuntime mWaveSoundRuntime; // _130 + nn::atk::detail::AdvancedWaveSoundRuntime mAdvancedWaveSound; // _1B0 + nn::atk::detail::StreamSoundRuntime mStreamSoundRuntime; // _1E0 + u64 _290; + u32 _298; + u8 _29C[0x2E8-0x29C]; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/SoundDataManager.h b/include/nn/atk/SoundDataManager.h new file mode 100644 index 0000000..2ca0be8 --- /dev/null +++ b/include/nn/atk/SoundDataManager.h @@ -0,0 +1,28 @@ +/** + * @file SoundDataManager.h + * @brief Sound data management implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace atk + { + class SoundDataManager + { + public: + SoundDataManager(); + virtual ~SoundDataManager(); + + virtual void InvalidateData(void const *, void const *); + virtual void SetFileAddressToTable(u32, void const *); + virtual u64 GetFileAddressFromTable(u32) const; + virtual u32 GetFileAddressImpl(u32) const; + + u8 _0[0x240]; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/SoundPlayer.h b/include/nn/atk/SoundPlayer.h new file mode 100644 index 0000000..d77103b --- /dev/null +++ b/include/nn/atk/SoundPlayer.h @@ -0,0 +1,60 @@ +/** + * @file SoundPlayer.h + * @brief Sound player. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace atk + { + enum PauseMode + { + + }; + + class SoundPlayer + { + public: + SoundPlayer(); + ~SoundPlayer(); + + void StopAllSound(s32); + void Update(); + void DoFreePlayerHeap(); + void detail_SortPriorityList(bool); + void PauseAllSound(s32, bool); + void PauseAllSound(bool, s32, nn::atk::PauseMode); + void SetVolume(f32 vol); + void SetLowPassFilterFrequency(f32 filterFreq); + void SetBiquadFilter(s32 filterType, f32 baseFreq); + void SetDefaultOutputLine(u32 line); + + void detail_SetPlayableSoundLimit(s32 limit); + bool CanPlaySound(s32); + + u64 _0; + u64 _8; + u64 _10; + u64 _18; + u64 _20; + u64 _28; + u64 _30; + u64 _38; + s32 _40; + s32 mPlayableSoundCount; // _44 + s32 _48; + f32 mVolume; // _4C + f32 mLowPassFreq; // _50 + s32 mFilterType; // _54 + f32 mBaseFreq; // _58 + u32 mDefaultOutputLine; // _5C + f32 mOutputVolume; // _60 + u64 _64; + u64 _6C; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/detail/AdvancedWaveSoundRuntime.h b/include/nn/atk/detail/AdvancedWaveSoundRuntime.h new file mode 100644 index 0000000..b78860a --- /dev/null +++ b/include/nn/atk/detail/AdvancedWaveSoundRuntime.h @@ -0,0 +1,32 @@ +/** + * @file AdvancedWaveSoundRuntime.h + * @brief Runtime wave sound api. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace atk + { + namespace detail + { + class AdvancedWaveSoundRuntime + { + public: + AdvancedWaveSoundRuntime(); + ~AdvancedWaveSoundRuntime(); + + void Initialize(s32, void **, void const *); + void Finalize(); + s32 GetActiveCount() const; + void SetupUserParam(void **, u64); + void Update(); + + u8 _0[0x30]; + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/detail/BasicSound.h b/include/nn/atk/detail/BasicSound.h new file mode 100644 index 0000000..04090b0 --- /dev/null +++ b/include/nn/atk/detail/BasicSound.h @@ -0,0 +1,143 @@ +/** + * @file BasicSound.h + * @brief A basic sound. + */ + +#pragma once + +#include "types.h" +#include "nn/atk/SoundPlayer.h" + +namespace nn +{ + namespace atk + { + class SoundActor; + + enum MixMode + { + + }; + + namespace detail + { + class PlayerHeap; + class ExternalSoundPlayer; + + class BasicSound + { + public: + BasicSound(); + virtual ~BasicSound(); + + virtual void Initialize(); + virtual void Finalize(); + virtual bool IsPrepared() const = 0; + virtual bool IsAttachedTempSpecialHandle() = 0; + virtual void DetachTempSpecialHandle() = 0; + virtual void OnUpdatePlayerPriority(); + virtual void UpdateMoveValue(); + virtual void OnUpdateParam(); + + void SetPriority(s32, s32); + void GetPriority(s32 *, s32 *) const; + void ClearIsFinalizedForCannotAllocatedResourceFlag(); + void SetId(u32 newID); + bool IsAttachedGeneralHandle(); + void DetachGeneralHandle(); + bool IsAttachedTempGeneralHandle(); + void DetachTempGeneralHandle(); + void StartPrepared(); + void Stop(s32); + void SetPlayerPriority(s32); + void ForceStop(); + void Pause(bool, s32); + void Mute(bool, s32); + void SetAutoStopCounter(s32); + void FadeIn(s32); + bool IsPause() const; + bool IsMute() const; + void Update(); + void UpdateParam(); + void UpdateMoveValue(); + void CalculateVolume() const; + f32 CalculatePitch() const; + f32 CalculateLpfFrequency() const; + u32 CalculateOutLineFlag() const; + void CalculateBiquadFilter(s32 *, f32 *) const; + void AttachPlayerHeap(nn::atk::detail::PlayerHeap *); + void DetachPlayerHeap(nn::atk::detail::PlayerHeap *); + void AttachSoundPlayer(nn::atk::SoundPlayer *); + void DetachSoundPlayer(nn::atk::SoundPlayer *); + void AttachSoundActor(nn::atk::SoundActor *); + void DetachSoundActor(nn::atk::SoundActor *); + void AttachExternalSoundPlayer(nn::atk::detail::ExternalSoundPlayer *); + void DetachExternalSoundPlayer(nn::atk::detail::ExternalSoundPlayer *); + u32 GetRemainingFadeFrames() const; + u32 GetRemainingPauseFadeFrames() const; + u32 GetRemainingMuteFadeFrames() const; + void SetInitialVolume(f32 vol); + f32 GetInitialVolume() const; + void SetVolume(f32, s32); + s32 GetVolume() const; + void SetPitch(f32); + f32 GetPitch() const; + void SetLpfFreq(f32); + f32 GetLpfFreq() const; + void SetBiquadFilter(s32, f32); + void GetBiquadFilter(s32 *, f32 *) const; + void SetOutputLine(u32); + u32 GetOutputLine() const; + void ResetOutputLine(); + void SetMixMode(nn::atk::MixMode); + nn::atk::MixMode GetMixMode(); + void SetPan(f32); + f32 GetPan() const; + void SetSurroundPan(f32); + f32 GetSurroundPan() const; + void SetMainSend(f32); + f32 GetMainSend() const; + + u64* _8; // nn::atk::detail::PlayerHeap* + u64* _10; // nn::atk::SoundHandle* + u64* _18; // nn::atk::SoundHandle* + nn::atk::SoundPlayer* mSoundPlayer; // _20 + u64* _28; // nn::atk::SoundActor* + u64* _30; // nn::atk::detail::ExternalSoundPlayer* + u64* _38; // nn::atk::SoundArchive* + u8 _40[0xF0-0x40]; + s32 mPriority; // _F0 + u32 _F4; + u32 _F8; + s32 mAutoStopCounter; // _FC + u64 _100; + u32 mID; // _108 + u32 _10C; + u32 _110; + u32 _114; + f32 mInitialVolume; // _118 + f32 mPitch; // _11C + f32 mLpfFreq; // _120 + f32 _124; + u32 mOutputLine; // _128 + f32 _12C; + f32 mVolume; // _130 + u32 _134; + u32 _138; + nn::atk::MixMode mMixMode; // _13C + f32 mPan; // _140 + f32 mSurroundPan; // _144 + f32 mMainSend; // _148 + u8 _14C[0x158-0x14C]; + f32 mOutputVol; // _158 + u8 _15C[0x190-0x15C]; + f32 mOutputPan; // _190 + f32 mOutputSurroundPan; // _194 + f32 mOutputMainSend; // _198 + f32 mOutputFxSend; // _19C + + static u64 g_LastInstanceId; + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/detail/SequenceSoundRuntime.h b/include/nn/atk/detail/SequenceSoundRuntime.h new file mode 100644 index 0000000..735ad2b --- /dev/null +++ b/include/nn/atk/detail/SequenceSoundRuntime.h @@ -0,0 +1,41 @@ +/** + * @file SequenceSoundRuntime.h + * @brief Sequenced Sound Runtime Info + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace atk + { + namespace detail + { + class SoundArchiveManager; + + class SequenceSoundRuntime + { + public: + SequenceSoundRuntime(); + ~SequenceSoundRuntime(); + + void Initialize(s32, void **, void const *); + void Finalize(); + void SetupSequenceTrack(s32, void **, void const *); + void SetupUserParam(void **, u64); + bool IsSoundArchiveAvailable() const; + s32 GetActiveCount() const; + s32 GetFreeCount() const; + void SetSequenceSkipIntervalTick(s32 tick); + s32 GetSequenceSkipIntervalTick(); + void Update(); + + u8 _0[0xD0]; + nn::atk::detail::SoundArchiveManager* mArchiveManager; // _D0 + u64 _D8; + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/detail/SoundArchiveManager.h b/include/nn/atk/detail/SoundArchiveManager.h new file mode 100644 index 0000000..59d010e --- /dev/null +++ b/include/nn/atk/detail/SoundArchiveManager.h @@ -0,0 +1,46 @@ +/** + * @file SoundArchiveManager.h + * @brief Sound archive manager implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace atk + { + class SoundHandle; + class SoundArchive; + class SoundDataManager; + + namespace detail + { + class AddonSoundArchiveContainer; + + class SoundArchiveManager + { + public: + SoundArchiveManager(); + + virtual ~SoundArchiveManager(); + + void Initialize(nn::atk::SoundArchive const *, nn::atk::SoundDataManager const *); + void ChangeTargetArchive(char const *); + void Finalize(); + bool IsAvailable() const; + nn::atk::detail::AddonSoundArchiveContainer* GetAddonSoundArchive(char const *) const; + + u64 _8; + u64* _10; + nn::atk::detail::AddonSoundArchiveContainer* _18; + u64* _20; + nn::atk::SoundArchive* mSoundArchive; // _28 + u64 _30; + u64 _38; + u64 _40; + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/detail/StreamSoundRuntime.h b/include/nn/atk/detail/StreamSoundRuntime.h new file mode 100644 index 0000000..9873a57 --- /dev/null +++ b/include/nn/atk/detail/StreamSoundRuntime.h @@ -0,0 +1,26 @@ +/** + * @file StreamSoundRuntime.h + * @brief Stream sound runtime information. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace atk + { + namespace detail + { + class StreamSoundRuntime + { + public: + StreamSoundRuntime(); + ~StreamSoundRuntime(); + + u8 _0[0xB0]; + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/atk/detail/WaveSoundRuntime.h b/include/nn/atk/detail/WaveSoundRuntime.h new file mode 100644 index 0000000..5c6db84 --- /dev/null +++ b/include/nn/atk/detail/WaveSoundRuntime.h @@ -0,0 +1,33 @@ +/** + * @file WaveSoundRuntime.h + * @brief Wave sound runtime info. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace atk + { + namespace detail + { + class WaveSoundRuntime + { + public: + WaveSoundRuntime(); + ~WaveSoundRuntime(); + + void Initialize(s32, void **, void const *); + void Finalize(); + s32 GetActiveCount() const; + s32 GetFreeWaveSoundCount() const; + void SetupUserParam(void **, u64); + void Update(); + + u8 _0[0x80]; + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/audio.h b/include/nn/audio.h new file mode 100644 index 0000000..8d992fd --- /dev/null +++ b/include/nn/audio.h @@ -0,0 +1,49 @@ +/** + * @file audio.h + * @brief Audio implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace audio + { + struct AudioRendererConfig + { + u64* _0; + u64* _8; + u64* _10; + u64* _18; + u64* _20; + u64* _28; + u64* _30; + u64* _38; + u64* _40; + u64* _48; + u64* _50; + }; + + struct DelayType + { + u64* _0; + }; + + struct FinalMixType + { + u64* _0; + }; + + struct SubMixType + { + u64* _0; + }; + + void SetDelayInputOutput(nn::audio::DelayType *, s8 const *, s8 const *, s32); + void* RemoveDelay(nn::audio::AudioRendererConfig *, nn::audio::DelayType *, nn::audio::FinalMixType *); + void* RemoveDelay(nn::audio::AudioRendererConfig *, nn::audio::DelayType *, nn::audio::SubMixType *); + bool IsDelayRemoveable(nn::audio::DelayType *); + }; +}; \ No newline at end of file diff --git a/include/nn/crypto.h b/include/nn/crypto.h new file mode 100644 index 0000000..6702114 --- /dev/null +++ b/include/nn/crypto.h @@ -0,0 +1,89 @@ +/** + * @file crypto.h + * @brief Crypto service implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace crypto + { + class Sha256Context; + + void DecryptAes128Cbc(void *, u64, void const *, u64, void const *, u64, void const *, u64); + void EncryptAes128Cbc(void *, u64, void const *, u64, void const *, u64, void const *, u64); + void DecryptAes128Ccm(void *, u64, void *, u64, void const *, u64, void const *, u64, void const *, u64, void const *, u64, u64); + + namespace detail + { + class Md5Impl + { + public: + void Initialize(); + void Update(void const *, u64 dataSize); + void ProcessBlock(); + void GetHash(void *, u64 hashSize); + void ProcessLastBlock(); + + u32 _0; + u32 _4; + u32 _8; + u32 _C; + u8 _10[0x50-0x10]; + u64 _50; + u32 _58; + }; + + class Sha1Impl + { + public: + void Initialize(); + void Update(void const *, u64); + void ProcessBlock(void const *); + void GetHash(void *destHash, u64); + void ProcessLastBlock(); + + u64 _0; + u64 _8; + u32 _10; + u128 _14; + u128 _24; + u128 _34; + u32 _44; + u64 _48; + u64 _50; + u64 _58; + u64 _60; + }; + + class Sha256Impl + { + public: + void Initialize(); + void Update(void const *, u64); + void ProcessBlocks(u8 const *, u64); + void GetHash(void *destHash, u64); + void ProcessLastBlock(); + void InitializeWithContext(nn::crypto::Sha256Context const *); + void GetContext(nn::crypto::Sha256Context *) const; + + u64 _0; + u64 _8; + u32 _10; + u128 _14; + u128 _24; + u128 _34; + u32 _44; + u64 _48; + u64 _50; + u64 _58; + u64 _60; + u64 _68; + u32 _70; + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/diag.h b/include/nn/diag.h new file mode 100644 index 0000000..c1acefa --- /dev/null +++ b/include/nn/diag.h @@ -0,0 +1,18 @@ +#pragma once + +#include "types.h" + +namespace nn { namespace diag { + +struct ModuleInfo +{ + const char* path; + u32 base; + u32 size; +}; + +u32 GetAllModuleInfo(ModuleInfo** outModuleInfos, void* buffer, u32 bufferLen); +u32 GetRequiredBufferSizeForGetAllModuleInfo(); +u32 GetModulePath(char* outName, u32 nameLenMax, u32 address); + +} } diff --git a/include/nn/err.h b/include/nn/err.h new file mode 100644 index 0000000..f2b1a8c --- /dev/null +++ b/include/nn/err.h @@ -0,0 +1,22 @@ +#pragma once + +#include "result.h" +#include "settings.h" + +namespace nn { namespace err { + +class ApplicationErrorArg +{ +public: + ApplicationErrorArg(); + ApplicationErrorArg(u32 code, const char* str1, const char* str2, const nn::settings::LanguageCode& languageCode); + + u8 data[0x1014]; +}; + +void ShowError(Result result); +void ShowApplicationError(const ApplicationErrorArg& arg); +void ShowUnacceptableApplicationVersionError(); +void ShowUnacceptableAddOnContentVersionError(); + +} } diff --git a/include/nn/friends.h b/include/nn/friends.h new file mode 100644 index 0000000..a46580f --- /dev/null +++ b/include/nn/friends.h @@ -0,0 +1,44 @@ +/** + * @file friends.h + * @brief Friend implementation. + */ + +#pragma once + +#include "account.h" +#include "os.h" + +namespace nn +{ + namespace friends + { + typedef char Url[0xA0]; + + class AsyncContext; + class Profile; + + void Initialize(); + Result GetProfileList(nn::friends::AsyncContext* context, nn::friends::Profile* profiles, nn::account::Uid const &userID, nn::account::NetworkServiceAccountId const *accountIDs, s32 numAccounts); + + class Profile + { + public: + Profile(); + + nn::account::NetworkServiceAccountId GetAccountId() const; + nn::account::Nickname& GetNickname() const; + bool IsValid() const; + Result GetProfileImageUrl(nn::friends::Url *, s32); + }; + + class AsyncContext + { + public: + AsyncContext(); + ~AsyncContext(); + + Result GetSystemEvent(nn::os::SystemEvent *); + Result GetResult() const; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/fs.h b/include/nn/fs.h new file mode 100644 index 0000000..2df8217 --- /dev/null +++ b/include/nn/fs.h @@ -0,0 +1,91 @@ +#pragma once + +#include "result.h" + +namespace nn { namespace fs { + +const s32 PathLengthMax = 0x300; + +struct FileHandle +{ + u32 handle; +}; + +struct DirectoryHandle +{ + u32 handle; +}; + +struct DirectoryEntry +{ + char name[PathLengthMax+1]; // 0 + char _301[3]; // 301 + u8 type; // 304 + s64 fileSize; // 308 +}; + +enum OpenMode +{ + OpenMode_Read = 1 << 0, + OpenMode_Write = 1 << 1, + OpenMode_ReadWrite = OpenMode_Read | OpenMode_Write +}; + +enum DirectoryMode +{ + DirectoryMode_Directories = 1 << 0, + DirectoryMode_Files = 1 << 1, + DirectoryMode_DirectoriesFiles = DirectoryMode_Directories | DirectoryMode_Files +}; + +enum DirectoryEntryType +{ + DirectoryEntryType_Directory = 0, + DirectoryEntryType_File = 1 +}; + +struct ReadOption +{ + ReadOption(u32 flags = 0) : flags(flags) { } + u32 flags; +}; + +struct WriteOption +{ + enum Flags + { + Flush = 1 + }; + + WriteOption(u32 flags = 0) : flags(flags) { } + u32 flags; +}; + + +Result OpenFile(FileHandle* out, const char* path, s32 mode); +void CloseFile(FileHandle handle); +Result ReadFile(FileHandle handle, s64 offset, void* buffer, u32 size); +Result ReadFile(FileHandle handle, s64 offset, void* buffer, u32 size, const ReadOption& options); +Result WriteFile(FileHandle handle, s64 offset, const void* buffer, u32 size, const WriteOption& options = WriteOption(0)); +Result FlushFile(FileHandle handle); +Result GetFileSize(s64* out, FileHandle handle); +Result SetFileSize(FileHandle handle, s64 size); + +Result CreateFile(const char* path, s64 size); +Result DeleteFile(const char* path); +Result RenameFile(const char* path, const char* newPath); + +Result OpenDirectory(DirectoryHandle* out, const char* path, s32 mode); +void CloseDirectory(DirectoryHandle handle); +Result ReadDirectory(s64* out, DirectoryEntry* entries, DirectoryHandle handle, s64 maxEntryCount); +Result GetDirectoryEntryCount(s64* out, DirectoryHandle handle); + +Result CreateDirectory(char const* path); +Result DeleteDirectory(const char* path); +Result DeleteDirectoryRecursively(const char* path); +Result RenameDirectory(const char* path, const char* newPath); + +Result MountSdCard(const char* mountPoint); +void Unmount(const char* mountPoint); + +} } diff --git a/include/nn/g3d.h b/include/nn/g3d.h new file mode 100644 index 0000000..436bfd7 --- /dev/null +++ b/include/nn/g3d.h @@ -0,0 +1,3 @@ +#pragma once + +#include \ No newline at end of file diff --git a/include/nn/g3d/BindFuncTable.h b/include/nn/g3d/BindFuncTable.h new file mode 100644 index 0000000..225d3f2 --- /dev/null +++ b/include/nn/g3d/BindFuncTable.h @@ -0,0 +1,18 @@ +#pragma once + +#include "types.h" + +namespace nn +{ + namespace g3d + { + struct BindFuncTable + { + u64 _0; + u32 _8; + u32 _C; + u64 _10; + u64 _18; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResFile.h b/include/nn/g3d/ResFile.h new file mode 100644 index 0000000..cf2da25 --- /dev/null +++ b/include/nn/g3d/ResFile.h @@ -0,0 +1,69 @@ +/** + * @file ResFile.h + * @brief Resource file for models. + */ + +#pragma once + +#include "types.h" +#include "nn/gfx/api.h" +#include "nn/gfx/device.h" +#include "nn/gfx/memory.h" +#include "nn/util.h" + +#include "ResMaterialAnim.h" +#include "ResModel.h" +#include "ResSceneAnim.h" +#include "ResShapeAnim.h" + +namespace nn +{ + namespace g3d + { + typedef void* TextureRef; + + class ResFile : public nn::util::BinaryFileHeader + { + public: + static bool IsValid(void const *modelSrc); + void Relocate(); + void Unrelocate(); + static nn::g3d::ResFile* ResCast(void *); + s32 BindTexture(nn::g3d::TextureRef (*ref)(char const *, void *), void *); + void ReleaseTexture(); + void Setup(nn::gfx::TDevice, nn::gfx::ApiVersion<8>>> *); + void Setup(nn::gfx::TDevice,nn::gfx::ApiVersion<8>>> *, nn::gfx::TMemoryPool, nn::gfx::ApiVersion<8>>> *, s64, u64); + void Cleanup(nn::gfx::TDevice, nn::gfx::ApiVersion<8>>> *); + void Reset(); + + u64 mFileNameLength; // _20 + nn::g3d::ResModel* mModels; // _28 + u64 mModelDictOffset; // _30 + u64 mSkeleAnimOffset; // _38 + u64 mSkeleAnimDictOffset; // _40 + nn::g3d::ResMaterialAnim* mMatAnims; // _48 + u64 mMatAnimsDictOffset; // _50 + u64 mBoneVisiOffset; // _58 + u64 mBoneVisiDictOffset; // _60 + nn::g3d::ResShapeAnim* mShapeAnims; // _68 + u64 mShapeAnimDictOffset; // _70 + nn::g3d::ResSceneAnim* mSceneAnims; // _78 + u64 mSceneAnimDictOffset; // _80 + u64 mMemoryPool; // _88 + u64 mBufferSection; // _90 + u64 mEmbeddedFilesOffset; // _98 + u64 mEmbeddedFilesDictOffset; // _A0 + u64 mPadding; // _A8 + u64 mStrTableOffset; // _B0 + u32 mStrTableSize; // _B8 + u16 mModelCount; // _BC + u16 mSkeleAnimCount; // _BE + u16 mMatAnimCount; // _C0 + u16 mBoneAnimCount; // _C2 + u16 mShapeAnimCount; // _C4 + u16 mSceneAnimCount; // _C6 + u16 mExternalFileCount; // _C8 + u8 mPad[0x6]; // _CA + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResFogAnim.h b/include/nn/g3d/ResFogAnim.h new file mode 100644 index 0000000..e8e588d --- /dev/null +++ b/include/nn/g3d/ResFogAnim.h @@ -0,0 +1,25 @@ +#pragma once + +#include "types.h" + +namespace nn +{ + namespace g3d + { + class ResFogAnim + { + public: + char mMagic[4]; // _0 + u16 mFlags; // _4 + u16 mPad; // _6 + s32 mNumFrames; // _8 + u8 mNumCurves; // _C + u8 mIdxDistanceAttnFunc; // _D + u16 mNumUserData; // _E + u32 mSizeBaked; // _10 + u64 mNameOffset; // _14 + u64 mFuncNameOffset; // _1C + + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResLightAnim.h b/include/nn/g3d/ResLightAnim.h new file mode 100644 index 0000000..976c2ac --- /dev/null +++ b/include/nn/g3d/ResLightAnim.h @@ -0,0 +1,15 @@ +#pragma once + +#include "BindFuncTable.h" + +namespace nn +{ + namespace g3d + { + class ResLightAnim + { + public: + s32 Bind(nn::g3d::BindFuncTable const &); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResMaterial.h b/include/nn/g3d/ResMaterial.h new file mode 100644 index 0000000..fca9ea3 --- /dev/null +++ b/include/nn/g3d/ResMaterial.h @@ -0,0 +1,32 @@ +/** + * @file ResMaterial.h + * @brief Resource material for models. + */ + +#pragma once + +#include "types.h" +#include "nn/gfx/api.h" +#include "nn/gfx/device.h" + +namespace nn +{ + namespace g3d + { + typedef void* TextureRef; + + class ResMaterial + { + public: + u64 BindTexture(nn::g3d::TextureRef (*)(char const *, void *), void *); + void ForceBindTexture(nn::g3d::TextureRef const &, char const *); + void ReleaseTexture(); + void Setup(nn::gfx::TDevice, nn::gfx::ApiVersion<4>>> *); + void Cleanup(nn::gfx::TDevice, nn::gfx::ApiVersion<8>>> *); + void Reset(); + void Reset(u32); + + u8 _0[0xB4]; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResMaterialAnim.h b/include/nn/g3d/ResMaterialAnim.h new file mode 100644 index 0000000..fc52f30 --- /dev/null +++ b/include/nn/g3d/ResMaterialAnim.h @@ -0,0 +1,22 @@ +/** + * @file ResMaterialAnim.h + * @brief Resource file for material animations. + */ + +#pragma once + +namespace nn +{ + namespace g3d + { + typedef void* TextureRef; + + class ResMaterialAnim + { + public: + void ReleaseTexture(); + s32 BindTexture(nn::g3d::TextureRef (*)(char const*, void *), void *); + void Reset(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResModel.h b/include/nn/g3d/ResModel.h new file mode 100644 index 0000000..2969d1a --- /dev/null +++ b/include/nn/g3d/ResModel.h @@ -0,0 +1,35 @@ +/** + * @file ResModel.h + * @brief Resource model. + */ + +#pragma once + +#include "types.h" +#include "nn/gfx/api.h" +#include "nn/gfx/device.h" + +namespace nn +{ + namespace g3d + { + class ResMaterial; + + typedef void* TextureRef; + + class ResModel + { + public: + u64 BindTexture(nn::g3d::TextureRef (*)(char const *, void *), void *); + void ForceBindTexture(nn::g3d::TextureRef const &, char const *); + void ReleaseTexture(); + void Setup(nn::gfx::TDevice, nn::gfx::ApiVersion<8>>> *); + void Cleanup(nn::gfx::TDevice, nn::gfx::ApiVersion<8>>> *); + void Reset(); + void Reset(u32); + nn::g3d::ResMaterial* FindMaterial(char const *materialName) const; + + u8 _0[0x70]; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResSceneAnim.h b/include/nn/g3d/ResSceneAnim.h new file mode 100644 index 0000000..b336df6 --- /dev/null +++ b/include/nn/g3d/ResSceneAnim.h @@ -0,0 +1,43 @@ +/** + * @file ResSceneAnim.h + * @brief Resource file for scene animations. + */ + +#pragma once + +#include "BindFuncTable.h" +#include "ResFogAnim.h" +#include "ResLightAnim.h" +#include "types.h" + +namespace nn +{ + namespace g3d + { + class ResSceneAnim + { + public: + s32 Bind(nn::g3d::BindFuncTable const &); + void Release(); + void Reset(); + + char mMagic[4]; // _0 + s32 mBlockOffset; // _4 + u64 mBlockSize; // _8 + u64 mNameOffset; // _10 + u64 mPathOffset; // _18 + u64 mCameraAnimOffset; // _20 + u64 mCameraAnimDictOffset; // _28 + nn::g3d::ResLightAnim* mLightAnims; // _30 + u64 mLightAnimDictOffset; // _38 + nn::g3d::ResFogAnim* mFogAnims; // _40 + u64 mFogAnimDictOffset; // _48 + u64 mUserDataOffset; // _50 + u64 mUserDataDictOffset; // _58 + u16 mUserDataCount; // _60 + u16 mCameraAnimCount; // _62 + u16 mLightAnimCount; // _64 + u16 mFogAnimCount; // _66 + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResShapeAnim.h b/include/nn/g3d/ResShapeAnim.h new file mode 100644 index 0000000..0564ad6 --- /dev/null +++ b/include/nn/g3d/ResShapeAnim.h @@ -0,0 +1,18 @@ +/** + * @file ResShapeAnim.h + * @brief Resource file for shape animations. + */ + +#pragma once + +namespace nn +{ + namespace g3d + { + class ResShapeAnim + { + public: + void Reset(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/ResSkeletalAnim.h b/include/nn/g3d/ResSkeletalAnim.h new file mode 100644 index 0000000..4d5efaf --- /dev/null +++ b/include/nn/g3d/ResSkeletalAnim.h @@ -0,0 +1,18 @@ +/** + * @file ResSkeletalAnim.h + * @brief Resource file for skeletal animations. + */ + +#pragma once + +namespace nn +{ + namespace g3d + { + class ResSkeletalAnim + { + public: + void Reset(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/g3d/g3d_ResFile.h b/include/nn/g3d/g3d_ResFile.h new file mode 100644 index 0000000..93d97f9 --- /dev/null +++ b/include/nn/g3d/g3d_ResFile.h @@ -0,0 +1,24 @@ +#pragma once + +#include "nn/util/util_AccessorBase.h" + +namespace nn +{ + namespace g3d + { + struct ResFileData + { + // empty for now + }; + + class ResFile : public nn::util::AccessorBase + { + public: + + static ResFile* ResCast(void *); + + void ReleaseTexture(); + void Reset(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/gfx/api.h b/include/nn/gfx/api.h new file mode 100644 index 0000000..8b14315 --- /dev/null +++ b/include/nn/gfx/api.h @@ -0,0 +1,33 @@ +/** + * @file api.h + * @brief GFX API version and typing. + */ + +#pragma once + +namespace nn +{ + namespace gfx + { + // passes both ApiType<4> and ApiVersion<8> + template + class ApiVariation + { + + }; + + // usually passed as just a 4 + template + class ApiType + { + + }; + + // usually passed as just a 8 + template + class ApiVersion + { + + }; + }; +}; \ No newline at end of file diff --git a/include/nn/gfx/buffer.h b/include/nn/gfx/buffer.h new file mode 100644 index 0000000..43d2c8a --- /dev/null +++ b/include/nn/gfx/buffer.h @@ -0,0 +1,32 @@ +/** + * @file buffer.h + * @brief GFX Buffers. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace gfx + { + class BufferInfo + { + public: + void SetDefault(); + + u64 mInfo; // _0 + }; + + class BufferTextureViewInfo + { + public: + void SetDefault(); + + u64 _0; + u64 _8; + u64 _10; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/gfx/detail/bufferimpl.h b/include/nn/gfx/detail/bufferimpl.h new file mode 100644 index 0000000..15c1140 --- /dev/null +++ b/include/nn/gfx/detail/bufferimpl.h @@ -0,0 +1,51 @@ +/** + * @file bufferimpl.h + * @brief Buffer implementation for GFX. + */ + +#pragma once + +#include "nn/gfx/api.h" +#include "nn/gfx/buffer.h" + +namespace nn +{ + namespace gfx + { + class GpuAddress; + + namespace detail + { + template + class BufferImpl + { + public: + BufferImpl(); + ~BufferImpl(); + + void Initialize(nn::gfx::detail::DeviceImpl,nn::gfx::ApiVersion<8>>> *,nn::gfx::BufferInfo const&,nn::gfx::detail::MemoryPoolImpl,nn::gfx::ApiVersion<8>>> *, s64, u64); + void Finalize(nn::gfx::detail::DeviceImpl,nn::gfx::ApiVersion<8>>> *); + void* Map() const; + void Unmap() const; + void FlushMappedRange(s64, u64) const; + void InvalidateMappedRange(s64, u64) const; + void GetGpuAddress(nn::gfx::GpuAddress *) const; + + T* mBuff; // _0 + }; + + template + class CommandBufferImpl + { + public: + CommandBufferImpl(); + ~CommandBufferImpl(); + + void Reset(); + void Begin(); + void End(); + void Dispatch(s32, s32, s32); + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/gfx/detail/deviceimpl.h b/include/nn/gfx/detail/deviceimpl.h new file mode 100644 index 0000000..105bb79 --- /dev/null +++ b/include/nn/gfx/detail/deviceimpl.h @@ -0,0 +1,28 @@ +/** + * @file deviceimpl.h + * @brief Device implementation for GFX. + */ + +#pragma once + +#include "nn/gfx/device.h" + +namespace nn +{ + namespace gfx + { + namespace detail + { + template + class DeviceImpl + { + public: + DeviceImpl(); + ~DeviceImpl(); + + void Initialize(nn::gfx::DeviceInfo const &deviceInfo); + void Finalize(); + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/gfx/detail/pool.h b/include/nn/gfx/detail/pool.h new file mode 100644 index 0000000..f97c69e --- /dev/null +++ b/include/nn/gfx/detail/pool.h @@ -0,0 +1,41 @@ +#pragma once + +#include "deviceimpl.h" + +namespace nn +{ + namespace gfx + { + struct MemoryPoolInfo; + + namespace detail + { + class MemoryPoolData + { + public: + void SetDefault(); + + s32 _0; // set to 0x88 + s32 _4; + u64 _8; + }; + + template + class MemoryPoolImpl + { + public: + MemoryPoolImpl(); + ~MemoryPoolImpl(); + + void Initialize(nn::gfx::detail::DeviceImpl, nn::gfx::ApiVersion<8>>> *, nn::gfx::MemoryPoolInfo const &); + void Finalize(nn::gfx::detail::DeviceImpl, nn::gfx::ApiVersion<8>>> *); + void* Map() const; + void Unmap() const; + void FlushMappedRange(s64, u64) const; + void InvalidateMappedRange(s64, u64) const; + + u8 _0[0x120]; // pool data + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/gfx/device.h b/include/nn/gfx/device.h new file mode 100644 index 0000000..1f02bac --- /dev/null +++ b/include/nn/gfx/device.h @@ -0,0 +1,27 @@ +/** + * @file device.h + * @brief Device implementation for GFX. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace gfx + { + class DeviceInfo + { + public: + + u64 mInfo; // _0 + }; + + template + class TDevice + { + + }; + }; +}; \ No newline at end of file diff --git a/include/nn/gfx/memory.h b/include/nn/gfx/memory.h new file mode 100644 index 0000000..8c0e5b6 --- /dev/null +++ b/include/nn/gfx/memory.h @@ -0,0 +1,16 @@ +/** + * @file memory.h + * @brief GFX Memory Pool. + */ + +#pragma once + +namespace nn +{ + namespace gfx + { + // todo: finish me! + template + class TMemoryPool { }; + }; +}; \ No newline at end of file diff --git a/include/nn/hid.h b/include/nn/hid.h new file mode 100644 index 0000000..051f76d --- /dev/null +++ b/include/nn/hid.h @@ -0,0 +1,24 @@ +/** + * @file hid.h + * @brief Functions that help process gamepad inputs. + */ + +#pragma once + +#include "types.h" +#include "util.h" + +namespace nn +{ + namespace hid + { + struct NpadHandheldState; + struct NpadStyleTag; + + void InitializeNpad(); + void SetSupportedNpadIdType(u32 const* , u64); + void SetSupportedNpadStyleSet(nn::util::BitFlagSet<32, nn::hid::NpadStyleTag>); + void GetNpadStyleSet(u32 const &); + void GetNpadStates(nn::hid::NpadHandheldState *, s32, u32 const &); + }; +}; \ No newline at end of file diff --git a/include/nn/image.h b/include/nn/image.h new file mode 100644 index 0000000..8cc0b25 --- /dev/null +++ b/include/nn/image.h @@ -0,0 +1,64 @@ +/** + * @file image.h + * @brief JPEG decoding library. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace image + { + // there's probably more + enum JpegStatus + { + OK = 0, + INVALID_FORMAT = -32, + UNSUPPORTED_FORMAT = -33, + OUT_OF_MEMORY = -64, + }; + + enum PixelFormat + { + RGBA32, + RGB24 + }; + + enum ProcessStage + { + UNREGISTERED = 0, + REGISTERED = 1, + ANALYZED = 2 + }; + + struct Dimension + { + f32 width; + f32 height; + }; + + class JpegDecoder + { + public: + JpegDecoder(); + virtual ~JpegDecoder(); + + void SetImageData(void const *source, u64 size); + nn::image::JpegStatus Analyze(); + nn::image::Dimension GetAnalyzedDimension() const; + s64 GetAnalyzedWorkBufferSize() const; + JpegStatus Decode(void *out, s64, s32 alignment, void *, s64); + + nn::image::ProcessStage mProcessStage; // _8 + void* mData; // _C + s64 mSize; // _14 + s32 _18; + nn::image::PixelFormat mFormat; // _1C + Dimension mImgDimensions; // _20 + s64 _28; + // rest is related to EXIF processing + }; + }; +}; \ No newline at end of file diff --git a/include/nn/init.h b/include/nn/init.h new file mode 100644 index 0000000..bf4dc56 --- /dev/null +++ b/include/nn/init.h @@ -0,0 +1,24 @@ +/** + * @file init.h + * @brief Initialization functions for OS related functions. + */ + +#pragma once + +#include "mem.h" +#include "types.h" + +namespace nn +{ + namespace init + { + void InitializeAllocator(void *addr, u64 size); + nn::mem::StandardAllocator* GetAllocator(); + + namespace detail + { + void* DefaultAllocatorForThreadLocal(u64, u64); + void* DefaultDeallocatorForThreadLocal(void *, u64); + }; + } +}; \ No newline at end of file diff --git a/include/nn/mem.h b/include/nn/mem.h new file mode 100644 index 0000000..3ce39ba --- /dev/null +++ b/include/nn/mem.h @@ -0,0 +1,33 @@ +/** + * @file mem.h + * @brief Memory allocation functions. + */ + +#pragma once + +#include "os.h" +#include "types.h" + +namespace nn +{ + namespace mem + { + class StandardAllocator + { + public: + StandardAllocator(); + + void Initialize(void* address, u64 size); + void Finalize(); + void* Reallocate(void* address, u64 newSize); + void* Allocate(u64 size); + void Free(void* address); + void Dump(); + + bool mIsInitialized; // _0 + bool mIsEnabledThreadCache; // _1 + u16 _2; + u64* mAllocAddr; // _4 + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/RootObject.h b/include/nn/nex/RootObject.h new file mode 100644 index 0000000..f3d919e --- /dev/null +++ b/include/nn/nex/RootObject.h @@ -0,0 +1,33 @@ +/** + * @file RootObject.h + * @brief RootObject for NEX. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace nex + { + class RootObject + { + public: + enum TargetPool; + + virtual ~RootObject(); + + void* operator new(std::u64); + void operator delete(void *); + void* operator new(std::u64, char const *, u32); + void* operator new[](std::u64); + void* operator new[](std::u64, char const *, u32); + void operator delete[](void *); + void operator delete(void *,char const *, u32); + void operator delete[](void *,char const *, u32); + void* operator new(std::u64, nn::nex::RootObject::TargetPool); + void* operator new(std::u64, nn::nex::RootObject::TargetPool, char const *, u32); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/auth.h b/include/nn/nex/auth.h new file mode 100644 index 0000000..3ec7522 --- /dev/null +++ b/include/nn/nex/auth.h @@ -0,0 +1,25 @@ +/** + * @file auth.h + * @brief Authorization for DDL. + */ + +#pragma once + +#include "ddl.h" +#include "types.h" + +namespace nn +{ + namespace nex + { + class NintendoAuthenticationDDLDeclarations : public nn::nex::DDLDeclarations + { + public: + virtual ~NintendoAuthenticationDDLDeclarations(); + virtual void Init(); + + void Register(); + + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/berkeley.h b/include/nn/nex/berkeley.h new file mode 100644 index 0000000..4329737 --- /dev/null +++ b/include/nn/nex/berkeley.h @@ -0,0 +1,52 @@ +#pragma once + +#include "types.h" + +namespace nn +{ + namespace nex + { + + namespace TransportProtocol + { + static enum Type { + unk1, // or this one + unk2, // i think we need to use this one + unk3, + unk4, + unk5, + unk6 + }; + } + + namespace SocketDriver + { + struct InetAddress { + + }; + + enum _SocketFlag { + + }; + } + + + namespace BerkeleySocketDriver { + class BerkeleySocket + { + public: + BerkeleySocket(); + BerkeleySocket(const nn::nex::BerkeleySocketDriver::BerkeleySocket *, int); + ~BerkeleySocket(); + + bool Open(nn::nex::TransportProtocol::Type); + bool SetAsync(bool); + bool SetBroadcastMode(bool); // empty class that always returns true + bool Bind(ushort &); + bool LastSocketErrorToResult(char const*, long); + s32 GetLastSocketError(long); // unknown if this returns anything, it branches to an external function probably located in nnsdk, but uses of it seem to require some sort of int + s32 RecvFrom(uchar *, ulong, nn:nex::SocketDriver::InetAddress *, unsigned long *, nn::nex::SocketDriver::_SocketFlag); + }; + } + }; +}; \ No newline at end of file diff --git a/include/nn/nex/buffer.h b/include/nn/nex/buffer.h new file mode 100644 index 0000000..8de56cd --- /dev/null +++ b/include/nn/nex/buffer.h @@ -0,0 +1,28 @@ +/** + * @file buffer.h + * @brief NEX buffer implementation. + */ + +#pragma once + +#include "string.h" +#include "types.h" + +namespace nn +{ + namespace nex + { + // todo + class Buffer + { + public: + Buffer(nn::nex::Buffer const &); + Buffer(nn::nex::Buffer &&); + Buffer(nn::nex::String const &); + + void FreeDataBuffer(u8 *, u64); + + virtual ~Buffer(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/cache.h b/include/nn/nex/cache.h new file mode 100644 index 0000000..fd6f63a --- /dev/null +++ b/include/nn/nex/cache.h @@ -0,0 +1,36 @@ +/** + * @file cache.h + * @brief NEX Cache Mangement. + */ + +#pragma once + +#include "string.h" + +namespace nn +{ + namespace nex + { + class BasicCache; + + class CacheManager + { + public: + CacheManager(); + ~CacheManager(); + + nn::nex::BasicCache* GetCache(nn::nex::String const &); + }; + + class BasicCache + { + public: + BasicCache(nn::nex::String const &); + + virtual ~BasicCache(); + + u64 _8; + u8 _10; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/checksum.h b/include/nn/nex/checksum.h new file mode 100644 index 0000000..6bb72f3 --- /dev/null +++ b/include/nn/nex/checksum.h @@ -0,0 +1,66 @@ +/** + * @file checksum.h + * @brief NEX Checksum Implementation. + */ + +#pragma once + +#include "buffer.h" +#include "RootObject.h" +#include "types.h" + +namespace nn +{ + namespace nex + { + class ChecksumAlgorithm : public nn::nex::RootObject + { + public: + ChecksumAlgorithm(); + + virtual ~ChecksumAlgorithm(); + + virtual bool ComputeChecksum(nn::nex::Buffer const &, nn::nex::Buffer *) = 0; + // virtual bool ComputeChecksum(u8 const **, u64 const *, u64, nn::nex::SignatureBytes &) = 0; + virtual bool IsReady() const; + virtual void ComputeChecksumForTransport(u8 const *, u64); + virtual u32 ComputeChecksumForTransportArray(u8 const **, u64 const*, u64) = 0; + virtual u32 GetChecksumLength() = 0; + + u64 _8; + u8 _10; + }; + + class MD5Checksum : public nn::nex::ChecksumAlgorithm + { + public: + MD5Checksum(); + + virtual ~MD5Checksum(); + + virtual bool ComputeChecksum(nn::nex::Buffer const &, nn::nex::Buffer *); + virtual u32 ComputeChecksumForTransportArray(u8 const **, u64 const*, u64); + virtual u32 GetChecksumLength(); + }; + + class CRC16Checksum : public nn::nex::ChecksumAlgorithm + { + public: + CRC16Checksum(); + + virtual ~CRC16Checksum(); + + virtual bool ComputeChecksum(nn::nex::Buffer const &, nn::nex::Buffer *); + virtual u32 ComputeChecksumForTransportArray(u8 const **, u64 const*, u64); + virtual u32 GetChecksumLength(); + }; + + /* + this actually inherits some sort of KeyedChecksum thing. whatever + class HMACChecksum : public nn::nex::ChecksumAlgorithm + { + + }; + */ + }; +}; \ No newline at end of file diff --git a/include/nn/nex/client.h b/include/nn/nex/client.h new file mode 100644 index 0000000..91bbb3e --- /dev/null +++ b/include/nn/nex/client.h @@ -0,0 +1,119 @@ +/** + * @file client.h + * @brief Client implementations for NEX. + */ +#pragma once + +#include "system.h" + +namespace nn +{ + namespace nex + { + class Credentials; + class EndPoint; + class Message; + class ProtocolCallContext; + class ProtocolRequestBrokerInterface; + + class Protocol : public nn::nex::SystemComponent + { + public: + enum _Command + { + Response, + Request + }; + + enum _Type + { + Client, // implemented in nn::nex::ClientProtocol + Server // implemented in nn::nex::ServerProtocol + }; + + Protocol(u32); + + virtual ~Protocol(); + + virtual char* GetType() const; + virtual bool IsAKindOf(char const *) const; + virtual void EnforceDeclareSysComponentMacro(); + + virtual bool BeginInitialization(); + virtual bool BeginTermination(); + + virtual nn::nex::Protocol::_Type GetProtocolType() const = 0; + virtual void EndPointDisconnected(nn::nex::EndPoint *); + virtual void FaultDetected(nn::nex::EndPoint *, u32); + virtual nn::nex::Protocol* Clone() const; + virtual bool Reload(); + + nn::nex::EndPoint* GetOutgoingConnection() const; + void SetIncomingConnection(nn::nex::EndPoint *); + void SetProtocolID(u16); + void AddMethodID(nn::nex::Message *, u32); + void CopyMembers(nn::nex::Protocol const *); + void AssociateProtocolRequestBroker(nn::nex::ProtocolRequestBrokerInterface *); + void ClearFlag(u32 newFlag); + + static void ExtractProtocolKey(nn::nex::Message *, nn::nex::Protocol::_Command &, u16 &); + static bool IsOldRVDDLVersion(nn::nex::EndPoint *); + + u16 mProtocolID; // _48 + u16 _4A; + u32 _4C; + nn::nex::EndPoint* mOutgoingConnection; // _50 + nn::nex::ProtocolRequestBrokerInterface* mBrokerInterface; // _58 + u32 mFlags; // _60 + u32 _64; + nn::nex::EndPoint* mIncomingConnection; // _68 + u32 mUseLoopback; // _70 (boolean) + u32 _74; + u64 _78; + u32 _80; + u32 _84; + }; + + class ClientProtocol : public nn::nex::Protocol + { + public: + ClientProtocol(u32); + + virtual ~ClientProtocol(); + + virtual char* GetType() const; + virtual bool IsAKindOf(char const *) const; + virtual void EnforceDeclareSysComponentMacro(); + + virtual nn::nex::Protocol::_Type GetProtocolType() const = 0; + + virtual void ExtractCallSpecificResults(nn::nex::Message *, nn::nex::ProtocolCallContext *) = 0; + virtual nn::nex::ClientProtocol* CreateResponder() const = 0; + virtual void SetDefaultCredentials(nn::nex::Credentials *); + + bool SendOverLocalLoopback(nn::nex::ProtocolCallContext *, nn::nex::Message *); + bool SendRMCMessage(nn::nex::ProtocolCallContext *, nn::nex::Message *); + void ProcessResponse(nn::nex::Message *, nn::nex::EndPoint *); + + nn::nex::Credentials* mCredentials; // _88 + }; + + class ServerProtocol : public nn::nex::Protocol + { + public: + ServerProtocol(u32); + + virtual ~ServerProtocol(); + + virtual char* GetType() const; + virtual bool IsAKindOf(char const *) const; + virtual void EnforceDeclareSysComponentMacro(); + + virtual nn::nex::Protocol::_Type GetProtocolType() const = 0; + + virtual void DispatchProtocolMessage(nn::nex::Message *, nn::nex::Message *, bool *, nn::nex::EndPoint *) = 0; + virtual void DispatchProtocolMessageWithAttemptCount(u64, nn::nex::Message *, nn::nex::Message *, bool *, int *, nn::nex::EndPoint *); + virtual bool UseAttemptCountMethod(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/data.h b/include/nn/nex/data.h new file mode 100644 index 0000000..52cc6a5 --- /dev/null +++ b/include/nn/nex/data.h @@ -0,0 +1,24 @@ +/** + * @file data.h + * @brief NEX Data. + */ + +#pragma once + +#include "RootObject.h" + +namespace nn +{ + namespace nex + { + class Data : public nn::nex::RootObject + { + public: + Data(); + + virtual ~Data(); + + u8 _8; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/ddl.h b/include/nn/nex/ddl.h new file mode 100644 index 0000000..afc3508 --- /dev/null +++ b/include/nn/nex/ddl.h @@ -0,0 +1,44 @@ +/** + * @file ddl.h + * @brief DDL Declaration Implementation. + */ + +#pragma once + +#include "RootObject.h" +#include "types.h" + +namespace nn +{ + namespace nex + { + class DDLDeclarations : public nn::nex::RootObject + { + public: + DDLDeclarations(bool); + + virtual ~DDLDeclarations(); + + virtual void Init() = 0; + + void RegisterIfRequired(); + void Unregister(); + static void UnregisterAll(); + void LoadAll(); + void Load(); + void UnloadAll(); + void Unload(); + void ResetDOClassIDs(); + + u32 mNumDecsLoaded; // _8 + u8 _C; + u8 _D; // padding + u8 _E; // ^^ + u8 _F; // ^^ + u64 _10; + bool _18; + + static nn::nex::DDLDeclarations* s_pFirstDDLDecl; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/dynamic.h b/include/nn/nex/dynamic.h new file mode 100644 index 0000000..9eb79ac --- /dev/null +++ b/include/nn/nex/dynamic.h @@ -0,0 +1,24 @@ +/** + * @file dynamic.h + * @brief NEX Dyamnic Runtime. + */ + +#pragma once + +#include "RootObject.h" + +namespace nn +{ + namespace nex + { + class DynamicRunTimeInterface : public nn::nex::RootObject + { + public: + DynamicRunTimeInterface(); + + virtual ~DynamicRunTimeInterface(); + + u64* GetInstance(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/encryption.h b/include/nn/nex/encryption.h new file mode 100644 index 0000000..e29c0c9 --- /dev/null +++ b/include/nn/nex/encryption.h @@ -0,0 +1,65 @@ +/** + * @file encryption.h + * @brief NEX Encryption Algorithm. + */ + +#pragma once + +#include "buffer.h" +#include "key.h" +#include "RootObject.h" +#include "sead/critical.h" + +namespace nn +{ + namespace nex + { + class EncryptionAlgorithm : public nn::nex::RootObject + { + public: + EncryptionAlgorithm(u32, u32); + + virtual ~EncryptionAlgorithm(); + + virtual bool Encrypt(nn::nex::Buffer const &, nn::nex::Buffer *) = 0; + virtual bool Encrypt(nn::nex::Buffer *); + virtual bool Decrypt(nn::nex::Buffer const &, nn::nex::Buffer *) = 0; + virtual bool Decrypt(nn::nex::Buffer *); + virtual bool GetErrorString(u32, char *destStr, u64 errLen); + virtual void KeyHasChanged(); + + bool SetKey(nn::nex::Key const &key); + + u64 _8; + u64 _10; + u64 _18; + u64 _20; + u64 _28; + u64 _30; + u64 _38; + u64 _40; + }; + + class RC4Encryption : public nn::nex::EncryptionAlgorithm + { + public: + RC4Encryption(); + + virtual ~RC4Encryption(); + + virtual bool Encrypt(nn::nex::Buffer const &, nn::nex::Buffer *); + virtual bool Encrypt(nn::nex::Buffer *); + virtual bool Decrypt(nn::nex::Buffer const &, nn::nex::Buffer *); + virtual bool Decrypt(nn::nex::Buffer *); + + virtual void KeyHasChanged(); + + void GetDefaultKey(); + void PrepareEncryption(); + void ReinitStateArray(); + void SetReinitEverytime(bool); + + u8 _48[0x298-0x48]; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/hash.h b/include/nn/nex/hash.h new file mode 100644 index 0000000..076692e --- /dev/null +++ b/include/nn/nex/hash.h @@ -0,0 +1,40 @@ +/** + * @file hash.h + * @brief NEX Hash Implementation. + */ + +#pragma once + +#include "nn/crypto.h" +#include "RootObject.h" +#include "types.h" + +namespace nn +{ + namespace nex + { + class MD5 : public nn::crypto::detail::Md5Impl, public nn::nex::RootObject + { + public: + MD5(); + + void init(); + void raw_digest(u8 *); + void hex_digest(); + + u8 _5C[0x74-0x5C]; + }; + + class Sha1 : public nn::crypto::detail::Sha1Impl, public nn::nex::RootObject + { + public: + Sha1(); + + void Update(void const *, u64); + void GetHash(void *, u64); + void GenerateHash(void *, u64, void const *, u64); + + u32 _68; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/instance.h b/include/nn/nex/instance.h new file mode 100644 index 0000000..b39e448 --- /dev/null +++ b/include/nn/nex/instance.h @@ -0,0 +1,50 @@ +/** + * @file instance.h + * @brief NEX Instance Controllers. + */ +#pragma once + +#include "RootObject.h" + +namespace nn +{ + namespace nex + { + class InstanceTable; + + class InstanceControl : public nn::nex::RootObject + { + public: + InstanceControl(u32, u32); + + u32 mInstanceContext; // _8 + u32 mInstanceType; // _C + void* mDelegateInstance; // _10 + bool mIsValidControl; // _18 + u8 _19; // probably padding + u8 _1A; + u8 _1B; + + static nn::nex::InstanceTable* s_oInstanceTable; + }; + + class InstanceTable : public nn::nex::RootObject + { + public: + InstanceTable(); + + virtual ~InstanceTable(); + + bool AddInstance(nn::nex::InstanceControl *, u32, u32); + void DelInstance(nn::nex::InstanceControl *, u32, u32); + u32 CreateContext(); + bool DeleteContext(u32); + void AllocateExtraContexts(u64 size); + void FreeExtraContexts(); + u32 GetHighestID() const; + u32 FindInstanceContext(nn::nex::InstanceControl *, u32); + + u8 _0[0x94]; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/key.h b/include/nn/nex/key.h new file mode 100644 index 0000000..bceae69 --- /dev/null +++ b/include/nn/nex/key.h @@ -0,0 +1,42 @@ +/** + * @file key.h + * @brief NEX Key Implementation. + */ + +#pragma once + +#include "RootObject.h" +#include "string.h" + +namespace nn +{ + namespace nex + { + class Key : public nn::nex::RootObject + { + public: + Key(); + Key(u8 const *src, u64 size); + Key(u64 size); + Key(nn::nex::Key const &); + Key(nn::nex::String const &); + + virtual ~Key(); + + u64* GetContentPtr(); + u64 GetLength() const; + nn::nex::Key& operator=(nn::nex::Key const &); + bool operator==(nn::nex::Key const &); + bool operator!=(nn::nex::Key const &); + void PrepareContentPtr(u64); + nn::nex::String* ToString(); + void ExtractToString(nn::nex::String *) const; + void Trace(u64) const; + void GenerateRandomKey(u64); + + u64* mContentPtrStart; // _10 + u64* mContentPtrEnd; // _18 + + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/plugin.h b/include/nn/nex/plugin.h new file mode 100644 index 0000000..605e38a --- /dev/null +++ b/include/nn/nex/plugin.h @@ -0,0 +1,36 @@ +/** + * @file plugin.h + * @brief Plugin interface for NEX. + */ + +#pragma once + +#include "RootObject.h" + +namespace nn +{ + namespace nex + { + class PluginObject : public nn::nex::RootObject { }; + + class Plugin : public nn::nex::RootObject + { + public: + Plugin(); + + virtual ~Plugin(); + + // there's a bunch of pure virtual methods but nothing ever inherits this class... + virtual bool Initalize(); + virtual void ThreadAttach(); + virtual void ThreadDetach(); + virtual void Destroy(); + + void SetLibrary(void *); + + void* mLibrary; // _8s + + static nn::nex::Plugin* s_pInstance; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/pseudo.h b/include/nn/nex/pseudo.h new file mode 100644 index 0000000..4dd176a --- /dev/null +++ b/include/nn/nex/pseudo.h @@ -0,0 +1,87 @@ +/** + * @file psuedo.h + * @brief Psuedo variable implementation for NEX. + */ + +#pragma once + +#include "instance.h" +#include "RootObject.h" +#include "types.h" + +namespace nn +{ + namespace nex + { + class PseudoGlobalVariableList; + + class PseudoGlobalVariableRoot : public nn::nex::RootObject + { + public: + PseudoGlobalVariableRoot(); + + virtual ~PseudoGlobalVariableRoot(); + + virtual void AllocateExtraContexts() = 0; + virtual void FreeExtraContexts() = 0; + virtual void ResetContext(u32) = 0; + virtual PseudoGlobalVariableRoot* GetNext() = 0; + virtual void SetNext(PseudoGlobalVariableRoot* pNextVariable) = 0; + + static void ResetContextForAllVariables(u32); + static void AllocateExtraContextsForAllVariables(u64); + static void FreeExtraContextsForAllVariables(); + static s64 GetNbOfExtraContexts(); + + nn::nex::PseudoGlobalVariableRoot* mNextRoot; // _8 + + static s64 s_uiNbOfExtraContexts; + static PseudoGlobalVariableList s_oList; + }; + + class PseudoGlobalVariableList : public nn::nex::RootObject + { + public: + PseudoGlobalVariableList(); + + virtual ~PseudoGlobalVariableList(); + + void AddVariable(nn::nex::PseudoGlobalVariableRoot *); + void RemoveVariable(nn::nex::PseudoGlobalVariableRoot *); + static nn::nex::PseudoGlobalVariableRoot* GetVariable(u32 idx); + static u32 FindVariableIndex(nn::nex::PseudoGlobalVariableRoot *); + void AllocateExtraContextsForAllVariables(); + void FreeExtraContextsForAllVariables(); + void ResetContextForAllVariables(u32); + static u32 GetNbOfVariables(); + + static PseudoGlobalVariableRoot* s_pVariableListHead; + static u32 m_uiNbOfVariables; + }; + + template + class PseudoGlobalVariable : public nn::nex::PseudoGlobalVariableRoot + { + public: + PseudoGlobalVariable(); + + virtual ~PseudoGlobalVariable(); + + virtual void AllocateExtraContexts(); + virtual void FreeExtraContexts(); + virtual void ResetContext(u32); + virtual PseudoGlobalVariableRoot* GetNext(); + virtual void SetNext(PseudoGlobalVariableRoot* pNextVariable); + }; + + class PseudoSingleton : public nn::nex::InstanceControl + { + public: + PseudoSingleton(u32); + + virtual ~PseudoSingleton(); + + static bool s_bUseInstantiationContext; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/reference.h b/include/nn/nex/reference.h new file mode 100644 index 0000000..9f6b6b8 --- /dev/null +++ b/include/nn/nex/reference.h @@ -0,0 +1,21 @@ +/** + * @file reference.h + * @brief Reference implementations for NEX. + */ +#pragma once + +#include "RootObject.h" + +namespace nn +{ + namespace nex + { + class RefCountedObject : public nn::nex::RootObject + { + public: + virtual ~RefCountedObject(); + + u32 _8; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/string.h b/include/nn/nex/string.h new file mode 100644 index 0000000..9f349cf --- /dev/null +++ b/include/nn/nex/string.h @@ -0,0 +1,36 @@ +/** + * @file string.h + * @brief NEX String Implementation. + */ + +#pragma once + +#include "RootObject.h" + +namespace nn +{ + namespace nex + { + class String : public nn::nex::RootObject + { + public: + bool operator<(nn::nex::String const &); + + void Truncate(u64) const; + u64 GetLength() const; + void Reserve(u64); + void SetBufferChar(char *); + void SetStringToPreReservedBuffer(char const *); + s32 GetWideCharLength() const; + void CopyString(char *, u64) const; + void CreateCopy(wchar_t **) const; + void ReleaseCopy(wchar_t *); + void ToUpper(); + void ToLower(); + void DeleteContent(); + + template + void Assign(T const *); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/system.h b/include/nn/nex/system.h new file mode 100644 index 0000000..186d17a --- /dev/null +++ b/include/nn/nex/system.h @@ -0,0 +1,70 @@ +/** + * @file system.h + * @brief System state / component interface for NEX. + */ + +#pragma once + +#include "reference.h" +#include "string.h" + +namespace nn +{ + namespace nex + { + class SystemComponent : public nn::nex::RefCountedObject + { + public: + + enum _State + { + State_Uninitialized = 1 << 0, + State_Initializing = 1 << 1, + State_Ready = 1 << 2, + State_ReadyInUse = 1 << 3, + State_Terminating = 1 << 4, + State_TerminatingWhileInUse = 1 << 5, + State_Terminated = 1 << 6, + State_Faulty = 1 << 7, + State_Unknown = 1 << 8, + State_HighestState = 1 << 8 + }; + + SystemComponent(nn::nex::String const &); + + virtual ~SystemComponent(); + + virtual char* GetType() const; + virtual bool IsAKindOf(char const *) const; + virtual void EnforceDeclareSysComponentMacro() = 0; + virtual void StateTransition(nn::nex::SystemComponent::_State); + virtual void OnInitialize(); + virtual void OnTerminate(); + virtual bool BeginInitialization(); + virtual bool EndInitialization(); + virtual bool BeginTermination(); + virtual bool EndTermination(); + virtual bool ValidTransition(nn::nex::SystemComponent::_State); + virtual bool UseIsAllowed(); + virtual nn::nex::SystemComponent::_State TestState(); + virtual void DoWork(); + + nn::nex::SystemComponent::_State Initialize(); + nn::nex::SystemComponent::_State Terminate(); + + u8 _C; + u8 _D; + u8 _E; + u8 _F; + u64 _10; + u64 _18; + u64 _20; + u32 _28; + u32 _2C; + u64 _30; + nn::nex::SystemComponent::_State mState; // _38 + u32 _3C; + u64 _40; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nex/time.h b/include/nn/nex/time.h new file mode 100644 index 0000000..3b370f7 --- /dev/null +++ b/include/nn/nex/time.h @@ -0,0 +1,54 @@ +/** + * @file time.h + * @brief NEX Time Library. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace nex + { + class TimeProvider; + + class Time + { + public: + static void Reset(); + static void RegisterTimeProvider(TimeProvider *provider); + + Time Multiply(f32) const; + Time Divide(f32) const; + Time Scale(f32) const; + + static Time ConvertTimeoutToDeadline(u32 timeout); + static u32 ConvertDeadlineToTimeout(Time deadline); + + u64 mCurTime; // _0 + + static u64* s_pfGetSessionTime; // some sort of callback? + + }; + + class SystemClock + { + public: + SystemClock(); + + virtual ~SystemClock(); + + static void RegisterTimeProvider(nn::nex::TimeProvider *, bool); + static void ApplyCorrection(Time curTime, Time newTime); + static Time ProtectedGetTime(); + static Time GetTimeImpl(bool); + static Time GetTimeImplCorrectless(); + static void Reset(); + + static nn::nex::TimeProvider* s_pTimeProvider; + static bool s_needCorrection; + static bool s_tiCorrection; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/nifm.h b/include/nn/nifm.h new file mode 100644 index 0000000..df405a3 --- /dev/null +++ b/include/nn/nifm.h @@ -0,0 +1,15 @@ +#pragma once + +#include "types.h" +#include "result.h" + +namespace nn { namespace nifm { + +Result Initialize(); +void SubmitNetworkRequest(); +void SubmitNetworkRequestAndWait(); +void CancelNetworkRequest(); +bool IsNetworkRequestOnHold(); +bool IsNetworkAvailable(); + +} } diff --git a/include/nn/nn.h b/include/nn/nn.h new file mode 100644 index 0000000..0f30e06 --- /dev/null +++ b/include/nn/nn.h @@ -0,0 +1,27 @@ +/** + * @file nn.h + * @brief Barebones NN functions, such as init and nnMain. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +int main(int argc, char **argv); +void nninitStartup(); + +void _init(); +void _fini(); +void __nnDetailNintendoSdkRuntimeObjectFileRefer(); +void __nnDetailNintendoSdkRuntimeObjectFile(); +void __nnDetailNintendoSdkNsoFileRefer(); + +void __nnmusl_init_dso_0(); +void __nnmusl_fini_dso_0(); +void __nnDetailNintendoSdkNsoFile_0(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/include/nn/oe.h b/include/nn/oe.h new file mode 100644 index 0000000..422e780 --- /dev/null +++ b/include/nn/oe.h @@ -0,0 +1,34 @@ +/** + * @file oe.h + * @brief Extenstions to OS functions. + */ + +#pragma once + +#include "settings.h" +#include "types.h" + +namespace nn +{ + namespace oe + { + typedef s32 FocusHandlingMode; + typedef s32 PerformanceMode; + + void Initialize(); + void SetPerformanceConfiguration(nn::oe::PerformanceMode, s32); + s32 GetOperationMode(); + s32 GetPerformanceMode(); + void SetResumeNotificationEnabled(bool); + void SetOperationModeChangedNotificationEnabled(bool); + void SetPerformanceModeChangedNotificationEnabled(bool); + void SetFocusHandlingMode(nn::oe::FocusHandlingMode); + bool TryPopNotificationMessage(u32 *); + s32 GetCurrentFocusState(); + void EnableGamePlayRecording(void *, u64); + bool IsUserInactivityDetectionTimeExtended(); + void SetUserInactivityDetectionTimeExtended(bool); + void FinishStartupLogo(); + nn::settings::LanguageCode GetDesiredLanguage(); + }; +}; \ No newline at end of file diff --git a/include/nn/os.h b/include/nn/os.h new file mode 100644 index 0000000..ceee980 --- /dev/null +++ b/include/nn/os.h @@ -0,0 +1,265 @@ +/** + * @file os.h + * @brief Operating System implementations. + */ + +#pragma once + +#include + +#include +#include + +namespace nn { +namespace os { +namespace detail { +struct InternalCriticalSection { + u32 Image; +}; + +struct InternalConditionVariable { + u32 Image; +}; +} // namespace detail + +typedef u64 Tick; + +struct LightEventType { + std::aligned_storage_t<0xc, 4> storage; +}; + +// https://github.com/misson20000/nn-types/blob/master/nn_os.h +struct EventType { + nn::os::EventType* _x0; + nn::os::EventType* _x8; + bool isSignaled; + bool initiallySignaled; + bool shouldAutoClear; + bool isInit; + u32 signalCounter; + u32 signalCounter2; + nn::os::detail::InternalCriticalSection crit; + nn::os::detail::InternalConditionVariable condvar; +}; +typedef EventType Event; + +// https://github.com/misson20000/nn-types/blob/master/nn_os.h +struct ThreadType { + u64 field_8; + u64 field_10; + u64 field_18; + char field_20[32]; + uint32_t thread_status; + char field_41; + u16 field_42; + uint32_t thread_prio_shift; + uint64_t thread_stack_base_addr; + uint64_t thread_stack_base_addr_mirror; + uint64_t thread_stack_size; + uint64_t thread_param; + uint64_t thread_func; + u64 field_70; + u64 field_78; + u64 field_80; + char field_88[0x100]; + char thread_name[0x20]; + const char* thread_name_addr; + nn::os::detail::InternalCriticalSection crit; + nn::os::detail::InternalConditionVariable condvar; + u32 thread_handle; +}; +#ifdef SWITCH +static_assert(sizeof(ThreadType) == 0x1C0, "Wrong size"); +#endif + +struct MessageQueueType { + u64 _x0; + u64 _x8; + u64 _x10; + u64 _x18; + void* Buffer; + u32 MaxCount; + u32 Count; + u32 Offset; + bool Initialized; + detail::InternalCriticalSection _x38; + detail::InternalConditionVariable _x3C; + detail::InternalConditionVariable _x40; +}; + +struct ConditionVariableType {}; + +struct SemaphoreType { + std::aligned_storage_t<0x28, 8> storage; +}; + +struct SystemEvent; +struct SystemEventType; + +// ARG +void SetHostArgc(s32); +s32 GetHostArgc(); +void SetHostArgv(char**); +char** GetHostArgv(); + +// MEMORY +void InitializeVirtualAddressMemory(); +Result AllocateAddressRegion(u64*, u64); +Result AllocateMemory(u64*, u64); +Result AllocateMemoryPages(u64, u64); +void AllocateMemoryBlock(u64*, u64); +void FreeMemoryBlock(u64, u64); +void SetMemoryHeapSize(u64); + +// MUTEX +struct MutexType { + u8 curState; // _0 + bool isRecursiveMutex; // _1 + s32 lockLevel; // _2 + u8 _6[0x20 - 0xE]; +}; + +void InitializeMutex(nn::os::MutexType*, bool, s32); +void FinalizeMutex(nn::os::MutexType*); +void LockMutex(nn::os::MutexType*); +bool TryLockMutex(nn::os::MutexType*); +void UnlockMutex(nn::os::MutexType*); +bool IsMutexLockedByCurrentThread(nn::os::MutexType const*); + +// QUEUE +void InitializeMessageQueue(nn::os::MessageQueueType*, u64* buf, u64 queueCount); +void FinalizeMessageQueue(nn::os::MessageQueueType*); + +bool TrySendMessageQueue(MessageQueueType*, u64); +void SendMessageQueue(MessageQueueType*, u64); +bool TimedSendMessageQueue(MessageQueueType*, u64, nn::TimeSpan); + +bool TryReceiveMessageQueue(u64* out, MessageQueueType*); +void ReceiveMessageQueue(u64* out, MessageQueueType*); +bool TimedReceiveMessageQueue(u64* out, MessageQueueType*, nn::TimeSpan); + +bool TryPeekMessageQueue(u64*, MessageQueueType const*); +void PeekMessageQueue(u64*, MessageQueueType const*); +bool TimedPeekMessageQueue(u64*, MessageQueueType const*); + +bool TryJamMessageQueue(nn::os::MessageQueueType*, u64); +void JamMessageQueue(nn::os::MessageQueueType*, u64); +bool TimedJamMessageQueue(nn::os::MessageQueueType*, u64, nn::TimeSpan); + +// CONDITION VARIABLE +void InitializeConditionVariable(ConditionVariableType*); +void FinalizeConditionVariable(ConditionVariableType*); + +void SignalConditionVariable(ConditionVariableType*); +void BroadcastConditionVariable(ConditionVariableType*); +void WaitConditionVariable(ConditionVariableType*); +u8 TimedWaitConditionVariable(ConditionVariableType*, MutexType*, nn::TimeSpan); + +// THREAD +Result CreateThread(nn::os::ThreadType*, void (*)(void*), void* arg, void* srcStack, u64 stackSize, + s32 priority, s32 coreNum); +Result CreateThread(nn::os::ThreadType*, void (*)(void*), void* arg, void* srcStack, u64 stackSize, + s32 priority); +void DestroyThread(nn::os::ThreadType*); +void StartThread(nn::os::ThreadType*); +void SetThreadName(nn::os::ThreadType*, char const* threadName); +void SetThreadNamePointer(nn::os::ThreadType*, char const*); +char* GetThreadNamePointer(nn::os::ThreadType const*); +nn::os::ThreadType* GetCurrentThread(); +void GetCurrentStackInfo(uintptr_t* stack_addr, size_t* stack_size); +s32 ChangeThreadPriority(nn::os::ThreadType* thread, s32 priority); +s32 GetThreadPriority(nn::os::ThreadType const* thread); +u64 GetThreadId(const nn::os::ThreadType* thread); +void YieldThread(); +void SuspendThread(nn::os::ThreadType*); +void ResumeThread(nn::os::ThreadType*); +void SleepThread(nn::TimeSpan); +void WaitThread(nn::os::ThreadType*); +void SetThreadCoreMask(nn::os::ThreadType*, int, u64 mask); + +// EVENTS +void InitializeEvent(EventType*, bool initiallySignaled, bool autoclear); +void FinalizeEvent(EventType*); +void SignalEvent(EventType*); +void WaitEvent(EventType*); +bool TryWaitEvent(EventType*); +bool TimedWaitEvent(EventType*, nn::TimeSpan); +void ClearEvent(EventType*); + +// LIGHT EVENTS +void InitializeLightEvent(LightEventType*, bool initiallySignaled, bool autoclear); +void FinalizeLightEvent(LightEventType*); +void SignalLightEvent(LightEventType*); +void WaitLightEvent(LightEventType*); +bool TimedWaitLightEvent(LightEventType*, nn::TimeSpan); +void ClearLightEvent(LightEventType*); + +TimeSpan ConvertToTimeSpan(Tick ticks); + +// SEMAPHORES +void InitializeSemaphore(SemaphoreType* semaphore, s32 initial_count, s32 max_count); +void FinalizeSemaphore(SemaphoreType* semaphore); +void AcquireSemaphore(SemaphoreType* semaphore); +bool TryAcquireSemaphore(SemaphoreType* semaphore); +void ReleaseSemaphore(SemaphoreType* semaphore); + +// EXCEPTION HANDLING +typedef union { + u64 x; ///< 64-bit AArch64 register view. + u32 w; ///< 32-bit AArch64 register view. + u32 r; ///< AArch32 register view. +} CpuRegister; +/// Armv8 NEON register. + +typedef union { + u128 v; ///< 128-bit vector view. + double d; ///< 64-bit double-precision view. + float s; ///< 32-bit single-precision view. +} FpuRegister; + +struct UserExceptionInfo { + u32 ErrorDescription; ///< See \ref ThreadExceptionDesc. + u32 pad[3]; + + CpuRegister CpuRegisters[29]; ///< GPRs 0..28. Note: also contains AArch32 registers. + CpuRegister FP; ///< Frame pointer. + CpuRegister LR; ///< Link register. + CpuRegister SP; ///< Stack pointer. + CpuRegister PC; ///< Program counter (elr_el1). + + u64 padding; + + FpuRegister FpuRegisters[32]; ///< 32 general-purpose NEON registers. + + u32 PState; ///< pstate & 0xFF0FFE20 + u32 AFSR0; + u32 AFSR1; + u32 ESR; + + CpuRegister FAR; ///< Fault Address Register. +}; +void SetUserExceptionHandler(void (*)(UserExceptionInfo*), void*, ulong, UserExceptionInfo*); + +// OTHER +void GenerateRandomBytes(void*, u64); +nn::os::Tick GetSystemTick(); +nn::os::Tick GetSystemTickFrequency(); +u64 GetThreadAvailableCoreMask(); +void SetMemoryHeapSize(u64 size); + +// Thread-local storage +struct TlsSlot { + u32 slot; +}; +Result AllocateTlsSlot(TlsSlot* slot_out, void (*)(u64)); +void FreeTlsSlot(TlsSlot slot); +u64 GetTlsValue(TlsSlot slot); +void SetTlsValue(TlsSlot slot, u64 value); +u32 GetCurrentCoreNumber(); + +namespace detail { +extern s32 g_CommandLineParameter; +extern char** g_CommandLineParameterArgv; +}; // namespace detail +}; // namespace os +}; // namespace nn diff --git a/include/nn/result.h b/include/nn/result.h new file mode 100644 index 0000000..b454815 --- /dev/null +++ b/include/nn/result.h @@ -0,0 +1,24 @@ +#pragma once + +#include "types.h" + +namespace nn { + +struct Result +{ + Result(u32 value = 0) : value(value) { } + + inline bool isSuccess() + { + return value == 0; + } + + inline bool isFailure() + { + return !isSuccess(); + } + + u32 value; +}; + +} diff --git a/include/nn/settings.h b/include/nn/settings.h new file mode 100644 index 0000000..09e7d31 --- /dev/null +++ b/include/nn/settings.h @@ -0,0 +1,12 @@ +#pragma once + +namespace nn { namespace settings { + +struct LanguageCode +{ + char data[8]; +}; + +void GetLanguageCode(LanguageCode* out); + +} } diff --git a/include/nn/socket.h b/include/nn/socket.h new file mode 100644 index 0000000..2fc4b6d --- /dev/null +++ b/include/nn/socket.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../types.h" + + +struct in_addr +{ + u32 data; // 0 +}; + +struct sockaddr +{ + u8 _0; // 0 + u8 family; // 1 + u16 port; // 2 + in_addr address; // 4 + u8 _8[8]; // 8 +}; + + +namespace nn { namespace socket { + +Result Initialize(void* pool, ulong poolSize, ulong allocPoolSize, int concurLimit); + +s32 SetSockOpt(s32 socket, s32 socketLevel, s32 option, void const*, u32 len); + +s32 Socket(s32 domain, s32 type, s32 protocol); +s32 Connect(s32 socket, const sockaddr* address, u32 addressLen); +Result Close(s32 socket); + +s32 Send(s32 socket, const void* data, ulong dataLen, s32 flags); +s32 Recv(s32 socket, void* out, ulong outLen, s32 flags); + +u16 InetHtons(u16 val); +s32 InetAton(const char* addressStr, in_addr* addressOut); + +} } diff --git a/include/nn/ssl.h b/include/nn/ssl.h new file mode 100644 index 0000000..f116eed --- /dev/null +++ b/include/nn/ssl.h @@ -0,0 +1,38 @@ +/** + * @file ssl.h + * @brief SSL implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace ssl + { + enum CertificateFormat + { + PEM = 0x01, + DER = 0x02 + }; + + class Context + { + public: + enum SslVersion + { + Auto = 0x01, + v10 = 0x08, + v11 = 0x10, + v12 = 0x20 + }; + + Result Create(nn::ssl::Context::SslVersion version); + Result ImportServerPki(u64 *, char const *certData, u32 certSize, nn::ssl::CertificateFormat certFormat); + }; + + Result Initialize(); + Result Finalize(); + }; +}; \ No newline at end of file diff --git a/include/nn/swkbd/swkbd.h b/include/nn/swkbd/swkbd.h new file mode 100644 index 0000000..fb822f5 --- /dev/null +++ b/include/nn/swkbd/swkbd.h @@ -0,0 +1,217 @@ +#pragma once + +#include +#include +typedef unsigned long int ulong; +typedef unsigned short int ushort; +typedef unsigned int uint; +typedef unsigned char uchar; + +namespace nn +{ + namespace swkbd + { + enum Preset + { + Default, + Password, + UserName, + DownloadCode, + Max_Preset + }; + + enum KeyboardMode + { + ModeLanguageSet1, + ModeNumeric, + ModeASCII, + ModeLanguageSet1Latin, + ModeAlphabet, + ModeSimplifiedChinese, + ModeTraditionalChinese, + ModeKorean, + ModeLanguageSet2, + ModeMax_KeyboardMode, + ModeFull, + ModeFullLatin + }; + + enum InvalidChar + { + Space = 1 << 1, + AtMark = 1 << 2, + Percent = 1 << 3, + Slash = 1 << 4, + BackSlash = 1 << 5, + Numeric = 1 << 6, + OutsideOfDownloadCode = 1 << 7, + OutsideOfMiiNickName = 1 << 8, + Force32 = 1 << 9 + }; + + enum PasswordMode + { + Show, + Hide, + Max_PasswordMode + }; + + enum InputFormMode + { + OneLine, + MultiLine, + Separate, + Max_InputFormMode + }; + + enum InitialCursorPos + { + First, + Last, + Max_InitialCursorPos + }; + + enum TextCheckResult + { + Success, + ShowFailureDialog, + ShowConfirmDialog, + Max_TextCheckResult + }; + + enum DictionaryLang + { + Japanese, + AmericanEnglish, + CanadianFrench, + LatinAmericanSpanish, + Reserved1, + BritishEnglish, + French, + German, + Spanish, + Italian, + Dutch, + Portuguese, + Russian, + Reserved2, + SimplifiedChinesePinyin, + TraditionalChineseCangjie, + TraditionalChineseSimplifiedCangjie, + TraditionalChineseZhuyin, + Korean, + Max_DictionaryLang + }; + + struct DictionaryInfo + { + uint offset; // 0x0 + ushort size; // 0x4 + DictionaryLang lang; // 0x6 + }; + + // KeyboardMode keyboardMode; // 0x0 + // const char okText[0x8]; // 0x8 + // char leftOptionalSymbolKey; // 0x10 + // char rightOptionalSymbolKey; // 0x12 + // bool isPredictionEnabled; // 0x14 + // InvalidChar invalidCharFlag; // 0x18 + // InitialCursorPos initialCursorPos; // 0x1C + // const char headerText[0x40]; // 0x20 + // const char subText[0x80]; // 0x28 + // const char guideText[0x100]; // 0x30 + // int textMaxLength; // 0x38 + // int textMinLength; // 0x3C + // PasswordMode passwordMode; // 0x40 + // InputFormMode inputFormMode; // 0x44 + // bool isUseNewLine; // 0x48 + // bool isUseUtf8; // 0x49 + // bool isUseBlurBackground; // 0x4A + // int _initialStringOffset; // 0x4C + // int _initialStringLength; // 0x50 + // int _userDictionaryOffset; // 0x54 + // int _userDictionaryNum; // 0x58 + // bool _isUseTextCheck; // 0x5C + // void *_textCheckCallback; // 0x60 + // int* separateTextPos; // 0x68 + // DictionaryInfo* _customizedDicInfoList; // 0x70 + // unsigned char _customizedDicCount; // 0x78 + // unsigned char* _reserved; // 0x80 + + struct KeyboardConfig + { + + KeyboardMode keyboardMode; + char okText[0x12]; + char16_t leftOptionalSymbolKey; + char16_t rightOptionalSymbolKey; + bool isPredictionEnabled; + InvalidChar invalidCharFlag; + InitialCursorPos initialCursorPos; + char headerText[0x82]; + char subText[0x102]; + char guideText[0x202]; + int textMaxLength; + int textMinLength; + PasswordMode passwordMode; + InputFormMode inputFormMode; + bool isUseNewLine; + bool isUseUtf8; + bool isUseBlurBackground; + int _initialStringOffset; + int _initialStringLength; + int _userDictionaryOffset; + int _userDictionaryNum; + bool _isUseTextCheck; + void *_textCheckCallback; + int separateTextPos[0x8]; + }; + + struct ShowKeyboardArg + { + KeyboardConfig keyboardConfig; // 0x0 + const char* workBuf; // 0x400 + long workBufSize; // 0x408 + const char* textCheckWorkBuf; // 0x98 + long textCheckWorkBufSize; // 0xA0 + const char* _customizeDicBuf; // 0xA8 + long _customizeDicBufSize; // 0xB0 + }; + + class String { + public: + String(int size) { + bufsize = size; + strBuf = (char *)malloc(bufsize); + } + + const char *cstr() { return strBuf; } + private: + char *strBuf; + int bufsize; + }; + + ulong GetRequiredWorkBufferSize(bool); + ulong GetRequiredStringBufferSize(void); + void MakePreset(nn::swkbd::KeyboardConfig *,nn::swkbd::Preset); + //void SetHeaderText(nn::swkbd::KeyboardConfig *,char16_t const*); + //void SetSubText(nn::swkbd::KeyboardConfig*, char16_t const*); + void SetOkText(nn::swkbd::KeyboardConfig *,char16_t const*); + void SetOkTextUtf8(nn::swkbd::KeyboardConfig *,char const*); + void SetLeftOptionalSymbolKey(nn::swkbd::KeyboardConfig *,char16_t); + void SetLeftOptionalSymbolKeyUtf8(nn::swkbd::KeyboardConfig *,char const*); + void SetRightOptionalSymbolKey(nn::swkbd::KeyboardConfig *,char16_t); + void SetRightOptionalSymbolKeyUtf8(nn::swkbd::KeyboardConfig *,char const*); + void SetHeaderText(nn::swkbd::KeyboardConfig *,char16_t const*); + void SetHeaderTextUtf8(nn::swkbd::KeyboardConfig *,char const*); + void SetSubText(nn::swkbd::KeyboardConfig *,char16_t const*); + void SetSubTextUtf8(nn::swkbd::KeyboardConfig *,char const*); + void SetGuideText(nn::swkbd::KeyboardConfig *,char16_t const*); + void SetGuideTextUtf8(nn::swkbd::KeyboardConfig *,char const*); + void SetInitialText(nn::swkbd::ShowKeyboardArg *,char16_t const*); + void SetInitialTextUtf8(nn::swkbd::ShowKeyboardArg *,char const*); + //void SetUserWordList(nn::swkbd::ShowKeyboardArg *,nn::swkbd::UserWord const*,int); + void ShowKeyboard(nn::swkbd::String *,nn::swkbd::ShowKeyboardArg const&); + + } // namespace swkbd +} // namespace nn diff --git a/include/nn/system_settings.ini b/include/nn/system_settings.ini new file mode 100644 index 0000000..600d03c --- /dev/null +++ b/include/nn/system_settings.ini @@ -0,0 +1,53 @@ +; Disable uploading error reports to Nintendo +[eupld] +; upload_enabled = u8!0x0 +; Control whether RO should ease its validation of NROs. +; (note: this is normally not necessary, and ips patches can be used.) +[ro] +; ease_nro_restriction = u8!0x1 +; Atmosphere custom settings +[atmosphere] +; Reboot from fatal automatically after some number of milliseconds. +; If field is not present or 0, fatal will wait indefinitely for user input. +; fatal_auto_reboot_interval = u64!0x0 +; Make the power menu's "reboot" button reboot to payload. +; Set to "normal" for normal reboot, "rcm" for rcm reboot. +; power_menu_reboot_function = str!payload +; Controls whether dmnt cheats should be toggled on or off by +; default. 1 = toggled on by default, 0 = toggled off by default. +; dmnt_cheats_enabled_by_default = u8!0x1 +; Controls whether dmnt should always save cheat toggle state +; for restoration on new game launch. 1 = always save toggles, +; 0 = only save toggles if toggle file exists. +; dmnt_always_save_cheat_toggles = u8!0x0 +; Enable writing to BIS partitions for HBL. +; This is probably undesirable for normal usage. +; enable_hbl_bis_write = u8!0x0 +; Enable reading the CAL0 partition for HBL. +; This is probably undesirable for normal usage. +; enable_hbl_cal_read = u8!0x0 +; Controls whether fs.mitm should redirect save files +; to directories on the sd card. +; 0 = Do not redirect, 1 = Redirect. +; NOTE: EXPERIMENTAL +; If you do not know what you are doing, do not touch this yet. +; fsmitm_redirect_saves_to_sd = u8!0x0 +; Controls whether to enable the deprecated hid mitm +; to fix compatibility with old homebrew. +; 0 = Do not enable, 1 = Enable. +; Please note this setting may be removed in a +; future release of Atmosphere. +; enable_deprecated_hid_mitm = u8!0x0 +; Controls whether am sees system settings "DebugModeFlag" as +; enabled or disabled. +; 0 = Disabled (not debug mode), 1 = Enabled (debug mode) +; enable_am_debug_mode = u8!0x0 +[hbloader] +; Controls the size of the homebrew heap when running as applet. +; If set to zero, all available applet memory is used as heap. +; The default is zero. +; applet_heap_size = u64!0x0 +; Controls the amount of memory to reserve when running as applet +; for usage by other applets. This setting has no effect if +; applet_heap_size is non-zero. The default is 0x8600000. +; applet_heap_reservation_size = u64!0x8600000 \ No newline at end of file diff --git a/include/nn/time.h b/include/nn/time.h new file mode 100644 index 0000000..4a731d5 --- /dev/null +++ b/include/nn/time.h @@ -0,0 +1,82 @@ +/** + * @file time.h + * @brief Time implementation. + */ + +#pragma once + +#include + +namespace nn { +class TimeSpan { +public: + u64 nanoseconds; + + static TimeSpan FromNanoSeconds(u64 nanoSeconds) { + TimeSpan ret; + ret.nanoseconds = nanoSeconds; + return ret; + } + + static TimeSpan FromSeconds(u64 seconds) { + return FromNanoSeconds(seconds * 1000 * 1000 * 1000); + } + static TimeSpan FromMinutes(u64 minutes) { + return FromNanoSeconds(minutes * 1000 * 1000 * 1000 * 60); + } + static TimeSpan FromHours(u64 hours) { + return FromNanoSeconds(hours * 1000 * 1000 * 1000 * 60 * 60); + } + static TimeSpan FromDays(u64 days) { + return FromNanoSeconds(days * 1000 * 1000 * 1000 * 60 * 60 * 24); + } +}; + +namespace time { + +Result Initialize(); +bool IsInitialized(); + +struct CalendarTime { + s16 year; + s8 month; + s8 day; + s8 hour; + s8 minute; + s8 second; +}; + +enum DayOfTheWeek { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }; + +struct TimeZone { + char standardTimeName[0x8]; + bool _9; // daylight savings or something? + s32 utcOffset; // in seconds +}; + +struct CalendarAdditionalInfo { + nn::time::DayOfTheWeek dayOfTheWeek; + s32 dayofYear; + nn::time::TimeZone timeZone; +}; + +struct PosixTime { + u64 time; +}; + +class StandardUserSystemClock { +public: + static Result GetCurrentTime(nn::time::PosixTime*); +}; + +struct TimeZoneRule; // shrug + +Result ToCalendarTime(nn::time::CalendarTime*, nn::time::CalendarAdditionalInfo*, + nn::time::PosixTime const&); +Result ToCalendarTime(nn::time::CalendarTime*, nn::time::CalendarAdditionalInfo*, + nn::time::PosixTime const&, nn::time::TimeZoneRule const&); +Result ToPosixTime(int*, PosixTime*, int, const CalendarTime&); +CalendarTime ToCalendarTimeInUtc(const PosixTime&); +PosixTime ToPosixTimeFromUtc(const CalendarTime&); +}; // namespace time +}; // namespace nn diff --git a/include/nn/ui2d/Layout.h b/include/nn/ui2d/Layout.h new file mode 100644 index 0000000..ab0766f --- /dev/null +++ b/include/nn/ui2d/Layout.h @@ -0,0 +1,55 @@ +/** + * @file Layout.h + * @brief UI Layout implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace ui2d + { + class AnimTransform; + class Pane; + + class Layout + { + public: + Layout(); + + virtual ~Layout(); + + virtual void DeleteAnimTransform(nn::ui2d::AnimTransform *); + virtual void BindAnimation(nn::ui2d::AnimTransform *); + virtual void UnbindAnimation(nn::ui2d::AnimTransform *); + virtual void UnbindAnimation(nn::ui2d::Pane *); + virtual void UnbindAllAnimation(); + + virtual void Animate(); + virtual void UpdateAnimFrame(f32 frame); + virtual void AnimateAndUpdateAnimFrame(f32 frame); + + static void SetAllocator(void* (*)(u64, u64, void *), void (*)(void *, void *), void *); + static void AllocateMemory(u64, u64); + static void AllocateMemory(u64); + void FreeMemory(void *src); + + u64 _10; + u64 _18; + u64 _20; + u64 _28; + u64 _30; + + u64 _40; + u64 _48; + u64 _50; + u64 _58; + u64 _60; + + static void* g_pAllocateFunction; + static void* g_pFreeFunction; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/ui2d/Material.h b/include/nn/ui2d/Material.h new file mode 100644 index 0000000..2faadf1 --- /dev/null +++ b/include/nn/ui2d/Material.h @@ -0,0 +1,32 @@ +/** + * @file Material.h + * @brief UI Material implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace ui2d + { + class AnimTransform; + class BuildResultInformation; + struct UserShaderInformation; + + class Material + { + public: + Material(); + + void Initialize(); + void ReserveMem(s32, s32, s32, s32, bool, s32, bool, s32, bool, bool); + void SetupUserShaderConstantBufferInformation(nn::ui2d::UserShaderInformation const &); + + virtual ~Material(); + virtual void BindAnimation(nn::ui2d::AnimTransform *); + virtual void UnbindAnimation(nn::ui2d::AnimTransform *); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/ui2d/Pane.h b/include/nn/ui2d/Pane.h new file mode 100644 index 0000000..3c287f6 --- /dev/null +++ b/include/nn/ui2d/Pane.h @@ -0,0 +1,83 @@ +/** + * @file Pane.h + * @brief Base UI panel. + */ + +#pragma once + +#include "types.h" +#include "sead/runtime.h" + +namespace nn +{ + namespace ui2d + { + class AnimTransform; + class Layout; + + class Pane + { + public: + Pane(); + Pane(nn::ui2d::Pane const &); + + virtual ~Pane(); + + virtual sead::RuntimeTypeInfo::Interface* GetRuntimeTypeInfo() const; + virtual s32 GetVertexColor(s32); + virtual u8 GetColorElement(s32); + virtual void SetColorElement(u32, u8); + virtual u8 GetVertexColorElement(s32); + virtual void SetVertexColorElement(u32, u8); + virtual u32 GetMaterialCount() const; + virtual u64* GetMaterial(s32) const; + + virtual void BindAnimation(nn::ui2d::AnimTransform *, bool, bool); + virtual void UnbindAnimation(nn::ui2d::AnimTransform *, bool); + + void Initialize(); + void SetName(char const *); + void SetUserData(char const *); + void AppendChild(nn::ui2d::Pane *); + void PrependChild(nn::ui2d::Pane *); + void InsertChild(nn::ui2d::Pane *, nn::ui2d::Pane *); + void RemoveChild(nn::ui2d::Pane *); + void GetVertexPos() const; + + nn::ui2d::Pane* mParent; // _8 + u64 _10; + u64 _18; + u64 _20; + u64 _28; + u32 mPositionX; // _30 + u32 mPositionY; // _34 + u32 mPositionZ; // _38 + u32 mRotationX; // _3C + u32 mRotationY; // _40 + u32 mRotationZ; // _44 + u32 mScaleX; // _48 + u32 mScaleY; // _4C + u32 mSizeX; // _50 + u32 mSizeY; // _54 + u8 mFlags; // _58 (pane + 0x8) + u8 mAlpha; // _59 + u8 mAlphaInfluence; // _5A + u8 mOriginFlags; // _5B + u32 _5C; + u64 _60; + u64 _68; + u128 _70; + u128 _80; + u128 _90; + u64 _A0; + u64 _A8; + void* mAnimExtUserData; // _B0 + char[0x18] mPanelName; // _B8 + u8 _D0; + char [8] mUserData; // _D1 + u8 _D9; + u16 _DA; + u32 _DC; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/ui2d/Parts.h b/include/nn/ui2d/Parts.h new file mode 100644 index 0000000..c221e3b --- /dev/null +++ b/include/nn/ui2d/Parts.h @@ -0,0 +1,32 @@ +/** + * @file Parts.h + * @brief Layout parts. + */ + +#pragma once + +#include "Pane.h" + +namespace nn +{ + namespace ui2d + { + struct BuildArgSet; + struct ResParts; + + class Parts : nn::ui2d::Pane + { + public: + Parts(); + Parts(nn::ui2d::ResParts const *, nn::ui2d::ResParts const *, nn::ui2d::BuildArgSet const &); + Parts(nn::ui2d::Parts const &); + + virtual ~Parts(); + virtual sead::RuntimeTypeInfo::Interface* GetRuntimeTypeInfo() const; + + u64 _E0; + u64 _E8; + u32 _F0; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/ui2d/Texture.h b/include/nn/ui2d/Texture.h new file mode 100644 index 0000000..392de3d --- /dev/null +++ b/include/nn/ui2d/Texture.h @@ -0,0 +1,8 @@ +#pragma once + +namespace nn { + namespace ui2d { + class TextureInfo; + class TextureData; + } +} \ No newline at end of file diff --git a/include/nn/ui2d/detail/TexCoordArray.h b/include/nn/ui2d/detail/TexCoordArray.h new file mode 100644 index 0000000..ca438c3 --- /dev/null +++ b/include/nn/ui2d/detail/TexCoordArray.h @@ -0,0 +1,38 @@ +/** + * @file TexCoordArray.h + * @brief Texture coordinate array implementation. + */ + +#pragma once + +#include "types.h" +#include "../util/Float2.h" + +namespace nn +{ + namespace ui2d + { + class Layout; + + namespace detail + { + class TexCoordArray + { + public: + void Initialize(); + void Free(); + void Reserve(s32); + void SetSize(s32 size); + void GetCoord(nn::util::Float2 *, s32) const; + void SetCoord(s32, nn::util::Float2 const *); + void Copy(void const *, s32); + bool CompareCopiedInstanceTest(nn::ui2d::detail::TexCoordArray const &) const; + + u16 _0; + u16 _2; + u32 _4; // padding? + nn::ui2d::Layout* mLayout; // _8 + }; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/util.h b/include/nn/util.h new file mode 100644 index 0000000..9f88a91 --- /dev/null +++ b/include/nn/util.h @@ -0,0 +1,10 @@ +#pragma once + +#include "../types.h" + +namespace nn { namespace util { + +s32 SNPrintf(char* s, ulong n, const char* format, ...); +s32 VSNPrintf(char* s, ulong n, const char* format, va_list arg); + +} } diff --git a/include/nn/util/Float2.h b/include/nn/util/Float2.h new file mode 100644 index 0000000..170edfe --- /dev/null +++ b/include/nn/util/Float2.h @@ -0,0 +1,22 @@ +/** + * @file Float2.h + * @brief Some odd float implementation that I don't understand yet... + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace util + { + struct Float2 + { + u64 _0; + u64 _8; + u64 _10; + u64 _18; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/util/util_AccessorBase.h b/include/nn/util/util_AccessorBase.h new file mode 100644 index 0000000..350b720 --- /dev/null +++ b/include/nn/util/util_AccessorBase.h @@ -0,0 +1,17 @@ +#pragma once + +namespace nn +{ + namespace util + { + template + class AccessorBase : protected T + { + protected: + AccessorBase(); + + public: + typedef T value_type; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/vfx/Config.h b/include/nn/vfx/Config.h new file mode 100644 index 0000000..09e916d --- /dev/null +++ b/include/nn/vfx/Config.h @@ -0,0 +1,18 @@ +/** + * @file Config.h + * @brief VFX configuration. + */ + +#pragma once + +namespace nn +{ + namespace vfx + { + class Config + { + public: + virtual ~Config(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/vfx/Heap.h b/include/nn/vfx/Heap.h new file mode 100644 index 0000000..52ccb6f --- /dev/null +++ b/include/nn/vfx/Heap.h @@ -0,0 +1,20 @@ +/** + * @file Heap.h + * @brief VFX heap implementation. + */ + +#pragma once + +#include "types.h" + +namespace nn +{ + namespace vfx + { + class Heap + { + public: + virtual ~Heap(); + }; + }; +}; \ No newline at end of file diff --git a/include/nn/vfx/System.h b/include/nn/vfx/System.h new file mode 100644 index 0000000..7f73533 --- /dev/null +++ b/include/nn/vfx/System.h @@ -0,0 +1,27 @@ +/** + * @file System.h + * @brief VFX system implementation. + */ + +#pragma once + +#include "Config.h" +#include "Heap.h" + +// this class is massive +namespace nn +{ + namespace vfx + { + class System + { + public: + System(nn::vfx::Config const &); + + virtual ~System(); + virtual void Initialize(nn::vfx::Heap *,nn::vfx::Heap *, nn::vfx::Config const &); + + u8 _0[0x1700]; + }; + }; +}; \ No newline at end of file diff --git a/include/nn/vi.h b/include/nn/vi.h new file mode 100644 index 0000000..6a3c57a --- /dev/null +++ b/include/nn/vi.h @@ -0,0 +1,34 @@ +/** + * @file vi.h + * @brief Visual interface implementation. + */ + +#pragma once + +#include "os.h" +#include "types.h" + +namespace nn +{ + namespace vi + { + class Display; + class Layer; + + enum ScalingMode + { + None, + Exact, + FitLayer, + ScaleAndCrop, + PreserveAspectRatio + }; + + void Initialize(); + Result OpenDefaultDisplay(nn::vi::Display **out_Disp); + Result CreateLayer(nn::vi::Layer *out_Layer*, nn::vi::Display *disp); + Result SetLayerScalingMode(nn::vi::Layer *layer, nn::vi::ScalingMode scalingMode); + Result GetDisplayVsyncEvent(nn::os::SystemEventType *, nn::vi::Display *); + Result GetNativeWindow(void **window, nn::vi::Layer *); + }; +}; \ No newline at end of file diff --git a/include/packets/CaptureInf.h b/include/packets/CaptureInf.h new file mode 100644 index 0000000..dc40f23 --- /dev/null +++ b/include/packets/CaptureInf.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Packet.h" + +struct CaptureInf : Packet { + CaptureInf() : Packet() { + this->mType = PacketType::CAPTUREINF; + mPacketSize = sizeof(CaptureInf) - sizeof(Packet); + }; + + char hackName[0x20] = {}; + +}; \ No newline at end of file diff --git a/include/packets/ChangeStagePacket.h b/include/packets/ChangeStagePacket.h new file mode 100644 index 0000000..69d0f29 --- /dev/null +++ b/include/packets/ChangeStagePacket.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Packet.h" + +struct ChangeStagePacket : Packet { + ChangeStagePacket() : Packet() { + this->mType = PacketType::CHANGESTAGE; + mPacketSize = sizeof(ChangeStagePacket) - sizeof(Packet); + }; + char changeStage[0x30] = {}; + char changeID[0x10] = {}; + s8 scenarioNo = -1; + u8 subScenarioType = -1; +}; \ No newline at end of file diff --git a/include/packets/CostumeInf.h b/include/packets/CostumeInf.h new file mode 100644 index 0000000..bba2277 --- /dev/null +++ b/include/packets/CostumeInf.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Packet.h" + +struct CostumeInf : Packet { + CostumeInf() : Packet() {this->mType = PacketType::COSTUMEINF; mPacketSize = sizeof(CostumeInf) - sizeof(Packet);}; + CostumeInf(const char* body, const char* cap) : Packet() { + this->mType = PacketType::COSTUMEINF; + mPacketSize = sizeof(CostumeInf) - sizeof(Packet); + strcpy(bodyModel, body); + strcpy(capModel, cap); + } + char bodyModel[COSTUMEBUFSIZE] = {}; + char capModel[COSTUMEBUFSIZE] = {}; +}; \ No newline at end of file diff --git a/include/packets/GameInf.h b/include/packets/GameInf.h new file mode 100644 index 0000000..7b82954 --- /dev/null +++ b/include/packets/GameInf.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Packet.h" +#include "al/util.hpp" + +struct GameInf : Packet { + GameInf() : Packet() {this->mType = PacketType::GAMEINF; mPacketSize = sizeof(GameInf) - sizeof(Packet);}; + bool is2D = false; + u8 scenarioNo = -1; + char stageName[0x40] = {}; + + bool operator==(const GameInf &rhs) const { + return ( + is2D == rhs.is2D && + scenarioNo == rhs.scenarioNo && + al::isEqualString(stageName, rhs.stageName) + ); + } + + bool operator!=(const GameInf& rhs) const { return !operator==(rhs); } + +}; \ No newline at end of file diff --git a/include/packets/HackCapInf.h b/include/packets/HackCapInf.h new file mode 100644 index 0000000..7434bd5 --- /dev/null +++ b/include/packets/HackCapInf.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Packet.h" + +struct HackCapInf : Packet { + HackCapInf() : Packet() {this->mType = PacketType::HACKCAPINF; mPacketSize = sizeof(HackCapInf) - sizeof(Packet);}; + sead::Vector3f capPos; + sead::Quatf capQuat; + bool isCapVisible = false; + char capAnim[PACKBUFSIZE] = {}; +}; \ No newline at end of file diff --git a/include/packets/InitPacket.h b/include/packets/InitPacket.h new file mode 100644 index 0000000..002b93d --- /dev/null +++ b/include/packets/InitPacket.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Packet.h" + +struct InitPacket : Packet { + InitPacket() : Packet() {this->mType = PacketType::CLIENTINIT; mPacketSize = sizeof(InitPacket) - sizeof(Packet);}; + u16 maxPlayers = 0; +}; \ No newline at end of file diff --git a/include/packets/Packet.h b/include/packets/Packet.h new file mode 100644 index 0000000..9fe183c --- /dev/null +++ b/include/packets/Packet.h @@ -0,0 +1,81 @@ +#pragma once + +#include "sead/math/seadVector.h" +#include "sead/math/seadQuat.h" + +#include "nn/account.h" + +#define PACKBUFSIZE 0x30 +#define COSTUMEBUFSIZE 0x20 + +#define MAXPACKSIZE 0x100 + +enum PacketType : short { + UNKNOWN, + CLIENTINIT, + PLAYERINF, + HACKCAPINF, + GAMEINF, + TAGINF, + PLAYERCON, + PLAYERDC, + COSTUMEINF, + SHINECOLL, + CAPTUREINF, + CHANGESTAGE, + CMD +}; + +// attribute otherwise the build log is spammed with unused warnings +__attribute((used)) static const char *packetNames[] = { + "Unknown", + "Player Info", + "Player Cap Info", + "Game Info", + "Tag Info", + "Player Connect", + "Player Disconnect", + "Costume Info", + "Moon Collection", + "Capture Info", + "Server Command" +}; + +enum SenderType { + SERVER, + CLIENT +}; + +enum ConnectionTypes { + INIT, + RECONNECT +}; + +// unused +/* +static const char *senderNames[] = { + "Server", + "Client" +}; +*/ + +struct Packet { + nn::account::Uid mUserID; // User ID of the packet owner + PacketType mType = PacketType::UNKNOWN; + short mPacketSize = 0; // represents packet size without size of header +}; + +// all packet types + +#include "packets/PlayerInfPacket.h" +#include "packets/PlayerConnect.h" +#include "packets/PlayerDC.h" +#include "packets/GameInf.h" +#include "packets/TagInf.h" +#include "packets/CostumeInf.h" +#include "packets/ServerCommand.h" +#include "packets/ShineCollect.h" +#include "packets/CaptureInf.h" +#include "packets/HackCapInf.h" +#include "packets/ChangeStagePacket.h" +#include "packets/InitPacket.h" \ No newline at end of file diff --git a/include/packets/PlayerConnect.h b/include/packets/PlayerConnect.h new file mode 100644 index 0000000..24e577e --- /dev/null +++ b/include/packets/PlayerConnect.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Packet.h" + +struct PlayerConnect : Packet { + PlayerConnect() : Packet() {this->mType = PacketType::PLAYERCON; mPacketSize = sizeof(PlayerConnect) - sizeof(Packet);}; + ConnectionTypes conType; + u16 maxPlayerCount; + char clientName[COSTUMEBUFSIZE] = {}; +}; \ No newline at end of file diff --git a/include/packets/PlayerDC.h b/include/packets/PlayerDC.h new file mode 100644 index 0000000..d88432e --- /dev/null +++ b/include/packets/PlayerDC.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Packet.h" + +struct PlayerDC : Packet { + PlayerDC() : Packet() {this->mType = PacketType::PLAYERDC; mPacketSize = sizeof(PlayerDC) - sizeof(Packet);}; +}; \ No newline at end of file diff --git a/include/packets/PlayerInfPacket.h b/include/packets/PlayerInfPacket.h new file mode 100644 index 0000000..0b54b0a --- /dev/null +++ b/include/packets/PlayerInfPacket.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Packet.h" +#include "al/util.hpp" +#include "algorithms/PlayerAnims.h" + +struct PlayerInf : Packet { + PlayerInf() : Packet() {mType = PacketType::PLAYERINF; mPacketSize = sizeof(PlayerInf) - sizeof(Packet);}; + sead::Vector3f playerPos; + sead::Quatf playerRot; + float animBlendWeights[6]; + PlayerAnims::Type actName; + PlayerAnims::Type subActName; + + bool operator==(const PlayerInf &rhs) const { + bool isWeightsEqual = true; + for (size_t i = 0; i < 6; i++) + { + if(animBlendWeights[i] != rhs.animBlendWeights[i]) { + isWeightsEqual = false; + break; + } + } + return ( + playerPos == rhs.playerPos && + playerRot == rhs.playerRot && + isWeightsEqual && + actName == rhs.actName && + subActName == rhs.subActName + ); + } + + bool operator!=(const PlayerInf& rhs) const { return !operator==(rhs); } +}; \ No newline at end of file diff --git a/include/packets/ServerCommand.h b/include/packets/ServerCommand.h new file mode 100644 index 0000000..4edd608 --- /dev/null +++ b/include/packets/ServerCommand.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Packet.h" + +struct ServerCommand : Packet { + ServerCommand(const char *command) : Packet() {this->mType = PacketType::CMD; strcpy(srvCmd, command); mPacketSize = sizeof(ServerCommand) - sizeof(Packet);}; + char srvCmd[PACKBUFSIZE] = {}; +}; \ No newline at end of file diff --git a/include/packets/ShineCollect.h b/include/packets/ShineCollect.h new file mode 100644 index 0000000..9f8a8c7 --- /dev/null +++ b/include/packets/ShineCollect.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Packet.h" + +struct ShineCollect : Packet { + ShineCollect() : Packet() {this->mType = PacketType::SHINECOLL; mPacketSize = sizeof(ShineCollect) - sizeof(Packet);}; + int shineId = -1; + bool isGrand = false; +}; \ No newline at end of file diff --git a/include/packets/TagInf.h b/include/packets/TagInf.h new file mode 100644 index 0000000..22bcdb9 --- /dev/null +++ b/include/packets/TagInf.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Packet.h" +#include "sead/basis/seadTypes.h" + +enum TagUpdateType : u8 { + TIME = 1 << 0, + STATE = 1 << 1 +}; + +struct TagInf : Packet { + TagInf() : Packet() { this->mType = PacketType::TAGINF; mPacketSize = sizeof(TagInf) - sizeof(Packet);}; + TagUpdateType updateType; + bool isIt = false; + u8 seconds; + u16 minutes; +}; \ No newline at end of file diff --git a/include/puppets/HackModelHolder.hpp b/include/puppets/HackModelHolder.hpp new file mode 100644 index 0000000..9e9de92 --- /dev/null +++ b/include/puppets/HackModelHolder.hpp @@ -0,0 +1,38 @@ +#pragma once +#include "al/LiveActor/LiveActor.h" +#include "al/util.hpp" + +#include "sead/prim/seadSafeString.hpp" + +#include "helpers.hpp" +#include "actors/PuppetHackActor.h" + +struct CaptureEntry { + PuppetHackActor *actor; + char className[0x16]; +}; + +class HackModelHolder { + public: + HackModelHolder() = default; + + PuppetHackActor *getCapture(const char *hackName); + PuppetHackActor *getCapture(int index); + + const char *getCaptureClass(int index); + bool addCapture(PuppetHackActor *capture, const char *hackName); + bool removeCapture(const char *hackName); + + int getEntryCount() { return mCaptureCount; }; + + bool setCurrent(const char* hackName); + + PuppetHackActor *getCurrentActor(); + const char *getCurrentActorName(); + + void resetList(); + private: + int mCaptureCount; + CaptureEntry *mCurCapture; + CaptureEntry mOnlineCaptures[128]; +}; \ No newline at end of file diff --git a/include/puppets/PuppetHolder.hpp b/include/puppets/PuppetHolder.hpp new file mode 100644 index 0000000..0b4efd6 --- /dev/null +++ b/include/puppets/PuppetHolder.hpp @@ -0,0 +1,39 @@ +#pragma once + + +#include "sead/container/seadPtrArray.h" +#include "sead/prim/seadSafeString.hpp" +#include "logger.hpp" +#include "actors/PuppetActor.h" + +class PuppetHolder { + public: + PuppetHolder(int size); + + void update(); + + bool tryRegisterPuppet(PuppetActor *puppet); + + bool tryRegisterDebugPuppet(PuppetActor *puppet); + + bool checkInfoIsInStage(PuppetInfo *info); + + int getSize() {return mPuppetArr.size(); } + + PuppetActor *getPuppetActor(int idx) {return mPuppetArr[idx];}; + + PuppetActor *getDebugPuppet(); + + void setStageInfo(const char *stageName, u8 scenarioNo); + + void clearPuppets() { mPuppetArr.clear(); } + + private: + sead::PtrArray mPuppetArr; + + PuppetActor *mDebugPuppet; + + sead::FixedSafeString<0x40> mStageName; + + u8 mScenarioNo; +}; \ No newline at end of file diff --git a/include/puppets/PuppetInfo.h b/include/puppets/PuppetInfo.h new file mode 100644 index 0000000..50086fd --- /dev/null +++ b/include/puppets/PuppetInfo.h @@ -0,0 +1,50 @@ +#pragma once + +#include "algorithms/PlayerAnims.h" +#include "packets/Packet.h" + +#include "al/LiveActor/LiveActor.h" + +#include "nn/account.h" + +#include "sead/math/seadVector.h" +#include "sead/math/seadQuat.h" + +struct PuppetInfo { + // General Puppet Info + char puppetName[0x10] = {}; // max user account name size is 10 chars, so this could go down to 0xB + bool isConnected = false; + nn::account::Uid playerID; + // Puppet Translation Info + sead::Vector3f playerPos = sead::Vector3f(0.f,0.f,0.f); + sead::Quatf playerRot = sead::Quatf(0.f,0.f,0.f,0.f); + // Puppet Stage Info + u8 scenarioNo = -1; + char stageName[0x40] = {}; + bool isInSameStage = false; + // Puppet Costume Info + char costumeBody[0x20] = {}; + char costumeHead[0x20] = {}; + // Puppet Capture Info + char curHack[0x40] = {}; + bool isCaptured = false; + bool isStartCapture = false; + // Puppet Model Info + PlayerAnims::Type curAnim; + PlayerAnims::Type curSubAnim; + char curAnimStr[PACKBUFSIZE] = {}; + char curSubAnimStr[PACKBUFSIZE] = {}; + float blendWeights[6] = {}; + float animRate = 0.f; + bool is2D = false; + // Puppet Hack Cap Info + sead::Vector3f capPos = sead::Vector3f(0.f,0.f,0.f); + sead::Quatf capRot = sead::Quatf(0.f,0.f,0.f,0.f); + char capAnim[PACKBUFSIZE] = {}; + bool isCapThrow = false; + bool isHoldThrow = false; + // Hide and Seek Gamemode Info + bool isIt = false; + u8 seconds = 0; + u16 minutes = 0; +}; \ No newline at end of file diff --git a/include/rs/util.hpp b/include/rs/util.hpp new file mode 100644 index 0000000..6da13c9 --- /dev/null +++ b/include/rs/util.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "game/GameData/GameDataFile.h" +#include "game/Info/QuestInfoHolder.h" +#include "sead/math/seadVector.h" +#include "al/util.hpp" +#include "al/sensor/SensorMsg.h" +#include "game/Player/PlayerActorHakoniwa.h" +#include "al/area/ChangeStageInfo.h" + +namespace rs { + + bool isModeE3Rom(void); + + bool isModeE3LiveRom(void); + + bool tryCalcMapNorthDir(sead::Vector3f *, al::IUseSceneObjHolder const*); + + void invalidateQuest(QuestInfo const *info); + + QuestInfoHolder *getQuestInfoHolder(al::IUseSceneObjHolder const*); + + char16_t* getWorldCoinCollectPictureFont(al::LayoutActor const*); + + bool calcOnGroundNormalOrGravityDir(sead::Vector3f*, al::LiveActor const*, IUsePlayerCollision const*); + + void buyCap(al::IUseSceneObjHolder const *, char const*); + void buyCloth(al::IUseSceneObjHolder const *, char const*); + + ChangeStageInfo *createChangeStageInfo(al::LiveActor const *actor, char const *changeStageID, char const *changeStageName, bool, int scenarioNo, ChangeStageInfo::SubScenarioType type); + ChangeStageInfo *createChangeStageInfo(al::LiveActor const*, al::PlacementInfo const&, char const*, char const*, bool, int, ChangeStageInfo::SubScenarioType); + ChangeStageInfo *createChangeStageInfo(const al::LiveActor *, const al::PlacementInfo *); + + bool isPlayerDamageStopDemo(const al::LiveActor *); + + PlayerActorHakoniwa * getPlayerActor(const al::Scene *); + + void get2DAreaPos(sead::Vector3 *, al::AreaObj const *); + + bool isInChangeStageArea(PlayerActorHakoniwa const*, sead::Vector3f const *); + + bool isPlayerOnGround(const al::LiveActor *); + + void faceToCamera(al::LiveActor *actor); + + bool isPlayerActiveMarioAmiiboInvincible(al::LiveActor *); + + bool isMsgPlayerAndCapObjHipDropAll(al::SensorMsg const *); + + bool isMsgPlayerDamage(al::SensorMsg const *); + + bool isMsgShineGet(al::SensorMsg const *); + + void saveCoinStack(al::LiveActor const* actor, al::PlacementId const* placement, int stackCount); + + bool isActiveDemo(al::LiveActor const *); + + bool isActiveDemoWithPlayer(al::Scene const *); + + bool isActiveDemoWithPlayerKeepCarry(al::Scene const *); + + bool isActiveDemoWithPlayerUseCoin(al::Scene const *); + + bool isActiveDemoShineGet(al::Scene const *); + + bool isActiveDemoWarp(al::Scene const *); + + bool isActiveDemoScenarioCamera(al::Scene const *); + + bool isActiveDemoTalk(al::Scene const *); + + void recoveryPlayerOxygen(const al::LiveActor *); + + bool is2D(IUseDimension const *); + + void calcGroundNormalOrGravityDir(sead::Vector3f *result, al::LiveActor const *actor, IUsePlayerCollision const *col); + + void calcPlayerFrontDir(sead::Vector3f *result, al::LiveActor const *); +} diff --git a/include/rs/util/InputUtil.h b/include/rs/util/InputUtil.h new file mode 100644 index 0000000..37b9069 --- /dev/null +++ b/include/rs/util/InputUtil.h @@ -0,0 +1,23 @@ +#pragma once + +#include "al/scene/SceneObjHolder.h" + +namespace rs { + bool isTriggerUiAnyABXY(al::IUseSceneObjHolder const*); + bool isTriggerUiCancel(al::IUseSceneObjHolder const*); + bool isTriggerUiDecide(class GameDataHolder const*); + bool isTriggerUiDecide(al::IUseSceneObjHolder const*); + bool isTriggerUiDown(al::IUseSceneObjHolder const*); + bool isTriggerUiL(al::IUseSceneObjHolder const*); + bool isTriggerUiLeft(al::IUseSceneObjHolder const*); + bool isTriggerUiPause(al::IUseSceneObjHolder const*); + bool isTriggerUiR(al::IUseSceneObjHolder const*); + bool isTriggerUiRacePause(al::IUseSceneObjHolder const*); + bool isTriggerUiRight(al::IUseSceneObjHolder const*); + bool isTriggerUiSelect(al::IUseSceneObjHolder const*); + bool isTriggerUiUp(al::IUseSceneObjHolder const*); + bool isTriggerUiX(al::IUseSceneObjHolder const*); + bool isTriggerUiY(al::IUseSceneObjHolder const*); + bool isTriggerUiZL(al::IUseSceneObjHolder const*); + bool isTriggerUiZR(al::IUseSceneObjHolder const*); +} \ No newline at end of file diff --git a/include/rs/util/ItemUtil.h b/include/rs/util/ItemUtil.h new file mode 100644 index 0000000..e9dd0d5 --- /dev/null +++ b/include/rs/util/ItemUtil.h @@ -0,0 +1,37 @@ +#pragma once + +#include "al/util/StringUtil.h" +#include "al/actor/ActorInitInfo.h" + +namespace rs { + enum ItemType { + COIN, + COIN2D, + COINBLOW, + COINBLOWVERYLITTLE, + COINPOPUP, + COINPOPUPWITHOUTHITREACTION, + COIN3, + COIN5, + COIN10, + COIN10AUTO, + COIN100, + COIN5COUNT, + LIFEUPITEM, + LIFEUPITEMBACK, + LIFEUPITEM2D, + LIFEMAXUPITEM, + LIFEMAXUPITEM2D, + SHINE, + AIRBUBBLE, + DOTMARIOCAT, + KURIBOMINI3, + KURIBOMINI8, + COINSTACKBOUND, + RANDOM + }; + + ItemType getItemType(const char *); + + ItemType getItemType(al::ActorInitInfo const &); +} \ No newline at end of file diff --git a/include/rs/util/LiveActorUtil.h b/include/rs/util/LiveActorUtil.h new file mode 100644 index 0000000..ab65598 --- /dev/null +++ b/include/rs/util/LiveActorUtil.h @@ -0,0 +1,9 @@ +#pragma once + +#include "al/LiveActor/LiveActor.h" + +namespace rs +{ + void initItemByPlacementInfo(al::LiveActor *, al::ActorInitInfo const &, bool); + float setShadowDropLength(al::LiveActor *,al::ActorInitInfo const&,char const*); +} // namespace rs diff --git a/include/rs/util/SensorUtil.h b/include/rs/util/SensorUtil.h new file mode 100644 index 0000000..959b520 --- /dev/null +++ b/include/rs/util/SensorUtil.h @@ -0,0 +1,1018 @@ +#pragma once + +#include "al/sensor/HitSensor.h" +#include "al/sensor/HitSensorKeeper.h" +#include "al/sensor/SensorHitGroup.h" + +#include "al/LiveActor/LiveActor.h" // for SensorMsg + +#include + +namespace rs +{ + +void setAppearItemFactorAndOffsetByMsg(al::LiveActor const*, al::SensorMsg const*, + al::HitSensor const*); + +bool isMsgBreakBySword(al::SensorMsg const*); +bool isMsgStatueDrop(al::SensorMsg const*); +bool isMsgHackerDamageAndCancel(al::SensorMsg const*); +bool isMsgEnableMapCheckPointWarpCollidedGround(al::SensorMsg const*, al::LiveActor const*); +bool isMsgEnableMapCheckPointWarpCollidedGround(al::SensorMsg const*, IUsePlayerCollision const*); +bool isMsgAckCheckpoint(al::SensorMsg const*); +bool isMsgAckGetShine(al::SensorMsg const*); +bool isMsgAckLifeUp(al::SensorMsg const*); +bool isMsgAckLifeMaxUp(al::SensorMsg const*); +bool isMsgAskRailCollision(al::SensorMsg const*); +bool isMsgAttachCactusNeedle(al::SensorMsg const*); +bool isMsgAirExplosion(al::SensorMsg const*); +bool isMsgBelowObjBroken(al::SensorMsg const*); +bool isMsgBindCollidedGround(al::SensorMsg const*); +bool isMsgBindKeepDemoStart(al::SensorMsg const*); +bool isMsgBindKeepDemoExecute(al::SensorMsg const*); +bool isMsgBindKeepDemoEnd(al::SensorMsg const*); +bool isMsgBindRecoveryLife(al::SensorMsg const*); +bool isMsgBirdFlyAway(al::SensorMsg const*); +bool isMsgBlowObjAttack(al::SensorMsg const*); +bool isMsgMayorItemCollide(al::SensorMsg const*); +bool isMsgMayorItemReflect(al::SensorMsg const*); +bool isMsgBlowObjAttackReflect(al::SensorMsg const*); +bool isMsgBossKnuckleCounter(al::SensorMsg const*); +bool isMsgBossKnuckleFallAttack(al::SensorMsg const*); +bool isMsgBossKnuckleHackAttack(al::SensorMsg const*); +bool isMsgBossKnuckleIceConflict(al::SensorMsg const*); +bool isMsgBossKnuckleIceFallToMummy(al::SensorMsg const*); +bool isMsgBossKnuckleKillerAttack(al::SensorMsg const*); +bool isMsgBreakPartsBreak(al::SensorMsg const*); +bool isMsgBreedaSlap(al::SensorMsg const*); +bool isMsgBreedaPush(al::SensorMsg const*); +bool isMsgBubbleAttack(al::SensorMsg const*); +bool isMsgBubbleAttackToPecho(al::SensorMsg const*); +bool isMsgBubbleLauncherStart(al::SensorMsg const*); +bool isMsgBubbleReflectH(al::SensorMsg const*); +bool isMsgBubbleReflectV(al::SensorMsg const*); +bool isMsgBubbleWallTouch(al::SensorMsg const*); +bool isMsgBubbleGroundTouchTrigger(al::SensorMsg const*); +bool isMsgBullAttack(al::SensorMsg const*); +bool isMsgBullHackAttack(al::SensorMsg const*); +bool isMsgBullEnemyAttack(al::SensorMsg const*); +bool isMsgByugoBlow(al::SensorMsg const*); +bool isMsgCameraAngleElevationResetFast(al::SensorMsg const*); +bool isMsgCancelHack(al::SensorMsg const*); +bool isMsgCancelHackArea(al::SensorMsg const*); +bool isMsgCancelHackByDokan(al::SensorMsg const*); +bool isMsgCapAttack(al::SensorMsg const*); +bool isMsgCapAttackCollide(al::SensorMsg const*); +bool isMsgCapAttackRailMove(al::SensorMsg const*); +bool isMsgCapAttackStayRolling(al::SensorMsg const*); +bool isMsgCapAttackStayRollingCollide(al::SensorMsg const*); +bool isMsgCapBeamerBeam(al::SensorMsg const*); +bool isMsgCapCancelLockOn(al::SensorMsg const*); +bool isMsgCapChangeGiant(al::SensorMsg const*); +bool isMsgCapEnableLockOn(al::SensorMsg const*); +bool isMsgCapStartLockOn(al::SensorMsg const*); +bool isMsgCapKeepLockOn(al::SensorMsg const*); +bool isMsgCapGiantAttack(al::SensorMsg const*); +bool isMsgCapHipDrop(al::SensorMsg const*); +bool isMsgCapObjHipDrop(al::SensorMsg const*); +bool isMsgCapObjHipDropReflect(al::SensorMsg const*); +bool isMsgCapIgnoreCancelLockOn(al::SensorMsg const*); +bool isMsgCapIgnoreCancelMissReaction(al::SensorMsg const*); +bool isMsgCapIgnoreCollisionCheck(al::SensorMsg const*); +bool isMsgCapItemGet(al::SensorMsg const*); +bool isMsgCapReflect(al::SensorMsg const*); +bool isMsgCapReflectCollide(al::SensorMsg const*); +bool isMsgCapRethrow(al::SensorMsg const*); +bool isMsgCapTouchWall(al::SensorMsg const*); +bool isMsgCapTrampolineAttack(al::SensorMsg const*); +bool isMsgCatchBombThrough(al::SensorMsg const*); +bool isMsgCheckCarObstacle(al::SensorMsg const*); +bool isMsgCheckIsCardboardBox(al::SensorMsg const*); +bool isMsgChorobonAttack(al::SensorMsg const*); +bool isMsgClearFire(al::SensorMsg const*); +bool isMsgCollectAnimalTouchCollide(al::SensorMsg const*); +bool isMsgCollisionImpulse(al::SensorMsg const*); +bool isMsgConductLightning(al::SensorMsg const*); +bool isMsgConfirmFrailBox(al::SensorMsg const*); +bool isMsgConfirmBrokenFrailBox(al::SensorMsg const*); +bool isMsgDamageBallAttack(al::SensorMsg const*); +bool isMsgDamageBallBodyAttack(al::SensorMsg const*); +bool isMsgDigPointSmell(al::SensorMsg const*); +bool isMsgDonsukeAttack(al::SensorMsg const*); +bool isMsgDonsukeGroundAttack(al::SensorMsg const*); +bool isMsgDonsukePush(al::SensorMsg const*); +bool isMsgDragonAttack(al::SensorMsg const*); +bool isMsgEatExplosion(al::SensorMsg const*); +bool isMsgElectricWireNoLimitDistance(al::SensorMsg const*); +bool isMsgEnableInSaucePan(al::SensorMsg const*); +bool isMsgEnableMapCheckPointWarp(al::SensorMsg const*); +bool isMsgEndInSaucePan(al::SensorMsg const*); +bool isMsgEnemyAttack2D(al::SensorMsg const*); +bool isMsgEnemyAttack3D(al::SensorMsg const*); +bool isMsgEnemyAttackDash(al::SensorMsg const*); +bool isMsgFireBrosFireBallCollide(al::SensorMsg const*); +bool isMsgFireDamageAll(al::SensorMsg const*); +bool isMsgHackAttackFire(al::SensorMsg const*); +bool isMsgFireSwitchFire(al::SensorMsg const*); +bool isMsgEnemyAttackFireCollision(al::SensorMsg const*); +bool isMsgEnemyAttackTRex(al::SensorMsg const*); +bool isMsgPoisonDamageAll(al::SensorMsg const*); +bool isMsgEnemyAttackPoison(al::SensorMsg const*); +bool isMsgHackAttackPoison(al::SensorMsg const*); +bool isMsgPaintAttackPoison(al::SensorMsg const*); +bool isMsgConfirmPaintObj(al::SensorMsg const*); +bool isMsgConfirmPaintObjForSeed(al::SensorMsg const*); +bool isMsgEnemyAttackStrong(al::SensorMsg const*); +bool isMsgEnemyKick(al::SensorMsg const*); +bool isMsgRabbitKick(al::SensorMsg const*); +bool isMsgEnemyObjBreak(al::SensorMsg const*); +bool isMsgFireBlowerAttack(al::SensorMsg const*); +bool isMsgFishingAttack(al::SensorMsg const*); +bool isMsgFishingCancel(al::SensorMsg const*); +bool isMsgFishingFishApproach(al::SensorMsg const*); +bool isMsgFishingFishFloatTouch(al::SensorMsg const*); +bool isMsgFishingItemGet(al::SensorMsg const*); +bool isMsgFishingLineTouch(al::SensorMsg const*); +bool isMsgFishingStart(al::SensorMsg const*); +bool isMsgFishingUpImmediately(al::SensorMsg const*); +bool isMsgFishingUpImmediatelyPrepare(al::SensorMsg const*); +bool isMsgFishingWait(al::SensorMsg const*); +bool isMsgFrogHackTrample(al::SensorMsg const*); +bool isMsgGamaneBullet(al::SensorMsg const*); +bool isMsgGamaneBulletThrough(al::SensorMsg const*); +bool isMsgGamaneBulletForCoinFlower(al::SensorMsg const*); +bool isMsgGemyAim(al::SensorMsg const*); +bool isMsgGhostCancel(al::SensorMsg const*); +bool isMsgGhostPlay(al::SensorMsg const*); +bool isMsgGhostRecordEnd(al::SensorMsg const*); +bool isMsgGhostRecordStart(al::SensorMsg const*); +bool isMsgGhostRecordStartOk(al::SensorMsg const*); +bool isMsgGhostReverse(al::SensorMsg const*); +bool isMsgGhostStop(al::SensorMsg const*); +bool isMsgGiantWanderBossAttack(al::SensorMsg const*); +bool isMsgGiantWanderBossBulletAttack(al::SensorMsg const*); +bool isMsgGiantWanderBossBulletPush(al::SensorMsg const*); +bool isMsgGoldHammerAttack(al::SensorMsg const*); +bool isMsgGrowFlowerSeedDisablePush(al::SensorMsg const*); +bool isMsgGrowFlowerSeedNear(al::SensorMsg const*); +bool isMsgGrowPlantPush(al::SensorMsg const*); +bool isMsgGrowerAttack(al::SensorMsg const*); +bool isMsgGrowerWallAttack(al::SensorMsg const*); +bool isMsgGunetterAttack(al::SensorMsg const*); +bool isMsgGunetterBodyTouch(al::SensorMsg const*); +bool isMsgGunetterPush(al::SensorMsg const*); +bool isMsgHackAttack(al::SensorMsg const*); +bool isMsgHackAttackKick(al::SensorMsg const*); +bool isMsgHackAttackMapObj(al::SensorMsg const*); +bool isMsgHackBrosContact(al::SensorMsg const*); +bool isMsgHackDeathAreaSelfCheck(al::SensorMsg const*); +bool isMsgHackDemoEnd(al::SensorMsg const*); +bool isMsgHackDemoStart(al::SensorMsg const*); +bool isMsgHackInvalidEscape(al::SensorMsg const*); +bool isMsgHackInvalidEscapeNoReaction(al::SensorMsg const*); +bool isMsgHackMarioCheckpointFlagWarp(al::SensorMsg const*); +bool isMsgHackMarioDead(al::SensorMsg const*); +bool isMsgHackMarioDemo(al::SensorMsg const*); +bool isMsgHackMarioInWater(al::SensorMsg const*); +bool isMsgHackMoveRockForestPush(al::SensorMsg const*); +bool isMsgHackSelfCeilingCheckMiss(al::SensorMsg const*); +bool isMsgHackSyncDamageVisibility(al::SensorMsg const*); +bool isMsgHackUpperPunch(al::SensorMsg const*); +bool isMsgHackObjUpperPunch(al::SensorMsg const*); +bool isMsgHammerAttackDown(al::SensorMsg const*); +bool isMsgHammerAttackSide(al::SensorMsg const*); +bool isMsgHammerAttackSideCollide(al::SensorMsg const*); +bool isMsgHammerBrosHammerEnemyAttack(al::SensorMsg const*); +bool isMsgHammerBrosHammerHackAttack(al::SensorMsg const*); +bool isMsgHammerBrosHammerSearch(al::SensorMsg const*); +bool isMsgHipDropTransformReverse(al::SensorMsg const*); +bool isMsgHipDropTransformTransform(al::SensorMsg const*); +bool isMsgHipDropTransformingUp(al::SensorMsg const*); +bool isMsgHipDropTransformingDown(al::SensorMsg const*); +bool isMsgHipDropTransformingFinish(al::SensorMsg const*); +bool isMsgHitGrowFlowerPot(al::SensorMsg const*); +bool isMsgHitGrowPlantPot(al::SensorMsg const*); +bool isMsgHosuiAttack(al::SensorMsg const*); +bool isMsgHosuiAttackCollide(al::SensorMsg const*); +bool isMsgHosuiAttackNoEffect(al::SensorMsg const*); +bool isMsgHosuiAttackStrong(al::SensorMsg const*); +bool isMsgHosuiTouch(al::SensorMsg const*); +bool isMsgHosuiTrample(al::SensorMsg const*); +bool isMsgHosuiTrampleReflect(al::SensorMsg const*); +bool isMsgHosuiTrampleReflectHigh(al::SensorMsg const*); +bool isMsgIgnoreTouchTarget(al::SensorMsg const*); +bool isMsgIgnorePushMotorcycle(al::SensorMsg const*); +bool isMsgIcicleAttack(al::SensorMsg const*); +bool isMsgIgnoreMirrorWarp(al::SensorMsg const*); +bool isMsgIgnoredByRunawayNpc(al::SensorMsg const*); +bool isMsgImplantGrowFlowerSeed(al::SensorMsg const*); +bool isMsgInitTouchTargetInfo(al::SensorMsg const*); +bool isMsgItemAmiiboKoopa(al::SensorMsg const*); +bool isMsgIsExistPukupuku(al::SensorMsg const*); +bool isMsgJangoAttack(al::SensorMsg const*); +bool isMsgJangoRemoveCap(al::SensorMsg const*); +bool isMsgKakkuKick(al::SensorMsg const*); +bool isMsgKoopaBindStart(al::SensorMsg const*); +bool isMsgKoopaCapPlayerFocusTarget(al::SensorMsg const*); +bool isMsgKoopaCapPunchFinishL(al::SensorMsg const*); +bool isMsgKoopaCapPunchFinishR(al::SensorMsg const*); +bool isMsgKoopaCapPunchInvincibleL(al::SensorMsg const*); +bool isMsgKoopaCapPunchInvincibleR(al::SensorMsg const*); +bool isMsgKoopaCapPunchKnockBackL(al::SensorMsg const*); +bool isMsgKoopaCapPunchKnockBackR(al::SensorMsg const*); +bool isMsgKoopaCapPunchL(al::SensorMsg const*); +bool isMsgKoopaCapPunchR(al::SensorMsg const*); +bool isMsgKoopaCapSpinAttack(al::SensorMsg const*); +bool isMsgKoopaCatchKoopaCap(al::SensorMsg const*); +bool isMsgKoopaDashPunchAttack(al::SensorMsg const*); +bool isMsgKoopaFire2D(al::SensorMsg const*); +bool isMsgKoopaFireBallAttack(al::SensorMsg const*); +bool isMsgKoopaHackDamage(al::SensorMsg const*); +bool isMsgKoopaHackPunch(al::SensorMsg const*); +bool isMsgKoopaHackPunchCollide(al::SensorMsg const*); +bool isMsgKoopaHackTrample(al::SensorMsg const*); +bool isMsgKoopaInvalidHackPunchFaceToCollision(al::SensorMsg const*); +bool isMsgKoopaRingBeamInvalidTouch(al::SensorMsg const*); +bool isMsgKoopaTailAttack(al::SensorMsg const*); +bool isMsgKoopaTouchFloor(al::SensorMsg const*); +bool isMsgKouraAttack2D(al::SensorMsg const*); +bool isMsgKouraItemGet2D(al::SensorMsg const*); +bool isMsgKuribo2DTouch(al::SensorMsg const*); +bool isMsgKuriboCollisionDamage(al::SensorMsg const*); +bool isMsgKuriboCollisionKill(al::SensorMsg const*); +bool isMsgKuriboFlick(al::SensorMsg const*); +bool isMsgKuriboGirlAttack(al::SensorMsg const*); +bool isMsgKuriboGirlLove(al::SensorMsg const*); +bool isMsgKuriboTop(al::SensorMsg const*); +bool isMsgKuriboTowerNum(al::SensorMsg const*); +bool isMsgLaunchBlow(al::SensorMsg const*); +bool isMsgLineDancerLink(al::SensorMsg const*); +bool isMsgLongPushSensorHit(al::SensorMsg const*); +bool isMsgLongPushBoxHit(al::SensorMsg const*); +bool isMsgMagnetBulletAttack(al::SensorMsg const*); +bool isMsgMeganeAttack(al::SensorMsg const*); +bool isMsgMeganeHackTrample(al::SensorMsg const*); +bool isMsgMofumofuBodyChainExplode(al::SensorMsg const*); +bool isMsgMofumofuBulletUnexplosion(al::SensorMsg const*); +bool isMsgMoonBasementAttackMeteor(al::SensorMsg const*); +bool isMsgMoonBasementBreakShockwaveMeteor(al::SensorMsg const*); +bool isMsgMoonBasementRockSyncClippingRegist(al::SensorMsg const*); +bool isMsgMoonBasementRockSyncClippingInvalidate(al::SensorMsg const*); +bool isMsgMoonBasementRockSyncClippingValidate(al::SensorMsg const*); +bool isMsgMoonBasementRockThroughCollision(al::SensorMsg const*); +bool isMsgMorningStarWarpEnd(al::SensorMsg const*); +bool isMsgMorningStarWarpStart(al::SensorMsg const*); +bool isMsgMotorcycleAttack(al::SensorMsg const*); +bool isMsgMotorcycleCollideParkingLot(al::SensorMsg const*); +bool isMsgMotorcycleDashAttack(al::SensorMsg const*); +bool isMsgMotorcycleDashCollide(al::SensorMsg const*); +bool isMsgCactusNeedleAttack(al::SensorMsg const*); +bool isMsgCactusNeedleAttackStrong(al::SensorMsg const*); +bool isMsgNoLimitTouchJump(al::SensorMsg const*); +bool isMsgNoticePlayerDamage(al::SensorMsg const*); +bool isMsgNpcScareByEnemy(al::SensorMsg const*); +bool isMsgVolleyballNpcScareByEnemy(al::SensorMsg const*); +bool isMsgObjSnapForce(al::SensorMsg const*); +bool isMsgPackunEatCancel(al::SensorMsg const*); +bool isMsgPackunEatEnd(al::SensorMsg const*); +bool isMsgPackunEatStart(al::SensorMsg const*); +bool isMsgPackunEatStartFollow(al::SensorMsg const*); +bool isMsgPaint(al::SensorMsg const*); +bool isMsgPaintTexture(al::SensorMsg const*); +bool isMsgCheckPaintClear(al::SensorMsg const*); +bool isMsgCheckPaintAlpha(al::SensorMsg const*); +bool isMsgPartyPopperSoundAttack(al::SensorMsg const*); +bool isMsgPechoSpot(al::SensorMsg const*); +bool isMsgPlayerBallToss(al::SensorMsg const*); +bool isMsgPlayerCarryCameraSubjectiveStart(al::SensorMsg const*); +bool isMsgPlayerCarryCameraSubjectiveEnd(al::SensorMsg const*); +bool isMsgPlayerCarryShineGetStart(al::SensorMsg const*); +bool isMsgPlayerCarryShineGetEnd(al::SensorMsg const*); +bool isMsgPlayerCapCatch(al::SensorMsg const*); +bool isMsgPlayerCapHipDrop(al::SensorMsg const*); +bool isMsgPlayerCapPush(al::SensorMsg const*); +bool isMsgPlayerCapRecovery(al::SensorMsg const*); +bool isMsgPlayerCapTouchJump(al::SensorMsg const*); +bool isMsgPlayerCapTrample(al::SensorMsg const*); +bool isMsgPlayerCoinDashGet(al::SensorMsg const*); +bool isMsgPlayerEyePriorityTarget(al::SensorMsg const*); +bool isMsgPlayerDisregardHomingAttack(al::SensorMsg const*); +bool isMsgPlayerDisregardTargetMarker(al::SensorMsg const*); +bool isMsgPlayerEquipKoopaCap(al::SensorMsg const*); +bool isMsgPlayerFireBallAttack2D(al::SensorMsg const*); +bool isMsgPlayerFireBallAttack3D(al::SensorMsg const*); +bool isMsgPlayerHipDropDemoTrigger(al::SensorMsg const*); +bool isMsgPlayerHipDropHipDropSwitch(al::SensorMsg const*); +bool isMsgPlayerItemGet2D(al::SensorMsg const*); +bool isMsgPlayerJumpTakeOffFloor(al::SensorMsg const*); +bool isMsgPlayerObjectWallHit(al::SensorMsg const*); +bool isMsgPlayerObjLeapFrog(al::SensorMsg const*); +bool isMsgPlayerPenguinAttack(al::SensorMsg const*); +bool isMsgPlayerPenguinAttackReflect(al::SensorMsg const*); +bool isMsgPlayerPoleClimbKeep(al::SensorMsg const*); +bool isMsgPlayerPoleClimbReaction(al::SensorMsg const*); +bool isMsgPlayerRabbitGet(al::SensorMsg const*); +bool isMsgPlayerRollingObjHit(al::SensorMsg const*); +bool isMsgPlayerRollingWallHitDown(al::SensorMsg const*); +bool isMsgPlayerRollingWallHitMove(al::SensorMsg const*); +bool isMsgPlayerStartGrabCeil(al::SensorMsg const*); +bool isMsgPlayerStartWallJump(al::SensorMsg const*); +bool isMsgPlayerEndGrabCeil(al::SensorMsg const*); +bool isMsgPlayerSwordAttack(al::SensorMsg const*); +bool isMsgPlayerTouchFloorJumpCode(al::SensorMsg const*); +bool isMsgPlayerTrample2D(al::SensorMsg const*); +bool isMsgPlayerUpperPunch2D(al::SensorMsg const*); +bool isMsgPlayerObjUpperPunch2D(al::SensorMsg const*); +bool isMsgPropellerAttack(al::SensorMsg const*); +bool isMsgPukupukuDash(al::SensorMsg const*); +bool isMsgPukupukuKiss(al::SensorMsg const*); +bool isMsgPukupukuRollingAttack(al::SensorMsg const*); +bool isMsgPunchMachinePunchHook(al::SensorMsg const*); +bool isMsgPunchMachinePunchStraight(al::SensorMsg const*); +bool isMsgPunchMachinePunchUpper(al::SensorMsg const*); +bool isMsgPush2D(al::SensorMsg const*); +bool isMsgPushToFish(al::SensorMsg const*); +bool isMsgPushToMotorcycle(al::SensorMsg const*); +bool isMsgPushToPlayer(al::SensorMsg const*); +bool isMsgRadishAttack(al::SensorMsg const*); +bool isMsgRadishReflect(al::SensorMsg const*); +bool isMsgRaceStart(al::SensorMsg const*); +bool isMsgRaceStop(al::SensorMsg const*); +bool isMsgRaceWait(al::SensorMsg const*); +bool isMsgRequestChangeFireFlower(al::SensorMsg const*); +bool isMsgRequestChangeKinokoSuper(al::SensorMsg const*); +bool isMsgRequestPlayerJumpBreakFloor(al::SensorMsg const*); +bool isMsgRequestPlayerJump(al::SensorMsg const*); +bool isMsgRequestPlayerTrampleJump(al::SensorMsg const*); +bool isMsgRequestPlayerSpinJump(al::SensorMsg const*); +bool isMsgRequestSphinxJump(al::SensorMsg const*); +bool isMsgRideOnEnd(al::SensorMsg const*); +bool isMsgRideOnRelease(al::SensorMsg const*); +bool isMsgRideOnStart(al::SensorMsg const*); +bool isMsgRocketFlowerExtension(al::SensorMsg const*); +bool isMsgSandSharkAttack(al::SensorMsg const*); +bool isMsgSeedAttack(al::SensorMsg const*); +bool isMsgSeedAttackBig(al::SensorMsg const*); +bool isMsgSeedAttackHold(al::SensorMsg const*); +bool isMsgSeedItemGet(al::SensorMsg const*); +bool isMsgSeedReflect(al::SensorMsg const*); +bool isMsgSeedTouch(al::SensorMsg const*); +bool isMsgSenobiCancelStretch(al::SensorMsg const*); +bool isMsgSenobiPunchBlockTransparent(al::SensorMsg const*); +bool isMsgSenobiPartsMove(al::SensorMsg const*); +bool isMsgSenobiTrample(al::SensorMsg const*); +bool isMsgShibakenApproach(al::SensorMsg const*); +bool isMsgShibakenKick(al::SensorMsg const*); +bool isMsgSkaterAttack(al::SensorMsg const*); +bool isMsgSpherePush(al::SensorMsg const*); +bool isMsgSphinxJumpAttack(al::SensorMsg const*); +bool isMsgSphinxQuizRouteKill(al::SensorMsg const*); +bool isMsgSphinxRideAttack(al::SensorMsg const*); +bool isMsgSphinxRideAttackReflect(al::SensorMsg const*); +bool isMsgSphinxRideAttackTouchThrough(al::SensorMsg const*); +bool isMsgSphinxRideAttackTouch(al::SensorMsg const*); +bool isMsgStampTo2D(al::SensorMsg const*); +bool isMsgStartHack(al::SensorMsg const*); +bool isMsgStartInSaucePan(al::SensorMsg const*); +bool isMsgStatueDrop(al::SensorMsg const*); +bool isMsgStatueTrampleReflect(al::SensorMsg const*); +bool isMsgStatuePush(al::SensorMsg const*); +bool isMsgStatueSnap(al::SensorMsg const*); +bool isMsgSunshineAttack(al::SensorMsg const*); +bool isMsgTankBullet(al::SensorMsg const*); +bool isMsgTankBulletNoReaction(al::SensorMsg const*); +bool isMsgTankExplosion(al::SensorMsg const*); +bool isMsgTankHackTrample(al::SensorMsg const*); +bool isMsgTankKickHack(al::SensorMsg const*); +bool isMsgTankKickEnemy(al::SensorMsg const*); +bool isMsgTankLookOn(al::SensorMsg const*); +bool isMsgTestPunch(al::SensorMsg const*); +bool isMsgTestPunchStrong(al::SensorMsg const*); +bool isMsgTimerAthleticDemoStart(al::SensorMsg const*); +bool isMsgTouchDoorDrumn(al::SensorMsg const*); +bool isMsgTouchFireDrum2D(al::SensorMsg const*); +bool isMsgTrashBoxIn(al::SensorMsg const*); +bool isMsgTRexAttack(al::SensorMsg const*); +bool isMsgTRexAttackCollideAll(al::SensorMsg const*); +bool isMsgTRexAttackCollideBody(al::SensorMsg const*); +bool isMsgTRexAttackCollideHead(al::SensorMsg const*); +bool isMsgTRexDashAttack(al::SensorMsg const*); +bool isMsgTRexScrollPartsBreakWith(al::SensorMsg const*); +bool isMsgTsukkunForceCancelCollide(al::SensorMsg const*); +bool isMsgTsukkunHoldCollide(al::SensorMsg const*); +bool isMsgTsukkunThroughCollide(al::SensorMsg const*); +bool isMsgTsukkunThrustAll(al::SensorMsg const*); +bool isMsgTsukkunThrust(al::SensorMsg const*, bool*); +bool isMsgTsukkunThrustCollide(al::SensorMsg const*, bool*); +bool isMsgTsukkunNoTrace(al::SensorMsg const*); +bool isMsgTsukkunThrustHole(al::SensorMsg const*); +bool isMsgUtsuboAttack(al::SensorMsg const*); +bool isMsgWanderBossCameraRange(al::SensorMsg const*); +bool isMsgWanwanEnemyAttack(al::SensorMsg const*); +bool isMsgWanwanBlockAttack(al::SensorMsg const*); +bool isMsgWanwanHoleIn(al::SensorMsg const*); +bool isMsgWaterRoadIn(al::SensorMsg const*); +bool isMsgWaterRoadNear(al::SensorMsg const*); +bool isMsgWanwanPush(al::SensorMsg const*); +bool isMsgWanwanReboundAttack(al::SensorMsg const*); +bool isMsgWeaponItemGet(al::SensorMsg const*); +bool isMsgWhipAttack(al::SensorMsg const*); +bool isMsgWhipBind(al::SensorMsg const*); +bool isMsgWhipHold(al::SensorMsg const*); +bool isMsgWhipThrow(al::SensorMsg const*); +bool isMsgYokinBallAttack(al::SensorMsg const*); +bool isMsgYoshiDirectEat(al::SensorMsg const*); +bool isMsgYoshiTongueAttack(al::SensorMsg const*); +bool isMsgYoshiTongueEatBind(al::SensorMsg const*); +bool isMsgYoshiTongueEatBindCancel(al::SensorMsg const*); +bool isMsgYoshiTongueEatBindFinish(al::SensorMsg const*); +bool isMsgYoshiTongueEatHomingTarget(al::SensorMsg const*); +bool isMsgYukimaruPush(al::SensorMsg const*); +bool isMsgKillerAttackNoExplode(al::SensorMsg const*); +bool isMsgKillerMagnumAttack(al::SensorMsg const*); +bool isMsgKillerMagnumHackAttack(al::SensorMsg const*); +bool isMsgGabuzouAttack(al::SensorMsg const*); +bool isMsgStackerRollingAttack(al::SensorMsg const*); +bool isMsgStackerCapBoostAttack(al::SensorMsg const*); +bool isMsgIgnoreIgnitionBomb(al::SensorMsg const*); +bool isMsgExplosionReflectBomb(al::SensorMsg const*); +bool isMsgGolemStampPress(al::SensorMsg const*); +bool isMsgTsukkunThrustSpin(al::SensorMsg const*, bool*); +bool isMsgTsukkunThrustReflect(al::SensorMsg const*, bool*); +bool isMsgTsukkunThrustHitReflectCollide(al::SensorMsg const*, bool*); +bool isMsgTsukkunThrustReflectCollide(al::SensorMsg const*, bool*); +// bool isMsgSwitchOnWithSaveRequest(al::SensorMsg const*, SaveObjInfo**); +bool isMsgNpcCapReactionAll(al::SensorMsg const*); +bool isMsgHackNpcCapReactionAll(al::SensorMsg const*); +bool isMsgPlayerAndCapObjHipDropReflectAll(al::SensorMsg const*); +bool isMsgKoopaCapPunchAll(al::SensorMsg const*); +bool isMsgKoopaCapPunchInvincibleAll(al::SensorMsg const*); +bool isMsgKoopaCapPunchFinishAll(al::SensorMsg const*); +bool isMsgItemGet(al::SensorMsg const*); +bool isMsgItemGetByWeapon(al::SensorMsg const*); +bool isMsgItemGet2D(al::SensorMsg const*); +bool isMsgBlockUpperPunch2D(al::SensorMsg const*); +bool isMsgItemGetAll(al::SensorMsg const*); +bool isMsgShineGet(al::SensorMsg const*); +bool isMsgShineGet2D(al::SensorMsg const*); +bool isMsgShineReaction(al::SensorMsg const*); +bool isMsgKillByShineGet(al::SensorMsg const*); +bool isMsgKillByHomeDemo(al::SensorMsg const*); +bool isMsgEndHomeDemo(al::SensorMsg const*); +bool isMsgBreakBySword(al::SensorMsg const*); +bool isMsgPressDown(al::SensorMsg const*); +bool isMsgPlayerAndCapObjHipDropAll(al::SensorMsg const*); +bool isMsgAttackDirect(al::SensorMsg const*); +bool isMsgBlowDown(al::SensorMsg const*); +bool isMsgTrampleReflectAll(al::SensorMsg const*); +bool isMsgThrowObjHit(al::SensorMsg const*); +bool isMsgThrowObjHitReflect(al::SensorMsg const*); +bool isMsgPlayerAndCapHipDropAll(al::SensorMsg const*); +bool isMsgUpperPunchAll(al::SensorMsg const*); +bool isMsgBlockReaction2D(al::SensorMsg const*); +bool isMsgBlockReaction3D(al::SensorMsg const*); +bool isMsgBlockReactionAll(al::SensorMsg const*); +bool isMsgBreakFrailBox(al::SensorMsg const*); +bool isMsgDamageFrailBox(al::SensorMsg const*); +bool isMsgChorobonReaction(al::SensorMsg const*); +bool isMsgBreakSignBoard(al::SensorMsg const*); +bool isMsgBreakCollapseSandHill(al::SensorMsg const*); +bool isMsgPlayerDamage(al::SensorMsg const*); +bool isMsgPlayerDamage2D(al::SensorMsg const*); +bool isMsgPlayerDamageBlowDown(al::SensorMsg const*); +bool isMsgPlayerJumpRequestAll(al::SensorMsg const*); +bool isMsgDashPanel(al::SensorMsg const*, int*); +bool isMsgNetworkShootingShot(al::SensorMsg const*, int); +bool isMsgNetworkShootingChargeShot(al::SensorMsg const*, int); +bool isMsgRaceReturnToCourse(al::SensorMsg const*, sead::Vector3f*, sead::Vector3f*); +bool isMsgTrampolineCrackJump(float*, float*, al::SensorMsg const*); +bool isMsgGotogotonOn(al::SensorMsg const*); +bool isMsgGotogotonGoalExist(al::SensorMsg const*); +bool isMsgBossMagmaCatchPlayer(al::SensorMsg const*); +bool isMsgBossMagmaReleasePlayer(al::SensorMsg const*); +bool isMsgBossMagmaDeadDemoStart(al::SensorMsg const*); +bool isMsgBossMagmaDeadDemoEnd(al::SensorMsg const*); +bool isMsgBossMagmaQueryToBubble(al::SensorMsg const*); +bool isMsgMofumofuReflectAll(al::SensorMsg const*); +bool isMsgCheckFishingTarget(al::SensorMsg const*); +bool isMsgPlayerLookAtPosition(al::SensorMsg const*); +bool isMsgTargetMarkerPosition(al::SensorMsg const*); +bool isMsgSandGeyserRaise(al::SensorMsg const*); +bool isMsgInitCapTarget(al::SensorMsg const*); +bool isMsgTransferHack(al::SensorMsg const*); +bool isMsgRequestTransferHack(al::SensorMsg const*); +bool isMsgInitHack(al::SensorMsg const*); +bool isMsgEndHack(HackEndParam const**, al::SensorMsg const*); +bool isMsgHackDirectStageInit(IUsePlayerHack**, al::SensorMsg const*); +bool isMsgKillByBossBattleDemo(al::SensorMsg const*); +bool isMsgKillByHackFirstDemo(al::SensorMsg const*); +bool isMsgKillByMoonRockDemo(al::SensorMsg const*); +bool isMsgKillBySwitchTimer(al::SensorMsg const*); + +bool setMsgYoshiTongueEatBindRadiusAndOffset(al::SensorMsg const*, float, float); +bool setMsgYoshiTongueEatBindScale(al::SensorMsg const*, float); +bool setMsgPlayerLookAtPosition(al::SensorMsg const*, sead::Vector3f const&); +bool setMsgTargetMarkerPosition(al::SensorMsg const*, sead::Vector3f const&); + +bool requestHitReactionToAttacker(al::SensorMsg const*, al::HitSensor const*, al::HitSensor const*); + +bool sendMsgHackerNoReaction(IUsePlayerHack const*, al::HitSensor*, al::HitSensor*); +bool sendMsgHackerNoReactionWithoutShine(IUsePlayerHack const*, al::HitSensor*, al::HitSensor*); +bool sendMsgBreakFloorToPlayer(al::LiveActor const*); +bool sendMsgAckCheckpoint(al::HitSensor*, al::HitSensor*); +bool sendMsgAckGetShine(al::HitSensor*, al::HitSensor*); +bool sendMsgAckLifeUp(al::HitSensor*, al::HitSensor*); +bool sendMsgAckLifeMaxUp(al::HitSensor*, al::HitSensor*); +bool sendMsgAskRailCollision(al::HitSensor*, al::HitSensor*); +bool sendMsgBreakPartsBreak(al::HitSensor*, al::HitSensor*); +bool sendMsgBirdFlyAway(al::HitSensor*, al::HitSensor*); +bool sendMsgCameraAngleElevationResetFast(al::HitSensor*, al::HitSensor*); +bool sendMsgChorobonAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgConductLightning(al::HitSensor*, al::HitSensor*); +bool sendMsgDamageBallAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgDamageBallBodyAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgDonsukeAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgDonsukeGroundAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgDonsukePush(al::HitSensor*, al::HitSensor*); +bool sendMsgDragonAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgEatExplosion(al::HitSensor*, al::HitSensor*); +bool sendMsgElectricWireNoLimitDistance(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyAttack2D(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyAttack3D(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyAttackDash(al::HitSensor*, al::HitSensor*); +bool sendMsgHackAttackFire(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyAttackFireCollision(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyAttackPoison(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyAttackTRex(al::HitSensor*, al::HitSensor*); +bool sendMsgHackAttackPoison(al::HitSensor*, al::HitSensor*); +bool sendMsgConfirmPaintObj(al::HitSensor*, al::HitSensor*); +bool sendMsgConfirmPaintObjForSeed(al::HitSensor*, al::HitSensor*); +bool sendMsgPaintAttackPoison(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyKick(al::HitSensor*, al::HitSensor*); +bool sendMsgRabbitKick(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingCancel(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingFishApproach(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingFishFloatTouch(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingItemGet(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingLineTouch(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingStart(al::HitSensor*, al::HitSensor*); +bool sendMsgFishingUpImmediatelyPrepare(al::HitSensor*, al::HitSensor*); +bool sendMsgFireBrosFireBallCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgFireSwitchFire(al::HitSensor*, al::HitSensor*); +bool sendMsgFrogHackTrample(al::HitSensor*, al::HitSensor*); +bool sendMsgGhostRecordStart(al::HitSensor*, al::HitSensor*); +bool sendMsgGhostRecordEnd(al::HitSensor*, al::HitSensor*); +bool sendMsgGhostPlay(al::HitSensor*, al::HitSensor*); +bool sendMsgGhostStop(al::HitSensor*, al::HitSensor*); +bool sendMsgGiantWanderBossAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgGiantWanderBossBulletAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgGiantWanderBossBulletPush(al::HitSensor*, al::HitSensor*); +bool sendMsgGhostReverse(al::HitSensor*, al::HitSensor*); +bool sendMsgGhostCancel(al::HitSensor*, al::HitSensor*); +bool sendMsgGrowFlowerSeedDisablePush(al::HitSensor*, al::HitSensor*); +bool sendMsgGrowFlowerSeedNear(al::HitSensor*, al::HitSensor*); +bool sendMsgGrowPlantPush(al::HitSensor*, al::HitSensor*); +bool sendMsgGrowerAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgGrowerWallAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgGunetterAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgGunetterBodyTouch(al::HitSensor*, al::HitSensor*); +bool sendMsgHammerAttackDown(al::HitSensor*, al::HitSensor*); +bool sendMsgHammerAttackSide(al::HitSensor*, al::HitSensor*); +bool sendMsgHammerAttackSideCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgItemAmiiboKoopa(al::HitSensor*, al::HitSensor*); +bool sendMsgIsExistPukupuku(al::HitSensor*, al::HitSensor*); +bool sendMsgJangoAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgJangoRemoveCap(al::HitSensor*, al::HitSensor*); +bool sendMsgKakkuKick(al::HitSensor*, al::HitSensor*); +bool sendMsgKillByBossBattleDemo(al::HitSensor*, al::HitSensor*); +bool sendMsgKillByHackFirstDemo(al::HitSensor*, al::HitSensor*); +bool sendMsgKillByHomeDemo(al::HitSensor*, al::HitSensor*); +bool sendMsgEndHomeDemo(al::HitSensor*, al::HitSensor*); +bool sendMsgKillByMoonRockDemo(al::HitSensor*, al::HitSensor*); +bool sendMsgKillByShineGet(al::HitSensor*, al::HitSensor*); +bool sendMsgKillBySwitchTimer(al::LiveActor*); +bool sendMsgKoopaBindStart(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPlayerFocusTarget(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchFinishL(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchFinishR(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchInvincibleL(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchInvincibleR(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchKnockBackL(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchKnockBackR(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchL(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapPunchR(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCapSpinAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaCatchKoopaCap(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaDashPunchAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaFire2D(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaFireBallAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaHackDamage(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaHackPunch(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaHackPunchCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaHackTrample(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaInvalidHackPunchFaceToCollision(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaRingBeamInvalidTouch(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaTailAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgKoopaTouchFloor(al::HitSensor*, al::HitSensor*); +bool sendMsgKouraAttack2D(al::HitSensor*, al::HitSensor*); +bool sendMsgKouraItemGet2D(al::HitSensor*, al::HitSensor*); +bool sendMsgLaunchBlow(al::HitSensor*, al::HitSensor*); +bool sendMsgMeganeAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgMeganeHackTrample(al::HitSensor*, al::HitSensor*); +bool sendMsgMofumofuBulletUnexplosion(al::HitSensor*, al::HitSensor*); +bool sendMsgMoonBasementAttackMeteor(al::HitSensor*, al::HitSensor*); +bool sendMsgMoonBasementBreakShockwaveMeteor(al::HitSensor*, al::HitSensor*); +bool sendMsgMoonBasementRockSyncClippingRegist(al::HitSensor*, al::HitSensor*); +bool sendMsgMoonBasementRockSyncClippingInvalidate(al::HitSensor*, al::HitSensor*); +bool sendMsgMoonBasementRockSyncClippingValidate(al::HitSensor*, al::HitSensor*); +bool sendMsgAttachCactusNeedle(al::HitSensor*, al::HitSensor*); +// bool sendMsgCactusNeedleAttack(al::HitSensor*, al::HitSensor*, al::ComboCounter*); +// bool sendMsgCactusNeedleAttackStrong(al::HitSensor*, al::HitSensor*, al::ComboCounter*); +bool sendMsgPlayerBallToss(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCarryCameraSubjectiveStart(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCarryCameraSubjectiveEnd(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCarryShineGetStart(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCarryShineGetEnd(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCapCatch(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCapHipDrop(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCapPush(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCapRecovery(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCapTouchJump(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCapTrample(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerCoinDashGet(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerEyePriorityTarget(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerDisregardHomingAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerDisregardTargetMarker(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerEquipKoopaCap(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerFireBallAttack2D(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerFireBallAttack3D(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerHipDropDemoTrigger(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerHipDropHipDropSwitch(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerItemGet2D(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerJumpTakeOffFloor(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerObjectWallHit(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerObjLeapFrog(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerPenguinAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerPenguinAttackReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerPoleClimbKeep(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerPoleClimbReaction(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerRabbitGet(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerRollingObjHit(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerRollingWallHitDown(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerRollingWallHitMove(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerStartGrabCeil(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerStartWallJump(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerEndGrabCeil(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerSwordAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerTouchFloorJumpCode(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerTrample2D(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerUpperPunch2D(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerObjUpperPunch2D(al::HitSensor*, al::HitSensor*); +bool sendMsgPukupukuDash(al::HitSensor*, al::HitSensor*); +bool sendMsgPukupukuKiss(al::HitSensor*, al::HitSensor*); +bool sendMsgPukupukuRollingAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgPush2D(al::HitSensor*, al::HitSensor*); +bool sendMsgPushToFish(al::HitSensor*, al::HitSensor*); +bool sendMsgPushToMotorcycle(al::HitSensor*, al::HitSensor*); +bool sendMsgPushToPlayer(al::HitSensor*, al::HitSensor*); +bool sendMsgRadishAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgRadishReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgRequestChangeKinokoSuper(al::HitSensor*, al::HitSensor*); +bool sendMsgRequestChangeFireFlower(al::HitSensor*, al::HitSensor*); +bool sendMsgRequestPlayerSandMoon(al::HitSensor*, al::HitSensor*); +bool sendMsgRequestPlayerSnow(al::HitSensor*, al::HitSensor*); +bool sendMsgRequestPlayerWet(al::HitSensor*, al::HitSensor*); +bool sendMsgStartHack(al::HitSensor*, al::HitSensor*); +bool sendMsgSunshineAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgCancelHack(al::HitSensor*, al::HitSensor*); +bool sendMsgCancelHackArea(al::HitSensor*, al::HitSensor*); +bool sendMsgCancelHackByDokan(al::HitSensor*, al::HitSensor*); +bool sendMsgPackunEatStart(al::HitSensor*, al::HitSensor*); +bool sendMsgHackAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgHackAttackKick(al::HitSensor*, al::HitSensor*); +bool sendMsgHackAttackMapObj(al::HitSensor*, al::HitSensor*); +bool sendMsgHackDeathAreaSelfCheck(al::HitSensor*, al::HitSensor*); +bool sendMsgHackDemoEnd(al::HitSensor*, al::HitSensor*); +bool sendMsgHackDemoStart(al::HitSensor*, al::HitSensor*); +bool sendMsgHackInvalidEscape(al::HitSensor*, al::HitSensor*); +bool sendMsgHackInvalidEscapeNoReaction(al::HitSensor*, al::HitSensor*); +bool sendMsgHackMarioCheckpointFlagWarp(al::HitSensor*, al::HitSensor*); +bool sendMsgHackMarioDead(al::HitSensor*, al::HitSensor*); +bool sendMsgHackMarioDemo(al::HitSensor*, al::HitSensor*); +bool sendMsgHackMarioInWater(al::HitSensor*, al::HitSensor*); +bool sendMsgHackMoveRockForestPush(al::HitSensor*, al::HitSensor*); +bool sendMsgHackSelfCeilingCheckMiss(al::HitSensor*, al::HitSensor*); +bool sendMsgHackSyncDamageVisibility(al::HitSensor*, al::HitSensor*); +bool sendMsgWeaponItemGet(al::HitSensor*, al::HitSensor*); +bool sendMsgCapAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgCapAttackCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgCapAttackStayRolling(al::HitSensor*, al::HitSensor*); +bool sendMsgCapAttackStayRollingCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgCapAttackRailMove(al::HitSensor*, al::HitSensor*); +bool sendMsgCapGiantAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgCapReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgCapReflectCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgCapStartLockOn(al::HitSensor*, al::HitSensor*); +bool sendMsgCapKeepLockOn(al::HitSensor*, al::HitSensor*); +bool sendMsgCapCancelLockOn(al::HitSensor*, al::HitSensor*); +bool sendMsgCapHipDrop(al::HitSensor*, al::HitSensor*); +bool sendMsgCapObjHipDrop(al::HitSensor*, al::HitSensor*); +bool sendMsgCapObjHipDropReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgCapIgnoreCancelLockOn(al::HitSensor*, al::HitSensor*); +bool sendMsgCapIgnoreCancelMissReaction(al::HitSensor*, al::HitSensor*); +bool sendMsgCapIgnoreCollisionCheck(al::HitSensor*, al::HitSensor*); +bool sendMsgCapItemGet(al::HitSensor*, al::HitSensor*); +bool sendMsgCapTrampolineAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgCatchBombThrough(al::HitSensor*, al::HitSensor*); +bool sendMsgCheckCarObstacle(al::HitSensor*, al::HitSensor*); +bool sendMsgCheckIsCardboardBox(al::HitSensor*, al::HitSensor*); +bool sendMsgBullHackAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgBullEnemyAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyAttackStrong(al::HitSensor*, al::HitSensor*); +bool sendMsgEnemyObjBreak(al::HitSensor*, al::HitSensor*); +bool sendMsgWhipAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgNoLimitTouchJump(al::HitSensor*, al::HitSensor*); +bool sendMsgNoticePlayerDamage(al::HitSensor*, al::HitSensor*); +bool sendMsgStatueDrop(al::HitSensor*, al::HitSensor*); +bool sendMsgStatueTrampleReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgStatuePush(al::HitSensor*, al::HitSensor*); +bool sendMsgStatueSnap(al::HitSensor*, al::HitSensor*); +bool sendMsgHitGrowFlowerPot(al::HitSensor*, al::HitSensor*); +bool sendMsgHitGrowPlantPot(al::HitSensor*, al::HitSensor*); +bool sendMsgImplantGrowFlowerSeed(al::HitSensor*, al::HitSensor*); +bool sendMsgIcicleAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgIgnoreMirrorWarp(al::HitSensor*, al::HitSensor*); +bool sendMsgIgnoredByRunawayNpc(al::HitSensor*, al::HitSensor*); +bool sendMsgIgnorePushMotorcycle(al::HitSensor*, al::HitSensor*); +bool sendMsgSandSharkAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgSeedAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgSeedAttackBig(al::HitSensor*, al::HitSensor*); +bool sendMsgSeedAttackHold(al::HitSensor*, al::HitSensor*); +bool sendMsgSeedItemGet(al::HitSensor*, al::HitSensor*); +bool sendMsgSeedReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgSenobiTrample(al::HitSensor*, al::HitSensor*); +bool sendMsgSenobiCancelStretch(al::HitSensor*, al::HitSensor*); +bool sendMsgSenobiPunchBlockTransparent(al::HitSensor*, al::HitSensor*); +bool sendMsgShibakenApproach(al::HitSensor*, al::HitSensor*); +bool sendMsgShibakenKick(al::HitSensor*, al::HitSensor*); +bool sendMsgHackUpperPunch(al::HitSensor*, al::HitSensor*); +bool sendMsgHackObjUpperPunch(al::HitSensor*, al::HitSensor*); +bool sendMsgShineGet(al::HitSensor*, al::HitSensor*); +bool sendMsgShineGet2D(al::HitSensor*, al::HitSensor*); +bool sendMsgSpherePush(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, float); +bool sendMsgSphinxJumpAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgSphinxQuizRouteKill(al::HitSensor*, al::HitSensor*); +bool sendMsgSphinxRideAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgSphinxRideAttackReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgPechoSpot(al::HitSensor*, al::HitSensor*); +bool sendMsgBelowObjBroken(al::HitSensor*, al::HitSensor*); +bool sendMsgBindCollidedGround(al::HitSensor*, al::HitSensor*); +bool sendMsgBindKeepDemoStart(al::HitSensor*, al::HitSensor*); +bool sendMsgBindKeepDemoExecute(al::HitSensor*, al::HitSensor*); +bool sendMsgBindKeepDemoEnd(al::HitSensor*, al::HitSensor*); +bool sendMsgBindRecoveryLife(al::HitSensor*, al::HitSensor*); +bool sendMsgMayorItemReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgMayorItemCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgBlowObjAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgBlowObjAttackReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgConfirmFrailBox(al::HitSensor*, al::HitSensor*); +bool sendMsgConfirmBrokenFrailBox(al::HitSensor*, al::HitSensor*); +bool sendMsgTankLookOn(al::HitSensor*, al::HitSensor*); +bool sendMsgTankKickHack(al::HitSensor*, al::HitSensor*); +bool sendMsgTankKickEnemy(al::HitSensor*, al::HitSensor*); +bool sendMsgTankBullet(al::HitSensor*, al::HitSensor*); +bool sendMsgTankBulletNoReaction(al::HitSensor*, al::HitSensor*); +bool sendMsgTimerAthleticDemoStart(al::LiveActor*); +bool sendMsgRideOnStart(al::HitSensor*, al::HitSensor*); +bool sendMsgRideOnEnd(al::HitSensor*, al::HitSensor*); +bool sendMsgRideOnRelease(al::HitSensor*, al::HitSensor*); +bool sendMsgHipDropTransformTransform(al::HitSensor*, al::HitSensor*); +bool sendMsgHipDropTransformReverse(al::HitSensor*, al::HitSensor*); +bool sendMsgHipDropTransformingUp(al::HitSensor*, al::HitSensor*); +bool sendMsgHipDropTransformingDown(al::HitSensor*, al::HitSensor*); +bool sendMsgHipDropTransformingFinish(al::HitSensor*, al::HitSensor*); +bool sendMsgClearFire(al::HitSensor*, al::HitSensor*); +bool sendMsgCollectAnimalTouchCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgBossKnuckleCounter(al::HitSensor*, al::HitSensor*); +bool sendMsgBossKnuckleFallAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgBossKnuckleHackAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgBossKnuckleKillerAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgBossKnuckleIceConflict(al::HitSensor*, al::HitSensor*); +bool sendMsgBossKnuckleIceFallToMummy(al::HitSensor*, al::HitSensor*); +bool sendMsgSkaterAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgEnableInSaucePan(al::HitSensor*, al::HitSensor*); +bool sendMsgEnableMapCheckPointWarp(al::HitSensor*, al::HitSensor*); +bool sendMsgStartInSaucePan(al::HitSensor*, al::HitSensor*, bool); +bool sendMsgEndInSaucePan(al::HitSensor*, al::HitSensor*); +bool sendMsgLineDancerLink(al::HitSensor*, al::HitSensor*); +bool sendMsgLongPushSensorHit(al::HitSensor*, al::HitSensor*); +bool sendMsgLongPushBoxHit(al::HitSensor*, al::HitSensor*); +bool sendMsgGoldHammerAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgTrashBoxIn(al::HitSensor*, al::HitSensor*); +bool sendMsgTouchDoorDrumn(al::HitSensor*, al::HitSensor*); +bool sendMsgKuribo2DTouch(al::HitSensor*, al::HitSensor*); +bool sendMsgKuriboCollisionDamage(al::HitSensor*, al::HitSensor*); +bool sendMsgKuriboCollisionKill(al::HitSensor*, al::HitSensor*); +bool sendMsgKuriboFlick(al::HitSensor*, al::HitSensor*); +bool sendMsgKuriboTowerOn(al::HitSensor*, al::HitSensor*, uint); +bool sendMsgPartyPopperSoundAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgYokinBallAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgYoshiDirectEat(al::HitSensor*, al::HitSensor*); +bool sendMsgYoshiTongueAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgYoshiTongueEatBind(al::HitSensor*, al::HitSensor*, float*, float*, float*); +bool sendMsgYoshiTongueEatBindCancel(al::HitSensor*, al::HitSensor*); +bool sendMsgYoshiTongueEatBindFinish(al::HitSensor*, al::HitSensor*); +bool sendMsgYoshiTongueEatHomingTarget(al::HitSensor*, al::HitSensor*); +bool sendMsgYukimaruPush(al::HitSensor*, al::HitSensor*); +bool sendMsgPunchMachinePunchStraight(al::HitSensor*, al::HitSensor*); +bool sendMsgPunchMachinePunchHook(al::HitSensor*, al::HitSensor*); +bool sendMsgPunchMachinePunchUpper(al::HitSensor*, al::HitSensor*); +bool sendMsgMorningStarWarpStart(al::HitSensor*, al::HitSensor*); +bool sendMsgMorningStarWarpEnd(al::HitSensor*, al::HitSensor*); +bool sendMsgGemyAim(al::HitSensor*, al::HitSensor*); +bool sendMsgHammerBrosHammerEnemyAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgHammerBrosHammerHackAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgHammerBrosHammerSearch(al::HitSensor*, al::HitSensor*); +bool sendMsgHackBrosContact(al::HitSensor*, al::HitSensor*); +bool sendMsgMotorcycleAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgMotorcycleCollideParkingLot(al::HitSensor*, al::HitSensor*); +bool sendMsgMotorcycleDashAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgMotorcycleDashCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgWanwanEnemyAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgWanwanPush(al::HitSensor*, al::HitSensor*); +bool sendMsgWanwanReboundAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgWanwanBlockAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgWanwanHoleIn(al::HitSensor*, al::HitSensor*); +bool sendMsgWaterRoadIn(al::HitSensor*, al::HitSensor*); +bool sendMsgWaterRoadNear(al::HitSensor*, al::HitSensor*); +bool sendMsgBreedaSlap(al::HitSensor*, al::HitSensor*); +bool sendMsgBreedaPush(al::HitSensor*, al::HitSensor*); +bool sendMsgGamaneBullet(al::HitSensor*, al::HitSensor*); +bool sendMsgGamaneBulletThrough(al::HitSensor*, al::HitSensor*); +bool sendMsgGamaneBulletForCoinFlower(al::HitSensor*, al::HitSensor*); +bool sendMsgVolleyballNpcScareByEnemy(al::HitSensor*, al::HitSensor*); +bool sendMsgRocketFlowerExtension(al::HitSensor*, al::HitSensor*); +bool sendMsgWanderBossCameraRange(al::HitSensor*, al::HitSensor*); +bool sendMsgKuriboGirlAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgKuriboGirlLove(al::HitSensor*, al::HitSensor*); +bool sendMsgKuriboTop(al::HitSensor*, al::HitSensor*); +bool sendMsgTRexAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgTRexAttackCollideBody(al::HitSensor*, al::HitSensor*); +bool sendMsgTRexAttackCollideHead(al::HitSensor*, al::HitSensor*); +bool sendMsgTRexDashAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgTRexScrollPartsBreakWith(al::HitSensor*, al::HitSensor*); +bool sendMsgTsukkunNoTrace(al::HitSensor*, al::HitSensor*); +bool sendMsgTouchFireDrum2D(al::HitSensor*, al::HitSensor*); +bool sendMsgPropellerAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgTankExplosion(al::HitSensor*, al::HitSensor*); +bool sendMsgTankHackTrample(al::HitSensor*, al::HitSensor*); +bool sendMsgUtsuboAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgKillerAttackNoExplode(al::HitSensor*, al::HitSensor*); +bool sendMsgKillerMagnumAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgKillerMagnumHackAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgGabuzouAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgStackerRollingAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgStackerCapBoostAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgIgnoreIgnitionBomb(al::HitSensor*, al::HitSensor*); +bool sendMsgExplosionReflectBomb(al::HitSensor*, al::HitSensor*); +bool sendMsgGolemStampPress(al::HitSensor*, al::HitSensor*); +// bool sendMsgSwitchOnWithSaveRequest(al::LiveActor*, SaveObjInfo*); +bool sendMsgWanwanReboundAttackToCollided(al::LiveActor const*, al::HitSensor*); +bool sendMsgWanwanBlockAttackToCollided(al::LiveActor const*, al::HitSensor*); +bool sendMsgDigPointSmell(al::HitSensor*, al::HitSensor*, DigPoint*); +bool sendMsgMofumofuBodyChainExplode(al::HitSensor*, al::HitSensor*, int); +bool sendMsgMoonBasementRockThroughCollision(al::HitSensor*, al::HitSensor*, bool); +bool sendMsgFishingWait(al::HitSensor*, al::HitSensor*, al::HitSensor*); +bool sendMsgFishingUpImmediately(al::HitSensor*, al::HitSensor*, sead::Matrix34 const&, + sead::Vector3 const&, char const*); +bool sendMsgGunetterPush(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, float); +bool sendMsgTestPunch(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, int, int); +bool sendMsgTestPunchStrong(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, int, int); +bool sendMsgPunchGuard(al::HitSensor*, al::HitSensor*, int, int); +bool sendMsgTsukkunThrust(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, int, bool); +bool sendMsgTsukkunThrustSpin(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, int, + bool); +bool sendMsgTsukkunThrustReflect(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, int, + bool); +bool sendMsgTsukkunThrustCollide(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, int, + bool); +bool sendMsgTsukkunThrustHitReflectCollide(al::HitSensor*, al::HitSensor*, + sead::Vector3 const&, int, bool); +bool sendMsgTsukkunThrustReflectCollide(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + int, bool); +bool sendMsgTsukkunThrustHole(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&); +bool sendMsgTsukkunThroughCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgTsukkunHoldCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgTsukkunForceCancelCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgNetworkShootingShot(al::HitSensor*, al::HitSensor*, int); +bool sendMsgNetworkShootingChargeShot(al::HitSensor*, al::HitSensor*, int); +bool sendMsgRequestPlayerJumpBreakFloor(al::HitSensor*, al::HitSensor*); +bool sendMsgRequestPlayerJump(al::HitSensor*, al::HitSensor*, float); +bool sendMsgRequestPlayerTrampleJump(al::HitSensor*, al::HitSensor*, float); +bool sendMsgRequestPlayerSpinJump(al::HitSensor*, al::HitSensor*, float); +bool sendMsgRequestSphinxJump(al::HitSensor*, al::HitSensor*, float); +bool sendMsgIgnoreTouchTarget(al::HitSensor*, al::HitSensor*); +bool sendMsgIgnoreTouchTarget(al::ScreenPointer*, al::ScreenPointTarget*); +// bool sendMsgInitTouchTargetInfo(al::HitSensor*, al::HitSensor*, TouchTargetInfo*, +// sead::Vector3 const*); +// bool sendMsgInitTouchTargetInfo(al::ScreenPointer*, al::ScreenPointTarget*, TouchTargetInfo*, +// sead::Vector3 const*); +bool sendMsgCollisionImpulse(al::HitSensor*, al::HitSensor*, sead::Vector3*, + sead::Vector3 const&, float, sead::Vector3 const&, + float); +// bool sendMsgWhipHold(al::HitSensor*, al::HitSensor*, WhipTargetInfo*); +// bool sendMsgWhipBind(al::HitSensor*, al::HitSensor*, WhipTargetInfo*); +bool sendMsgWhipThrow(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgMagnet(al::HitSensor*, al::HitSensor*, bool); +bool sendMsgMagnetBulletAttack(al::HitSensor*, al::HitSensor*, float); +bool sendMsgDashPanel(al::HitSensor*, al::HitSensor*, int); +bool sendMsgTrampolineCrackJump(al::HitSensor*, al::HitSensor*, float, float); +bool sendMsgCapTouchWall(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&); +bool sendMsgCapRethrow(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&, sead::Vector3 const&); +bool sendMsgCapRethrowReturnOnly(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&, sead::Vector3 const&); +bool sendMsgCapChangeGiant(al::HitSensor*, al::HitSensor*, float, int); +bool sendMsgPackunEatCancel(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&); +bool sendMsgPackunEatEnd(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&); +bool sendMsgPackunEatStartFollow(al::HitSensor*, al::HitSensor*, sead::Vector3 const*); +bool sendMsgFireBlowerAttack(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&, float); +// bool sendMsgPaint(al::HitSensor *,al::HitSensor *,sead::Color4u8 const&,int,int); +bool sendMsgPaintTexture(al::HitSensor*, al::HitSensor*, int, float, int); +// bool sendMsgCheckPaintClear(al::HitSensor *,al::HitSensor *,sead::Color4u8 +// const&,sead::Vector3 const&,int); +bool sendMsgCheckPaintAlpha(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgByugoBlow(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgHackDirectStageInit(al::HitSensor*, al::HitSensor*, IUsePlayerHack*); +bool sendMsgObjSnapForce(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgCapBeamerBeam(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiAttackCollide(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiAttackNoEffect(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiAttackStrong(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiTouch(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiTrample(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiTrampleReflect(al::HitSensor*, al::HitSensor*); +bool sendMsgHosuiTrampleReflectHigh(al::HitSensor*, al::HitSensor*); +bool sendMsgBubbleAttack(al::HitSensor*, al::HitSensor*); +bool sendMsgBubbleAttackToPecho(al::HitSensor*, al::HitSensor*); +bool sendMsgBubbleReflectH(al::HitSensor*, al::HitSensor*); +bool sendMsgBubbleReflectV(al::HitSensor*, al::HitSensor*); +bool sendMsgBubbleWallTouch(al::HitSensor*, al::HitSensor*); +bool sendMsgBubbleGroundTouchTrigger(al::HitSensor*, al::HitSensor*); +bool sendMsgBubbleLauncherStart(al::HitSensor*, al::HitSensor*); +bool sendMsgSenobiPartsMove(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, float); +bool sendMsgStampTo2D(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgAirExplosion(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgPushVelocity(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgGhostRecordStartOk(al::HitSensor*, al::HitSensor*, char const*); +bool sendMsgSandGeyserRaise(al::HitSensor*, al::HitSensor*, float, float); +bool sendMsgRaceWait(al::LiveActor*); +bool sendMsgRaceStart(al::LiveActor*); +bool sendMsgRaceStop(al::LiveActor*); +bool sendMsgRaceReturnToCourse(al::LiveActor*, sead::Vector3 const&, + sead::Vector3 const&); +bool sendMsgPlayerLookAtPosition(al::HitSensor*, al::HitSensor*, sead::Vector3*); +bool sendMsgTargetMarkerPosition(al::HitSensor*, al::HitSensor*, sead::Vector3*); +bool sendMsgHackBlowJump(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, float); +bool sendMsgGolemStampPushV(al::HitSensor*, al::HitSensor*, float); +bool sendMsgGolemStampPushH(al::HitSensor*, al::HitSensor*, float); +bool sendMsgRequestPlayerWaitAnimDigPoint(al::HitSensor*, al::HitSensor*); +// bool sendMsgEventFlowScareCheck(al::HitSensor*, al::HitSensor*, al::EventFlowExecutor*); +bool sendMsgPlayerItemGetAll(al::HitSensor*, al::HitSensor*); +bool sendMsgPlayerItemGetAll2D(al::HitSensor*, al::HitSensor*); +bool sendMsgTRexAttackAll(al::HitSensor*, al::HitSensor*); +bool sendMsgSeedTouch(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&); +bool sendMsgSphinxRideAttackTouchThrough(al::HitSensor*, al::HitSensor*, + sead::Vector3 const&, sead::Vector3 const&); +bool sendMsgSphinxRideAttackTouch(al::HitSensor*, al::HitSensor*, sead::Vector3 const&, + sead::Vector3 const&); +bool sendMsgBlockUpperPunch2D(al::HitSensor*, al::HitSensor*); +bool sendMsgGotogotonOn(al::HitSensor*, al::HitSensor*); +// bool sendMsgGotogotonGetJumpPath(al::HitSensor*, al::HitSensor*, al::ParabolicPath*); +bool sendMsgGotogotonGoalExist(al::HitSensor*, al::HitSensor*); +// bool sendMsgGotogotonGoalMatch(al::HitSensor*, al::HitSensor*, GotogotonMark const*); +bool sendMsgBossMagmaBreathForce(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgBossMagmaCatchPlayer(al::HitSensor*, al::HitSensor*); +bool sendMsgBossMagmaReleasePlayer(al::HitSensor*, al::HitSensor*); +bool sendMsgBossMagmaDeadDemoStart(al::HitSensor*, al::HitSensor*); +bool sendMsgBossMagmaDeadDemoEnd(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgBossMagmaResetPos(al::HitSensor*, al::HitSensor*, sead::Vector3 const&); +bool sendMsgBossMagmaQueryToBubble(al::HitSensor*, al::HitSensor*); +bool sendMsgCheckFishingTarget(al::HitSensor*, al::HitSensor*, FishingFish const*); +bool sendMsgPushToPlayerAndKillVelocityToTarget(al::LiveActor*, al::HitSensor*, al::HitSensor*); +bool sendMsgPushToPlayerAndKillVelocityToTargetH(al::LiveActor*, al::HitSensor*, al::HitSensor*); +bool sendMsgInitCapTarget(al::HitSensor*, al::HitSensor*, CapTargetInfo const**); +bool sendMsgTransferHack(al::HitSensor*, al::HitSensor*); +bool sendMsgRequestTransferHack(al::HitSensor*, al::HitSensor*); +bool sendMsgInitHack(al::HitSensor*, al::HitSensor*); +bool sendMsgEndHack(al::HitSensor*, al::HitSensor*, HackEndParam const*); +bool sendMsgNpcScareByEnemy(al::HitSensor*, al::HitSensor*, int); +} // namespace rs diff --git a/include/sead/basis/seadNew.h b/include/sead/basis/seadNew.h new file mode 100644 index 0000000..c5b66a0 --- /dev/null +++ b/include/sead/basis/seadNew.h @@ -0,0 +1,70 @@ +#ifndef SEAD_NEW_H_ +#define SEAD_NEW_H_ + +#include +#include + +#include + +namespace sead +{ +class Heap; + +void AllocFailAssert(Heap* heap, size_t size, u32 alignment); + +#ifndef SEAD_DEBUG +inline void AllocFailAssert(Heap*, size_t, u32) {} +#endif +} // namespace sead + +void* operator new(size_t size); +void* operator new[](size_t size); +void* operator new(size_t size, const std::nothrow_t&) noexcept; +void* operator new[](size_t size, const std::nothrow_t&) noexcept; + +void* operator new(size_t size, s32 alignment); +void* operator new[](size_t size, s32 alignment); +void* operator new(size_t size, s32 alignment, const std::nothrow_t&) noexcept; +void* operator new[](size_t size, s32 alignment, const std::nothrow_t&) noexcept; + +void* operator new(size_t size, sead::Heap* heap, const std::nothrow_t&) noexcept; +void* operator new[](size_t size, sead::Heap* heap, const std::nothrow_t&) noexcept; + +void* operator new(size_t size, sead::Heap* heap, s32 alignment = sizeof(void*)); +void* operator new[](size_t size, sead::Heap* heap, s32 alignment = sizeof(void*)); +void* operator new(size_t size, sead::Heap* heap, s32 alignment, const std::nothrow_t&) noexcept; +void* operator new[](size_t size, sead::Heap* heap, s32 alignment, const std::nothrow_t&) noexcept; + +void operator delete(void* ptr) noexcept; +void operator delete[](void* ptr) noexcept; +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; + +void operator delete(void* ptr, s32); +void operator delete[](void* ptr, s32); +void operator delete(void* ptr, s32, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, s32, const std::nothrow_t&) noexcept; + +void operator delete(void* ptr, sead::Heap*, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, sead::Heap*, const std::nothrow_t&) noexcept; + +void operator delete(void* ptr, sead::Heap*, s32); +void operator delete[](void* ptr, sead::Heap*, s32); +void operator delete(void* ptr, sead::Heap*, s32, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr, sead::Heap*, s32, const std::nothrow_t&) noexcept; + +namespace sead +{ +inline u8* AllocBuffer(size_t size, Heap* heap, s32 alignment) +{ + u8* buffer = new (heap, alignment, std::nothrow) u8[size]; + if (buffer) + return buffer; + +#ifdef SEAD_DEBUG + sead::AllocFailAssert(heap, size, alignment); +#endif + return nullptr; +} +} // namespace sead +#endif // SEAD_NEW_H_ diff --git a/include/sead/basis/seadRawPrint.h b/include/sead/basis/seadRawPrint.h new file mode 100644 index 0000000..979f969 --- /dev/null +++ b/include/sead/basis/seadRawPrint.h @@ -0,0 +1,95 @@ +#pragma once + +#include + +#include + +#ifdef SEAD_DEBUG +#define SEAD_ASSERT_MSG(condition, message, ...) \ + do \ + { \ + if (!(condition)) \ + sead::system::HaltWithDetail(__FILE__, __LINE__, message, ##__VA_ARGS__); \ + } while (0) +#define SEAD_ASSERT(condition) \ + do \ + { \ + if (!(condition)) \ + sead::system::HaltWithDetailNoFormat(__FILE__, __LINE__, #condition); \ + } while (0) +#define SEAD_WARN(message, ...) \ + do \ + sead::system::Warning(__FILE__, __LINE__, message, ##__VA_ARGS__); \ + while (0) +#define SEAD_DEBUG_PRINT(format, ...) \ + do \ + sead::system::Print(format, ##__VA_ARGS__); \ + while (0) +#else +#define SEAD_ASSERT_MSG(condition, message, ...) \ + do \ + { \ + if (false) \ + { \ + static_cast(condition); \ + sead::system::detail::CheckFormat(message, ##__VA_ARGS__); \ + } \ + } while (0) +#define SEAD_ASSERT(condition) \ + do \ + { \ + if (false) \ + static_cast(condition); \ + } while (0) +#define SEAD_WARN(message, ...) \ + do \ + { \ + if (false) \ + sead::system::detail::CheckFormat(message, ##__VA_ARGS__); \ + } while (0) +#define SEAD_DEBUG_PRINT(format, ...) \ + do \ + { \ + if (false) \ + sead::system::detail::CheckFormat(format, ##__VA_ARGS__); \ + } while (0) +#endif + +namespace sead +{ +namespace system +{ +namespace detail +{ +// Dummy function whose only purpose is to trigger a format string check. +#ifdef __GNUC__ +[[maybe_unused]] [[gnu::format(printf, 1, 2)]] +#endif +inline void +CheckFormat(const char*, ...) +{ +} +} // namespace detail + +void Halt(); +#ifdef __GNUC__ +[[gnu::format(printf, 3, 4)]] +#endif +void HaltWithDetail(const char* file, int line, const char* msg, ...); +void HaltWithDetailNoFormat(const char* file, int line, const char* msg); +void DebugBreak(); + +#ifdef __GNUC__ +[[gnu::format(printf, 1, 2)]] +#endif +void Print(const char* format, ...); +void PrintV(const char* format, std::va_list); +void PrintString(const char* format, s32); + +#ifdef __GNUC__ +[[gnu::format(printf, 3, 4)]] +#endif +void Warning(const char* file, int line, const char* msg, ...); +void SetWarningEnable(bool enable); +} // namespace system +} // namespace sead diff --git a/include/sead/basis/seadTypes.h b/include/sead/basis/seadTypes.h new file mode 100644 index 0000000..f007d62 --- /dev/null +++ b/include/sead/basis/seadTypes.h @@ -0,0 +1,27 @@ +#ifndef SEAD_TYPES_H_ +#define SEAD_TYPES_H_ + +#ifdef cafe +#include +#else +#include +#include + +using u8 = std::uint8_t; +using u16 = std::uint16_t; +using u32 = std::uint32_t; +using u64 = std::uint64_t; + +using s8 = std::int8_t; +using s16 = std::int16_t; +using s32 = std::int32_t; +using s64 = std::int64_t; + +using f32 = float; +using f64 = double; + +using char16 = char16_t; +using size_t = std::size_t; +#endif + +#endif // SEAD_NEW_H_ diff --git a/include/sead/camera/PerspectiveProjection.h b/include/sead/camera/PerspectiveProjection.h new file mode 100644 index 0000000..c6d1bff --- /dev/null +++ b/include/sead/camera/PerspectiveProjection.h @@ -0,0 +1,11 @@ +#pragma once + +namespace sead { + class PerspectiveProjection { + public: + float getFovy(); + + unsigned char padding[0xA0]; + float fovy; + }; +} \ No newline at end of file diff --git a/include/sead/codec/seadBase64.h b/include/sead/codec/seadBase64.h new file mode 100644 index 0000000..83acda5 --- /dev/null +++ b/include/sead/codec/seadBase64.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace sead +{ +class Base64 +{ +public: + static void encode(char* dst, const void* src, size_t length, bool url_safe); + static bool decode(void* dst, size_t dst_size, const char* src, size_t src_size, + size_t* decoded_size); +}; +} // namespace sead diff --git a/include/sead/codec/seadHashCRC16.h b/include/sead/codec/seadHashCRC16.h new file mode 100644 index 0000000..cc1b006 --- /dev/null +++ b/include/sead/codec/seadHashCRC16.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace sead +{ +class HashCRC16 +{ +public: + struct Context + { + u32 hash = 0; + }; + + static u32 calcHash(const void* ptr, u32 size); + static u32 calcHashWithContext(Context* context, const void* ptr, u32 size); + + static u32 calcStringHash(const char* str); + static u32 calcStringHash(const SafeString& str) { return calcStringHash(str.cstr()); } + static u32 calcStringHashWithContext(Context* context, const char* str); + static u32 calcStringHashWithContext(Context* context, const SafeString& str) + { + return calcStringHashWithContext(context, str.cstr()); + } + + static void initialize(); + +private: + static u16 sTable[256]; + static bool sInitialized; +}; +} // namespace sead diff --git a/include/sead/codec/seadHashCRC32.h b/include/sead/codec/seadHashCRC32.h new file mode 100644 index 0000000..1f2675b --- /dev/null +++ b/include/sead/codec/seadHashCRC32.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace sead +{ +class HashCRC32 +{ +public: + struct Context + { + u32 hash = -1; + }; + + static u32 calcHash(const void* ptr, u32 size); + static u32 calcHashWithContext(Context* context, const void* ptr, u32 size); + + static u32 calcStringHash(const char* str); + static u32 calcStringHash(const SafeString& str) { return calcStringHash(str.cstr()); } + static u32 calcStringHashWithContext(Context* context, const char* str); + static u32 calcStringHashWithContext(Context* context, const SafeString& str) + { + return calcStringHashWithContext(context, str.cstr()); + } + + static void initialize(); + +private: + static u32 sTable[256]; + static bool sInitialized; +}; +} // namespace sead diff --git a/include/sead/container/seadBuffer.h b/include/sead/container/seadBuffer.h new file mode 100644 index 0000000..eb058e6 --- /dev/null +++ b/include/sead/container/seadBuffer.h @@ -0,0 +1,442 @@ +#ifndef SEAD_BUFFER_H_ +#define SEAD_BUFFER_H_ + +#include +#include + +#include +#include +#include +#include +#include + +namespace sead +{ +class Heap; + +template +class Buffer +{ +public: + Buffer() : mSize(0), mBuffer(NULL) {} + Buffer(s32 size, T* buffer) : mSize(size), mBuffer(buffer) {} + template + Buffer(T (&array)[N]) : Buffer(N, array) + { + } + + Buffer(const Buffer& other) { *this = other; } + + Buffer& operator=(const Buffer& other) + { + mSize = other.mSize; + mBuffer = other.mBuffer; + return *this; + } + + class iterator + { + public: + explicit iterator(T* buffer, s32 index = 0) : mIndex(index), mBuffer(buffer) {} + bool operator==(const iterator& rhs) const + { + return mIndex == rhs.mIndex && mBuffer == rhs.mBuffer; + } + bool operator!=(const iterator& rhs) const { return !operator==(rhs); } + iterator& operator++() + { + ++mIndex; + return *this; + } + T& operator*() const { return mBuffer[mIndex]; } + T* operator->() const { return &mBuffer[mIndex]; } + s32 getIndex() const { return mIndex; } + + private: + s32 mIndex; + T* mBuffer; + }; + + class constIterator + { + public: + explicit constIterator(const T* buffer, s32 index = 0) : mIndex(index), mBuffer(buffer) {} + bool operator==(const constIterator& rhs) const + { + return mIndex == rhs.mIndex && mBuffer == rhs.mBuffer; + } + bool operator!=(const constIterator& rhs) const { return !operator==(rhs); } + constIterator& operator++() + { + ++mIndex; + return *this; + } + const T& operator*() const { return mBuffer[mIndex]; } + const T* operator->() const { return &mBuffer[mIndex]; } + s32 getIndex() const { return mIndex; } + + private: + s32 mIndex; + const T* mBuffer; + }; + + iterator begin() { return iterator(mBuffer); } + iterator begin(s32 idx) + { + if (u32(size()) < u32(idx)) + { + SEAD_ASSERT_MSG(false, "range over [0,%d] : %d", size(), idx); + return end(); + } + return iterator(mBuffer, idx); + } + + constIterator begin() const { return constIterator(mBuffer); } + constIterator begin(s32 idx) const + { + if (u32(size()) < u32(idx)) + { + SEAD_ASSERT_MSG(false, "range over [0,%d] : %d", size(), idx); + return end(); + } + return constIterator(mBuffer, idx); + } + + iterator end() { return iterator(mBuffer, mSize); } + constIterator end() const { return constIterator(mBuffer, mSize); } + + class reverseIterator + { + public: + explicit reverseIterator(T* buffer, s32 index = 0) : mIndex(index), mBuffer(buffer) {} + bool operator==(const reverseIterator& rhs) const { return mIndex == rhs.mIndex; } + bool operator!=(const reverseIterator& rhs) const { return !operator==(rhs); } + reverseIterator& operator++() + { + --mIndex; + return *this; + } + T& operator*() const { return mBuffer[mIndex]; } + T* operator->() const { return &mBuffer[mIndex]; } + s32 getIndex() const { return mIndex; } + + private: + s32 mIndex; + T* mBuffer; + }; + + reverseIterator rbegin() { return reverseIterator(mBuffer, mSize - 1); } + reverseIterator rbegin(s32 index) { return reverseIterator(mBuffer, index); } + reverseIterator rend() { return reverseIterator(mBuffer, -1); } + + void allocBuffer(s32 size, s32 alignment) + { + SEAD_ASSERT(mBuffer == nullptr); + if (size > 0) + { + T* buffer = new (alignment) T[size]; + if (buffer) + { + mSize = size; + mBuffer = buffer; + SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)), + "don't set alignment for a class with destructor"); + } + } + else + { + SEAD_ASSERT_MSG(false, "size[%d] must be larger than zero", size); + } + } + + void allocBuffer(s32 size, Heap* heap, s32 alignment = sizeof(void*)) + { + SEAD_ASSERT(mBuffer == nullptr); + if (size > 0) + { + T* buffer = new (heap, alignment) T[size]; + if (buffer) + { + mSize = size; + mBuffer = buffer; + SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)), + "don't set alignment for a class with destructor"); + } + } + else + { + SEAD_ASSERT_MSG(false, "size[%d] must be larger than zero", size); + } + } + + bool tryAllocBuffer(s32 size, s32 alignment = sizeof(void*)) + { + SEAD_ASSERT(mBuffer == nullptr); + if (size > 0) + { + T* buffer = new (alignment, std::nothrow) T[size]; + if (buffer) + { + mSize = size; + mBuffer = buffer; + SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)), + "don't set alignment for a class with destructor"); + return true; + } + return false; + } + SEAD_ASSERT_MSG(false, "size[%d] must be larger than zero", size); + return false; + } + + bool tryAllocBuffer(s32 size, Heap* heap, s32 alignment = sizeof(void*)) + { + SEAD_ASSERT(mBuffer == nullptr); + if (size > 0) + { + T* buffer = new (heap, alignment, std::nothrow) T[size]; + if (buffer) + { + mSize = size; + mBuffer = buffer; + SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)), + "don't set alignment for a class with destructor"); + return true; + } + return false; + } + SEAD_ASSERT_MSG(false, "size[%d] must be larger than zero", size); + return false; + } + + inline bool allocBufferAssert(s32 size, Heap* heap, s32 alignment = sizeof(void*)) + { + if (tryAllocBuffer(size, heap, alignment)) + return true; + AllocFailAssert(heap, sizeof(T) * size, alignment); + return false; + } + + void freeBuffer() + { + if (mBuffer) + { + delete[] mBuffer; + mBuffer = nullptr; + mSize = 0; + } + } + + void setBuffer(s32 size, T* bufferptr) + { + if (size < 1) + { + SEAD_ASSERT_MSG(false, "size[%d] must be larger than zero", size); + return; + } + if (!bufferptr) + { + SEAD_ASSERT_MSG(false, "bufferptr is null"); + return; + } + mSize = size; + mBuffer = bufferptr; + } + + bool isBufferReady() const { return mBuffer != nullptr; } + + bool isIndexValid(s32 idx) const { return u32(idx) < u32(mSize); } + + T& operator()(s32 idx) { return *unsafeGet(idx); } + const T& operator()(s32 idx) const { return *unsafeGet(idx); } + + T& operator[](s32 idx) + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d]", idx, mSize); + return mBuffer[0]; + } + return mBuffer[idx]; + } + + const T& operator[](s32 idx) const + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d]", idx, mSize); + return mBuffer[0]; + } + return mBuffer[idx]; + } + + T* get(s32 idx) + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d]", idx, mSize); + return nullptr; + } + return &mBuffer[idx]; + } + + const T* get(s32 idx) const + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d]", idx, mSize); + return nullptr; + } + return &mBuffer[idx]; + } + + T* unsafeGet(s32 idx) + { + SEAD_ASSERT_MSG(u32(idx) < u32(mSize), "index exceeded [%d/%d]", idx, mSize); + return &mBuffer[idx]; + } + const T* unsafeGet(s32 idx) const + { + SEAD_ASSERT_MSG(u32(idx) < u32(mSize), "index exceeded [%d/%d]", idx, mSize); + return &mBuffer[idx]; + } + + T& front() { return mBuffer[0]; } + const T& front() const { return mBuffer[0]; } + + T& back() { return mBuffer[mSize - 1]; } + const T& back() const { return mBuffer[mSize - 1]; } + + s32 size() const { return mSize; } + s32 getSize() const { return mSize; } + + T* getBufferPtr() { return mBuffer; } + const T* getBufferPtr() const { return mBuffer; } + + u32 getByteSize() const { return mSize * sizeof(T); } + + void fill(const T& v) + { + for (s32 i = 0; i < mSize; ++i) + mBuffer[i] = v; + } + + using CompareCallback = s32 (*)(const T* lhs, const T* rhs); + + s32 binarySearch(const T& item) const { return binarySearch(item, compareT); } + + s32 binarySearch(const T& item, CompareCallback cmp) const + { + if (mSize == 0) + return -1; + + s32 a = 0; + s32 b = mSize - 1; + while (a < b) + { + const s32 m = (a + b) / 2; + const s32 c = cmp(&mBuffer[m], &item); + if (c == 0) + return m; + if (c < 0) + a = m + 1; + else + b = m; + } + + if (cmp(&mBuffer[a], &item) == 0) + return a; + + return -1; + } + + template + s32 binarySearch(const Key& key, s32 (*cmp)(const T& item, const Key& key)) const + { + if (mSize == 0) + return -1; + + s32 a = 0; + s32 b = mSize - 1; + while (a < b) + { + const s32 m = (a + b) / 2; + const s32 c = cmp(mBuffer[m], key); + if (c == 0) + return m; + if (c < 0) + a = m + 1; + else + b = m; + } + + if (cmp(mBuffer[a], key) == 0) + return a; + + return -1; + } + + template + s32 binarySearchC(CustomCompareCallback cmp) const + { + if (mSize == 0) + return -1; + + s32 a = 0; + s32 b = mSize - 1; + while (a < b) + { + const s32 m = (a + b) / 2; + const s32 c = cmp(mBuffer[m]); + if (c == 0) + return m; + if (c < 0) + a = m + 1; + else + b = m; + } + + if (cmp(mBuffer[a]) == 0) + return a; + + return -1; + } + + /// Sort elements with indices in [start_idx .. end_idx] using heapsort. + void heapSort(s32 start_idx, s32 end_idx) + { + if (start_idx >= mSize || end_idx >= mSize || end_idx - start_idx < 1) + return; + // FIXME: Nintendo implemented heap sort manually without using + std::make_heap(mBuffer + start_idx, mBuffer + end_idx); + std::sort_heap(mBuffer + start_idx, mBuffer + end_idx); + } + + /// Sort elements with indices in [start_idx .. end_idx] using heapsort. + void heapSort(s32 start_idx, s32 end_idx, CompareCallback cmp) + { + if (start_idx >= mSize || end_idx >= mSize || end_idx - start_idx < 1) + return; + // FIXME: Nintendo implemented heap sort manually without using + const auto cmp_ = [cmp](const T& a, const T& b) { return cmp(&a, &b) < 0; }; + std::make_heap(mBuffer + start_idx, mBuffer + end_idx, cmp_); + std::sort_heap(mBuffer + start_idx, mBuffer + end_idx, cmp_); + } + +protected: + static s32 compareT(const T* lhs, const T* rhs) + { + if (*lhs < *rhs) + return -1; + if (*rhs < *lhs) + return 1; + return 0; + } + + s32 mSize; + T* mBuffer; +}; + +} // namespace sead + +#endif // SEAD_BUFFER_H_ diff --git a/include/sead/container/seadFreeList.h b/include/sead/container/seadFreeList.h new file mode 100644 index 0000000..93e2368 --- /dev/null +++ b/include/sead/container/seadFreeList.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include "basis/seadRawPrint.h" +#include "basis/seadTypes.h" + +namespace sead +{ +class FreeList +{ +public: + void setWork(void* work, s32 elem_size, s32 num); + void reset(); + + void* alloc(); + void free(void* ptr); + + void* getFree() const { return mFree; } + void* work() const { return mWork; } + + static const size_t cPtrSize = sizeof(void*); + +private: + struct Node + { + Node* nextFree; + }; + + Node* mFree = nullptr; + void* mWork = nullptr; +}; + +inline void FreeList::setWork(void* work, s32 elem_size, s32 num) +{ + SEAD_ASSERT(work); + SEAD_ASSERT(elem_size > 0 && elem_size % cPtrSize == 0); + SEAD_ASSERT(num > 0); + + const s32 idx_multiplier = elem_size / cPtrSize; + void** const ptrs = new (work) void*[num * idx_multiplier]; + + mFree = new (work) Node; + + // Create the linked list. + for (s32 i = 0; i < num - 1; ++i) + new (&ptrs[i * idx_multiplier]) Node{new (&ptrs[(i + 1) * idx_multiplier]) Node}; + + new (&ptrs[(num - 1) * idx_multiplier]) Node{nullptr}; + + mWork = work; +} + +inline void FreeList::reset() +{ + mFree = nullptr; + mWork = nullptr; +} + +inline void* FreeList::alloc() +{ + if (!mFree) + return nullptr; + + void* ptr = mFree; + mFree = mFree->nextFree; + return ptr; +} + +inline void FreeList::free(void* ptr) +{ + mFree = new (ptr) Node{mFree}; +} +} // namespace sead diff --git a/include/sead/container/seadListImpl.h b/include/sead/container/seadListImpl.h new file mode 100644 index 0000000..c78979b --- /dev/null +++ b/include/sead/container/seadListImpl.h @@ -0,0 +1,121 @@ +#ifndef SEAD_LIST_IMPL_H_ +#define SEAD_LIST_IMPL_H_ + +#include + +namespace sead +{ +class Random; + +class ListNode +{ +public: + ListNode* next() const { return mNext; } + ListNode* prev() const { return mPrev; } + bool isLinked() const { return mNext || mPrev; } + +private: + friend class ListImpl; + + void init_() { *this = {}; } + void insertBack_(ListNode* node); + void insertFront_(ListNode* node); + void erase_(); + + ListNode* mPrev = nullptr; + ListNode* mNext = nullptr; +}; + +class ListImpl +{ +public: + __attribute__((always_inline)) ListImpl() : mStartEnd(), mCount(0) + { + mStartEnd.mNext = &mStartEnd; + mStartEnd.mPrev = &mStartEnd; + } + + bool isEmpty() const { return mCount == 0; } + s32 size() const { return mCount; } + + void reverse(); + void shuffle(); + void shuffle(Random* random); + bool checkLinks() const; + +protected: + using CompareCallbackImpl = int (*)(const void*, const void*); + + template + void sort(s32 offset, const ComparePredicate& cmp) + { + this->mergeSort(offset, cmp); + } + + template + void mergeSort(s32 offset, const ComparePredicate& cmp) + { + this->mergeSortImpl_(mStartEnd.mNext, mStartEnd.mPrev, size(), offset, + cmp); + } + + void pushBack(ListNode* item) + { + mStartEnd.insertFront_(item); + ++mCount; + } + + void pushFront(ListNode* item) + { + mStartEnd.insertBack_(item); + ++mCount; + } + + ListNode* popBack(); + ListNode* popFront(); + + void insertBefore(ListNode* node, ListNode* node_to_insert) + { + node->insertFront_(node_to_insert); + ++mCount; + } + + void insertAfter(ListNode* node, ListNode* node_to_insert) + { + node->insertBack_(node_to_insert); + ++mCount; + } + + void erase(ListNode* item) + { + item->erase_(); + --mCount; + } + + ListNode* front() const { return mCount >= 1 ? mStartEnd.mNext : nullptr; } + ListNode* back() const { return mCount >= 1 ? mStartEnd.mPrev : nullptr; } + ListNode* nth(int n) const; + s32 indexOf(const ListNode*) const; + + void swap(ListNode* n1, ListNode* n2); + void moveAfter(ListNode* basis, ListNode* n); + void moveBefore(ListNode* basis, ListNode* n); + + ListNode* find(const void* ptr, s32 offset, CompareCallbackImpl cmp) const; + void uniq(s32 offset, CompareCallbackImpl cmp); + + void clear(); + + // FIXME: this should take an rvalue reference for predicate. + template + static void mergeSortImpl_(ListNode* front, ListNode* back, s32 num, s32 offset, + const ComparePredicate& predicate); + +protected: + ListNode mStartEnd; + s32 mCount; +}; + +} // namespace sead + +#endif // SEAD_LIST_IMPL_H_ diff --git a/include/sead/container/seadObjArray.h b/include/sead/container/seadObjArray.h new file mode 100644 index 0000000..df2b4de --- /dev/null +++ b/include/sead/container/seadObjArray.h @@ -0,0 +1,271 @@ +#pragma once + +#include +#include "basis/seadNew.h" +#include "basis/seadRawPrint.h" +#include "container/seadFreeList.h" +#include "container/seadPtrArray.h" + +namespace sead +{ +/// An ObjArray is a container that allocates elements using a FreeList and also keeps an array of +/// pointers for fast access to each element. +template +class ObjArray : public PtrArrayImpl +{ +public: + ObjArray() = default; + ObjArray(s32 max_num, void* buf) { setBuffer(max_num, buf); } + + void allocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) + { + SEAD_ASSERT(mPtrs == nullptr); + + if (capacity < 1) + { + SEAD_ASSERT_MSG(false, "capacity[%d] must be larger than zero", capacity); + return; + } + + setBuffer(capacity, + new (heap, alignment, std::nothrow) u8[calculateWorkBufferSize(capacity)]); + } + + bool tryAllocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) + { + SEAD_ASSERT(mPtrs == nullptr); + + if (capacity < 1) + { + SEAD_ASSERT_MSG(false, "capacity[%d] must be larger than zero", capacity); + return false; + } + + auto* buf = new (heap, alignment, std::nothrow) u8[calculateWorkBufferSize(capacity)]; + if (!buf) + return false; + + setBuffer(capacity, buf); + return true; + } + + void setBuffer(s32 max_num, void* buf) + { + if (!buf) + { + SEAD_ASSERT_MSG(false, "buf is null"); + return; + } + + mFreeList.setWork(buf, ElementSize, max_num); + PtrArrayImpl::setBuffer(max_num, reinterpret_cast(buf) + ElementSize * max_num); + } + + void freeBuffer() + { + if (!isBufferReady()) + return; + + clear(); + + if (mFreeList.work()) + delete[] static_cast(mFreeList.work()); + + mFreeList.reset(); + mPtrs = nullptr; + mPtrNumMax = 0; + } + + T* at(s32 pos) const { return static_cast(PtrArrayImpl::at(pos)); } + T* unsafeAt(s32 pos) const { return static_cast(PtrArrayImpl::unsafeAt(pos)); } + T* operator()(s32 pos) const { return unsafeAt(pos); } + T* operator[](s32 pos) const { return at(pos); } + + // XXX: Does this use at()? + T* front() const { return at(0); } + T* back() const { return at(mPtrNum - 1); } + + void pushBack(const T& item) + { + if (isFull()) + SEAD_ASSERT_MSG(false, "buffer full."); + else + PtrArrayImpl::pushBack(alloc(item)); + } + + template + T* emplaceBack(Args&&... args) + { + if (isFull()) + { + SEAD_ASSERT_MSG(false, "buffer full."); + return nullptr; + } + T* item = new (mFreeList.alloc()) T(std::forward(args)...); + PtrArrayImpl::pushBack(item); + return item; + } + + void insert(s32 pos, const T& item) { PtrArrayImpl::insert(pos, alloc(item)); } + + void erase(int index) { erase(index, 1); } + + void erase(int index, int count) + { + if (index + count <= size()) + { + for (int i = index; i < index + count; ++i) + { + auto* ptr = unsafeAt(i); + ptr->~T(); + mFreeList.free(ptr); + } + } + PtrArrayImpl::erase(index, count); + } + + void clear() + { + for (s32 i = 0; i < mPtrNum; ++i) + { + auto* ptr = unsafeAt(i); + ptr->~T(); + mFreeList.free(ptr); + } + mPtrNum = 0; + } + + using CompareCallback = s32 (*)(const T*, const T*); + + void sort() { sort(compareT); } + void sort(CompareCallback cmp) { PtrArrayImpl::sort_(cmp); } + void heapSort() { heapSort(compareT); } + void heapSort(CompareCallback cmp) { PtrArrayImpl::heapSort_(cmp); } + + bool equal(const ObjArray& other, CompareCallback cmp) const + { + return PtrArrayImpl::equal(other, cmp); + } + + s32 compare(const ObjArray& other, CompareCallback cmp) const + { + return PtrArrayImpl::compare(other, cmp); + } + + s32 binarySearch(const T* ptr) const { return PtrArrayImpl::binarySearch(ptr, compareT); } + s32 binarySearch(const T* ptr, CompareCallback cmp) const + { + return PtrArrayImpl::binarySearch(ptr, cmp); + } + + bool operator==(const ObjArray& other) const { return equal(other, compareT); } + bool operator!=(const ObjArray& other) const { return !(*this == other); } + bool operator<(const ObjArray& other) const { return compare(other) < 0; } + bool operator<=(const ObjArray& other) const { return compare(other) <= 0; } + bool operator>(const ObjArray& other) const { return compare(other) > 0; } + bool operator>=(const ObjArray& other) const { return compare(other) >= 0; } + + void uniq() { PtrArrayImpl::uniq(compareT); } + void uniq(CompareCallback cmp) { PtrArrayImpl::uniq(cmp); } + + class iterator + { + public: + iterator(T* const* pptr) : mPPtr{pptr} {} + bool operator==(const iterator& other) const { return mPPtr == other.mPPtr; } + bool operator!=(const iterator& other) const { return !(*this == other); } + iterator& operator++() + { + ++mPPtr; + return *this; + } + T& operator*() const { return **mPPtr; } + T* operator->() const { return *mPPtr; } + + private: + T* const* mPPtr; + }; + + iterator begin() const { return iterator(data()); } + iterator end() const { return iterator(data() + mPtrNum); } + + class constIterator + { + public: + constIterator(const T* const* pptr) : mPPtr{pptr} {} + bool operator==(const constIterator& other) const { return mPPtr == other.mPPtr; } + bool operator!=(const constIterator& other) const { return !(*this == other); } + constIterator& operator++() + { + ++mPPtr; + return *this; + } + const T& operator*() const { return **mPPtr; } + const T* operator->() const { return *mPPtr; } + + private: + const T* const* mPPtr; + }; + + constIterator constBegin() const { return constIterator(data()); } + constIterator constEnd() const { return constIterator(data() + mPtrNum); } + + T** data() const { return reinterpret_cast(mPtrs); } + +private: + union Node + { + void* next_node; + T elem; + }; + +public: + static constexpr size_t ElementSize = sizeof(Node); + + static constexpr size_t calculateWorkBufferSize(size_t n) + { + return n * (ElementSize + sizeof(T*)); + } + +protected: + T* alloc(const T& item) + { + void* storage = mFreeList.alloc(); + if (!storage) + return nullptr; + return new (storage) T(item); + } + + static int compareT(const void* a_, const void* b_) + { + const T* a = static_cast(a_); + const T* b = static_cast(b_); + if (*a < *b) + return -1; + if (*b < *a) + return 1; + return 0; + } + + sead::FreeList mFreeList; +}; + +template +class FixedObjArray : public ObjArray +{ +public: + FixedObjArray() : ObjArray(N, &mWork) {} + + // These do not make sense for a *fixed* array. + void setBuffer(s32 ptrNumMax, void* buf) = delete; + void allocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; + bool tryAllocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; + void freeBuffer() = delete; + +private: + std::aligned_storage_t::calculateWorkBufferSize(N), + std::max(alignof(T), alignof(T*))> + mWork; +}; + +} // namespace sead diff --git a/include/sead/container/seadObjList.h b/include/sead/container/seadObjList.h new file mode 100644 index 0000000..ad719eb --- /dev/null +++ b/include/sead/container/seadObjList.h @@ -0,0 +1,245 @@ +#pragma once + +#include +#include "basis/seadNew.h" +#include "basis/seadRawPrint.h" +#include "basis/seadTypes.h" +#include "container/seadFreeList.h" +#include "container/seadListImpl.h" +#include "prim/seadPtrUtil.h" + +namespace sead +{ +template +class ObjList : public ListImpl +{ +public: + ObjList() = default; + ObjList(s32 max_num, void* buf) { setBuffer(max_num, buf); } + + void allocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) + { + if (capacity < 1) + return; + + setBuffer(capacity, + new (heap, alignment, std::nothrow) u8[calculateWorkBufferSize(capacity)]); + } + + bool tryAllocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) + { + if (capacity < 1) + return false; + + auto* buf = new (heap, alignment, std::nothrow) u8[calculateWorkBufferSize(capacity)]; + if (!buf) + return false; + + setBuffer(capacity, buf); + return true; + } + + void setBuffer(s32 max_num, void* buf) + { + if (!buf) + { + SEAD_ASSERT_MSG(false, "buf is null"); + return; + } + + mFreeList.setWork(buf, ElementSize, max_num); + mMaxNum = max_num; + } + + void freeBuffer() + { + if (!isBufferReady()) + return; + + clear(); + + if (mFreeList.work()) + delete[] static_cast(mFreeList.work()); + + mMaxNum = 0; + mFreeList.reset(); + } + + bool isBufferReady() const { return mFreeList.work() != nullptr; } + + bool isFull() const { return size() >= mMaxNum; } + + T* front() const { return listNodeToObjWithNullCheck(ListImpl::front()); } + T* back() const { return listNodeToObjWithNullCheck(ListImpl::back()); } + + T popBack() + { + auto* item = back(); + if (!item) + return {}; + + T copy = *item; + erase(item); + return copy; + } + + T popFront() + { + auto* item = front(); + if (!item) + return {}; + + T copy = *item; + erase(item); + return copy; + } + + template + T* emplaceBack(Args&&... args) + { + if (isFull()) + { + SEAD_ASSERT_MSG(false, "buffer full."); + return nullptr; + } + Node* item = new (mFreeList.alloc()) Node{T{std::forward(args)...}, {}}; + ListImpl::pushBack(&item->node); + return &item->item; + } + + void erase(T* item) + { + ListImpl::erase(objToListNode(item)); + item->~T(); + mFreeList.free(item); + } + + void clear() + { + ListNode* node = mStartEnd.next(); + while (node != &mStartEnd) + { + // Fetch the next pointer before erasing the item from the linked list. + ListNode* next = node->next(); + ListImpl::erase(node); + + auto* item = listNodeToObj(node); + item->~T(); + mFreeList.free(item); + + node = next; + } + } + + T* prev(const T* obj) const + { + ListNode* prev_node = objToListNode(obj)->prev(); + if (prev_node == &mStartEnd) + return nullptr; + return listNodeToObj(prev_node); + } + + T* next(const T* obj) const + { + ListNode* next_node = objToListNode(obj)->next(); + if (next_node == &mStartEnd) + return nullptr; + return listNodeToObj(next_node); + } + + T* nth(s32 n) const { return listNodeToObjWithNullCheck(ListImpl::nth(n)); } + + s32 indexOf(const T* obj) const { return ListImpl::indexOf(objToListNode(obj)); } + + bool isNodeLinked(const T* obj) const { return objToListNode(obj)->isLinked(); } + + class iterator + { + public: + explicit iterator(T* ptr) : mPtr{ptr} {} + bool operator==(const iterator& other) const { return mPtr == other.mPtr; } + bool operator!=(const iterator& other) const { return !operator==(other); } + iterator& operator++() + { + constexpr auto offset = Node::getListNodeOffset(); + auto* node = static_cast(PtrUtil::addOffset(mPtr, offset))->next(); + mPtr = static_cast(PtrUtil::addOffset(node, -offset)); + return *this; + } + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr; } + + private: + T* mPtr; + }; + + iterator begin() const { return iterator(listNodeToObj(mStartEnd.next())); } + iterator end() const { return iterator(listNodeToObj(const_cast(&mStartEnd))); } + iterator begin(T* ptr) const { return iterator(ptr); } + + static constexpr size_t calculateWorkBufferSize(size_t n) { return n * ElementSize; } + +private: + struct Node + { + static constexpr auto getListNodeOffset() { return offsetof(Node, node); } + T item; + ListNode node; + }; + static_assert(offsetof(Node, item) == 0, "item must be at offset 0 in Node"); + + ListNode* objToListNode(T* obj) const + { + return static_cast(PtrUtil::addOffset(obj, Node::getListNodeOffset())); + } + + const ListNode* objToListNode(const T* obj) const + { + return static_cast(PtrUtil::addOffset(obj, Node::getListNodeOffset())); + } + + T* listNodeToObj(ListNode* node) const + { + return static_cast(PtrUtil::addOffset(node, -Node::getListNodeOffset())); + } + + const T* listNodeToObj(const ListNode* node) const + { + return static_cast(PtrUtil::addOffset(node, -Node::getListNodeOffset())); + } + + T* listNodeToObjWithNullCheck(ListNode* node) const + { + return node ? listNodeToObj(node) : nullptr; + } + + const T* listNodeToObjWithNullCheck(const ListNode* node) const + { + return node ? listNodeToObj(node) : nullptr; + } + + static constexpr size_t ElementSize = std::max(sizeof(Node), FreeList::cPtrSize); + + sead::FreeList mFreeList; + s32 mMaxNum = 0; +}; + +template +class FixedObjList : public ObjList +{ +public: + FixedObjList() : ObjList(N, &mWork) {} + + // These do not make sense for a *fixed* array. + void setBuffer(s32 ptrNumMax, void* buf) = delete; + void allocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; + bool tryAllocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; + void freeBuffer() = delete; + +private: + std::aligned_storage_t::calculateWorkBufferSize(N), + std::max(alignof(T), alignof(T*))> + mWork; +}; + +} // namespace sead diff --git a/include/sead/container/seadOffsetList.h b/include/sead/container/seadOffsetList.h new file mode 100644 index 0000000..76ec2cb --- /dev/null +++ b/include/sead/container/seadOffsetList.h @@ -0,0 +1,219 @@ +#ifndef SEAD_OFFSET_LIST_H_ +#define SEAD_OFFSET_LIST_H_ + +#include +#include +#include +#include + +namespace sead +{ +template +class OffsetList : public ListImpl +{ +public: + OffsetList() = default; + + void initOffset(s32 offset) { mOffset = offset; } + + void clear() { ListImpl::clear(); } + + void pushBack(T* item) + { + SEAD_ASSERT(mOffset >= 0); + ListImpl::pushBack(objToListNode(item)); + } + + void pushFront(T* item) + { + SEAD_ASSERT(mOffset >= 0); + ListImpl::pushFront(objToListNode(item)); + } + + T* popBack() { return listNodeToObjWithNullCheck(ListImpl::popBack()); } + + T* popFront() { return listNodeToObjWithNullCheck(ListImpl::popFront()); } + + void insertBefore(const T* obj, T* obj_to_insert) + { + ListImpl::insertBefore(const_cast(objToListNode(obj)), + objToListNode(obj_to_insert)); + } + void insertAfter(const T* obj, T* obj_to_insert) + { + ListImpl::insertAfter(const_cast(objToListNode(obj)), + objToListNode(obj_to_insert)); + } + + void erase(T* item) { ListImpl::erase(objToListNode(item)); } + + T* front() const { return listNodeToObjWithNullCheck(ListImpl::front()); } + + T* back() const { return listNodeToObjWithNullCheck(ListImpl::back()); } + + T* prev(const T* obj) const + { + ListNode* prev_node = objToListNode(obj)->prev(); + if (prev_node == &mStartEnd) + return nullptr; + return listNodeToObj(prev_node); + } + + T* next(const T* obj) const + { + ListNode* next_node = objToListNode(obj)->next(); + if (next_node == &mStartEnd) + return nullptr; + return listNodeToObj(next_node); + } + + T* nth(s32 n) const { return listNodeToObjWithNullCheck(ListImpl::nth(n)); } + + s32 indexOf(const T* obj) const { return ListImpl::indexOf(objToListNode(obj)); } + + bool isNodeLinked(const T* obj) const { return objToListNode(obj)->isLinked(); } + + void swap(T* obj1, T* obj2) { ListImpl::swap(objToListNode(obj1), objToListNode(obj2)); } + void moveAfter(T* basis, T* obj) + { + ListImpl::moveAfter(objToListNode(basis), objToListNode(obj)); + } + void moveBefore(T* basis, T* obj) + { + ListImpl::moveBefore(objToListNode(basis), objToListNode(obj)); + } + + using CompareCallback = int (*)(const T*, const T*); + + void sort() { sort(compareT); } + void sort(CompareCallback cmp) { ListImpl::sort(mOffset, cmp); } + void mergeSort() { mergeSort(compareT); } + void mergeSort(CompareCallback cmp) { ListImpl::mergeSort(mOffset, cmp); } + + T* find(const T* obj) const { return find(obj, compareT); } + T* find(const T* obj, CompareCallback cmp) const + { + return listNodeToObj(ListImpl::find(obj, mOffset, cmp)); + } + + void uniq() { uniq(compareT); } + void uniq(CompareCallback cmp) { ListImpl::uniq(mOffset, cmp); } + + class iterator + { + public: + iterator(T* ptr, s32 offset) : mPtr{ptr}, mOffset{offset} {} + bool operator==(const iterator& other) const { return mPtr == other.mPtr; } + bool operator!=(const iterator& other) const { return !(*this == other); } + iterator& operator++() + { + ListNode* node = static_cast(PtrUtil::addOffset(mPtr, mOffset))->next(); + mPtr = static_cast(PtrUtil::addOffset(node, -mOffset)); + return *this; + } + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr; } + + private: + T* mPtr; + s32 mOffset; + }; + + iterator begin() const { return iterator(listNodeToObj(mStartEnd.next()), mOffset); } + iterator end() const + { + return iterator(listNodeToObj(const_cast(&mStartEnd)), mOffset); + } + iterator begin(T* ptr) const { return iterator(ptr, mOffset); } + + class robustIterator + { + public: + robustIterator(T* ptr, s32 offset) + : mPtr{ptr}, mNextNode(static_cast(PtrUtil::addOffset(ptr, offset))->next()), + mOffset{offset} + { + } + bool operator==(const robustIterator& other) const { return mPtr == other.mPtr; } + bool operator!=(const robustIterator& other) const { return !operator==(other); } + robustIterator& operator++() + { + mPtr = static_cast(PtrUtil::addOffset(mNextNode, -mOffset)); + mNextNode = mNextNode->next(); + return *this; + } + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr; } + + private: + T* mPtr; + ListNode* mNextNode; + s32 mOffset; + }; + + robustIterator robustBegin() const + { + return robustIterator(listNodeToObj(mStartEnd.next()), mOffset); + } + + robustIterator robustEnd() const + { + return robustIterator(listNodeToObj(const_cast(&mStartEnd)), mOffset); + } + + robustIterator robustBegin(T* ptr) const { return robustIterator(ptr, mOffset); } + + struct RobustRange + { + auto begin() const { return mList.robustBegin(); } + auto end() const { return mList.robustEnd(); } + const OffsetList& mList; + }; + RobustRange robustRange() const { return {*this}; } + +protected: + static int compareT(const T* lhs, const T* rhs) + { + if (lhs < rhs) + return -1; + if (lhs > rhs) + return 1; + return 0; + } + + ListNode* objToListNode(T* obj) const + { + return static_cast(PtrUtil::addOffset(obj, mOffset)); + } + + const ListNode* objToListNode(const T* obj) const + { + return static_cast(PtrUtil::addOffset(obj, mOffset)); + } + + T* listNodeToObj(ListNode* node) const + { + return static_cast(PtrUtil::addOffset(node, -mOffset)); + } + + const T* listNodeToObj(const ListNode* node) const + { + return static_cast(PtrUtil::addOffset(node, -mOffset)); + } + + T* listNodeToObjWithNullCheck(ListNode* node) const + { + return node ? listNodeToObj(node) : nullptr; + } + + const T* listNodeToObjWithNullCheck(const ListNode* node) const + { + return node ? listNodeToObj(node) : nullptr; + } + + s32 mOffset = -1; +}; + +} // namespace sead + +#endif // SEAD_OFFSET_LIST_H_ diff --git a/include/sead/container/seadOrderedSet.h b/include/sead/container/seadOrderedSet.h new file mode 100644 index 0000000..7093648 --- /dev/null +++ b/include/sead/container/seadOrderedSet.h @@ -0,0 +1,189 @@ +#pragma once + +#include "container/seadTreeMap.h" + +namespace sead +{ +/// Requires Value to have operator< defined +/// This can be specialized, but all specializations must define `compare` as follows. +template +struct OrderedSetItemImpl +{ + OrderedSetItemImpl() = default; + // NOLINTNEXTLINE(google-explicit-constructor) + OrderedSetItemImpl(const Value& value_) : value(value_) {} + OrderedSetItemImpl& operator=(const Value& value_) + { + value = value_; + return *this; + } + + /// Returns -1 if this->value < rhs, 0 if this->value = rhs and 1 if this->value > rhs. + s32 compare(const OrderedSetItemImpl& rhs) const + { + if (value < rhs.value) + return -1; + if (rhs.value < value) + return 1; + return 0; + } + + Value value; +}; + +/// This is essentially std::set. Values are stored in a red-black tree in an order +/// determined by the partial order defined for Value (via operator<). +template +class OrderedSet : public TreeMapImpl> +{ +public: + using MapImpl = TreeMapImpl>; + class Node : public MapImpl::Node + { + public: + Node(OrderedSet* set, const Value& value) : mSet(set) { this->mKey = value; } + + void erase_() override; + + // Values cannot be modified. + const Value& value() const { return this->key().value; } + + private: + friend class OrderedSet; + OrderedSet* mSet; + }; + + /// Clears the set and frees the allocated buffer (if needed). + /// Should only be used if the buffer was allocated using allocBuffer. + void finalize() + { + clear(); + freeBuffer(); + } + + bool allocBuffer(s32 node_max, Heap* heap, s32 alignment = sizeof(void*)); + void setBuffer(s32 node_max, void* buffer); + /// Should only be used if the buffer was allocated using allocBuffer. + void freeBuffer(); + + const Value* insert(const Value& value); + void clear(); + + Node* find(const Value& value) const; + + // Callable must have the signature Value& + template + void forEach(const Callable& delegate) const; + + Node* startIterating() const { return static_cast(MapImpl::startIterating()); } + Node* nextNode(Node* node) const { return static_cast(MapImpl::nextNode(node)); } + +private: + void eraseNodeForClear_(typename MapImpl::Node* node); + + FreeList mFreeList; + s32 mSize = 0; + s32 mCapacity = 0; +}; + +template +inline bool OrderedSet::allocBuffer(s32 node_max, Heap* heap, s32 alignment) +{ + SEAD_ASSERT(mFreeList.work() == nullptr); + if (node_max <= 0) + { + SEAD_ASSERT_MSG(false, "node_max[%d] must be larger than zero", node_max); + AllocFailAssert(heap, node_max * sizeof(Node), alignment); + } + + void* work = AllocBuffer(node_max * sizeof(Node), heap, alignment); + if (!work) + return false; + setBuffer(node_max, work); + return true; +} + +template +inline void OrderedSet::setBuffer(s32 node_max, void* buffer) +{ + mCapacity = node_max; + mFreeList.setWork(buffer, sizeof(Node), node_max); +} + +template +inline void OrderedSet::freeBuffer() +{ + void* buffer = mFreeList.work(); + if (!buffer) + return; + + mSize = 0; + MapImpl::clear(); + ::operator delete[](buffer); + mCapacity = 0; + mFreeList.reset(); +} + +template +inline const Value* OrderedSet::insert(const Value& value) +{ + if (mSize >= mCapacity) + { + if (Node* node = find(value)) + { + node->mKey = value; + return &node->value(); + } + SEAD_ASSERT_MSG(false, "map is full."); + return nullptr; + } + + Node* node = new (mFreeList.alloc()) Node(this, value); + ++mSize; + MapImpl::insert(node); + return &node->value(); +} + +template +inline void OrderedSet::clear() +{ + Delegate1, typename MapImpl::Node*> delegate(this, + &OrderedSet::eraseNodeForClear_); + MapImpl::forEach(delegate); + mSize = 0; + MapImpl::clear(); +} + +template +inline typename OrderedSet::Node* OrderedSet::find(const Value& value) const +{ + return static_cast(MapImpl::find(value)); +} + +template +template +inline void OrderedSet::forEach(const Callable& delegate) const +{ + MapImpl::forEach([&delegate](auto* base_node) { + auto* node = static_cast(base_node); + delegate(node->value()); + }); +} + +template +inline void OrderedSet::eraseNodeForClear_(typename MapImpl::Node* node) +{ + // Note: Nintendo does not call the destructor, which is dangerous... + mFreeList.free(node); +} + +template +inline void OrderedSet::Node::erase_() +{ + OrderedSet* const map = mSet; + void* const this_ = this; + // Note: Nintendo does not call the destructor, which is dangerous... + map->mFreeList.free(this_); + --map->mSize; +} +} // namespace sead diff --git a/include/sead/container/seadPtrArray.h b/include/sead/container/seadPtrArray.h new file mode 100644 index 0000000..f597dfe --- /dev/null +++ b/include/sead/container/seadPtrArray.h @@ -0,0 +1,377 @@ +#ifndef SEAD_PTR_ARRAY_H_ +#define SEAD_PTR_ARRAY_H_ + +#include +#include +#include +#include +#include + +namespace sead +{ +class Heap; +class Random; + +class PtrArrayImpl +{ +public: + PtrArrayImpl() = default; + PtrArrayImpl(s32 ptrNumMax, void* buf) { setBuffer(ptrNumMax, buf); } + + void setBuffer(s32 ptrNumMax, void* buf); + void allocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)); + bool tryAllocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)); + void freeBuffer(); + bool isBufferReady() const { return mPtrs != nullptr; } + + bool isEmpty() const { return mPtrNum == 0; } + bool isFull() const { return mPtrNum >= mPtrNumMax; } + + s32 size() const { return mPtrNum; } + s32 capacity() const { return mPtrNumMax; } + + void erase(s32 position) { erase(position, 1); } + void erase(s32 position, s32 count); + void clear() { mPtrNum = 0; } + + // TODO + void resize(s32 size); + // TODO + void unsafeResize(s32 size); + + void swap(s32 pos1, s32 pos2) + { + auto* ptr = mPtrs[pos1]; + mPtrs[pos1] = mPtrs[pos2]; + mPtrs[pos2] = ptr; + } + void reverse(); + void shuffle() + { + Random random; + shuffle(&random); + } + void shuffle(Random* random); + +protected: + using CompareCallbackImpl = int (*)(const void* a, const void* b); + + void* at(s32 idx) const + { + if (u32(mPtrNum) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d]", idx, mPtrNum); + return nullptr; + } + return mPtrs[idx]; + } + + void* unsafeAt(s32 idx) const { return mPtrs[idx]; } + + // XXX: should this use at()? + void* front() const { return mPtrs[0]; } + void* back() const { return mPtrs[mPtrNum - 1]; } + + void pushBack(void* ptr) + { + if (isFull()) + { + SEAD_ASSERT_MSG(false, "list is full."); + return; + } + // Simplest insert case, so this is implemented directly without using insert(). + mPtrs[mPtrNum] = ptr; + ++mPtrNum; + } + + void pushFront(void* ptr) { insert(0, ptr); } + + void* popBack() { return isEmpty() ? nullptr : mPtrs[--mPtrNum]; } + + void* popFront() + { + if (isEmpty()) + return nullptr; + + void* result = mPtrs[0]; + erase(0); + return result; + } + + void replace(s32 idx, void* ptr) { mPtrs[idx] = ptr; } + + void* find(const void* ptr, CompareCallbackImpl cmp) const + { + for (s32 i = 0; i < mPtrNum; ++i) + { + if (cmp(mPtrs[i], ptr) == 0) + return mPtrs[i]; + } + return nullptr; + } + + s32 search(const void* ptr, CompareCallbackImpl cmp) const + { + for (s32 i = 0; i < mPtrNum; ++i) + { + if (cmp(mPtrs[i], ptr) == 0) + return i; + } + return -1; + } + + bool equal(const PtrArrayImpl& other, CompareCallbackImpl cmp) const + { + if (mPtrNum != other.mPtrNum) + return false; + + for (s32 i = 0; i < mPtrNum; ++i) + { + if (cmp(mPtrs[i], other.mPtrs[i]) != 0) + return false; + } + return true; + } + + s32 indexOf(const void* ptr) const + { + for (s32 i = 0; i < mPtrNum; ++i) + { + if (mPtrs[i] == ptr) + return i; + } + return -1; + } + + void createVacancy(s32 pos, s32 count) + { + if (mPtrNum <= pos) + return; + + MemUtil::copyOverlap(mPtrs + pos + count, mPtrs + pos, + s32(sizeof(void*)) * (mPtrNum - pos)); + } + + void insert(s32 idx, void* ptr); + void insertArray(s32 idx, void* array, s32 array_length, s32 elem_size); + bool checkInsert(s32 idx, s32 num); + + template + void sort_(Compare cmp) + { + // Note: Nintendo did not use + std::sort(mPtrs, mPtrs + size(), [&](const void* a, const void* b) { + return cmp(static_cast(a), static_cast(b)) < 0; + }); + } + + template + void heapSort_(Compare cmp) + { + // Note: Nintendo did not use + const auto less_cmp = [&](const void* a, const void* b) { + return cmp(static_cast(a), static_cast(b)) < 0; + }; + std::make_heap(mPtrs, mPtrs + size(), less_cmp); + std::sort_heap(mPtrs, mPtrs + size(), less_cmp); + } + + void heapSort(CompareCallbackImpl cmp); + + s32 compare(const PtrArrayImpl& other, CompareCallbackImpl cmp) const; + void uniq(CompareCallbackImpl cmp); + + s32 binarySearch(const void* ptr, CompareCallbackImpl cmp) const + { + if (mPtrNum == 0) + return -1; + + s32 a = 0; + s32 b = mPtrNum - 1; + while (a < b) + { + const s32 m = (a + b) / 2; + const s32 c = cmp(mPtrs[m], ptr); + if (c == 0) + return m; + if (c < 0) + a = m + 1; + else + b = m; + } + + if (cmp(mPtrs[a], ptr) == 0) + return a; + + return -1; + } + + s32 mPtrNum = 0; + s32 mPtrNumMax = 0; + void** mPtrs = nullptr; +}; + +template +class PtrArray : public PtrArrayImpl +{ +public: + PtrArray() = default; + PtrArray(s32 ptrNumMax, T** buf) : PtrArrayImpl(ptrNumMax, buf) {} + + T* at(s32 pos) const { return static_cast(PtrArrayImpl::at(pos)); } + T* unsafeAt(s32 pos) const { return static_cast(PtrArrayImpl::unsafeAt(pos)); } + T* operator()(s32 pos) const { return unsafeAt(pos); } + T* operator[](s32 pos) const { return at(pos); } + + // XXX: Does this use at()? + T* front() const { return at(0); } + T* back() const { return at(mPtrNum - 1); } + + void pushBack(T* ptr) { PtrArrayImpl::pushBack(constCast(ptr)); } + void pushFront(T* ptr) { PtrArrayImpl::pushFront(constCast(ptr)); } + + T* popBack() { return static_cast(PtrArrayImpl::popBack()); } + T* popFront() { return static_cast(PtrArrayImpl::popFront()); } + + void insert(s32 pos, T* ptr) { PtrArrayImpl::insert(pos, constCast(ptr)); } + void insert(s32 pos, T* array, s32 count) + { + // XXX: is this right? + PtrArrayImpl::insertArray(pos, constCast(array), count, sizeof(T)); + } + void replace(s32 pos, T* ptr) { PtrArrayImpl::replace(pos, constCast(ptr)); } + + s32 indexOf(const T* ptr) const { return PtrArrayImpl::indexOf(ptr); } + + using CompareCallback = s32 (*)(const T*, const T*); + + void sort() { sort(compareT); } + void sort(CompareCallback cmp) { PtrArrayImpl::sort_(cmp); } + void heapSort() { heapSort(compareT); } + void heapSort(CompareCallback cmp) { PtrArrayImpl::heapSort_(cmp); } + + bool equal(const PtrArray& other, CompareCallback cmp) const + { + return PtrArrayImpl::equal(other, cmp); + } + + s32 compare(const PtrArray& other, CompareCallback cmp) const + { + return PtrArrayImpl::compare(other, cmp); + } + + T* find(const T* ptr) const + { + return PtrArrayImpl::find(ptr, + [](const void* a, const void* b) { return a == b ? 0 : -1; }); + } + T* find(const T* ptr, CompareCallback cmp) const { return PtrArrayImpl::find(ptr, cmp); } + s32 search(const T* ptr) const + { + return PtrArrayImpl::search(ptr, + [](const void* a, const void* b) { return a == b ? 0 : -1; }); + } + s32 search(const T* ptr, CompareCallback cmp) const { return PtrArrayImpl::search(ptr, cmp); } + s32 binarySearch(const T* ptr) const { return PtrArrayImpl::binarySearch(ptr, compareT); } + s32 binarySearch(const T* ptr, CompareCallback cmp) const + { + return PtrArrayImpl::binarySearch(ptr, cmp); + } + + bool operator==(const PtrArray& other) const { return equal(other, compareT); } + bool operator!=(const PtrArray& other) const { return !(*this == other); } + bool operator<(const PtrArray& other) const { return compare(other) < 0; } + bool operator<=(const PtrArray& other) const { return compare(other) <= 0; } + bool operator>(const PtrArray& other) const { return compare(other) > 0; } + bool operator>=(const PtrArray& other) const { return compare(other) >= 0; } + + void uniq() { PtrArrayImpl::uniq(compareT); } + void uniq(CompareCallback cmp) { PtrArrayImpl::uniq(cmp); } + + class iterator + { + public: + iterator(T* const* pptr) : mPPtr{pptr} {} + bool operator==(const iterator& other) const { return mPPtr == other.mPPtr; } + bool operator!=(const iterator& other) const { return !(*this == other); } + iterator& operator++() + { + ++mPPtr; + return *this; + } + T& operator*() const { return **mPPtr; } + T* operator->() const { return *mPPtr; } + + private: + T* const* mPPtr; + }; + + iterator begin() const { return iterator(data()); } + iterator end() const { return iterator(data() + mPtrNum); } + + class constIterator + { + public: + constIterator(const T* const* pptr) : mPPtr{pptr} {} + bool operator==(const constIterator& other) const { return mPPtr == other.mPPtr; } + bool operator!=(const constIterator& other) const { return !(*this == other); } + constIterator& operator++() + { + ++mPPtr; + return *this; + } + const T& operator*() const { return **mPPtr; } + const T* operator->() const { return *mPPtr; } + + private: + const T* const* mPPtr; + }; + + constIterator constBegin() const { return constIterator(data()); } + constIterator constEnd() const { return constIterator(data() + mPtrNum); } + + T** data() const { return reinterpret_cast(mPtrs); } + T** dataBegin() const { return data(); } + T** dataEnd() const { return data() + mPtrNum; } + +protected: + static void* constCast(const T* ptr) + { + // Unfortunately, we need to cast away const because several PtrArrayImpl functions + // only take void* even though the pointed-to object isn't actually modified. + return static_cast(const_cast*>(ptr)); + } + + static int compareT(const void* a_, const void* b_) + { + const T* a = static_cast(a_); + const T* b = static_cast(b_); + if (*a < *b) + return -1; + if (*b < *a) + return 1; + return 0; + } +}; + +template +class FixedPtrArray : public PtrArray +{ +public: + FixedPtrArray() : PtrArray(N, mWork) {} + + // These do not make sense for a *fixed* array. + void setBuffer(s32 ptrNumMax, void* buf) = delete; + void allocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; + bool tryAllocBuffer(s32 ptrNumMax, Heap* heap, s32 alignment = sizeof(void*)) = delete; + void freeBuffer() = delete; + +private: + // Nintendo uses an untyped u8[N*sizeof(void*)] buffer. That is undefined behavior, + // so we will not do that. + T* mWork[N]; +}; + +} // namespace sead + +#endif // SEAD_PTR_ARRAY_H_ diff --git a/include/sead/container/seadRingBuffer.h b/include/sead/container/seadRingBuffer.h new file mode 100644 index 0000000..ff9c08f --- /dev/null +++ b/include/sead/container/seadRingBuffer.h @@ -0,0 +1,337 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace sead +{ +class Heap; + +template +class RingBuffer +{ +public: + RingBuffer() = default; + RingBuffer(s32 capacity, T* buffer) { setBuffer(capacity, buffer); } + template + explicit RingBuffer(T (&array)[N]) : RingBuffer(N, array) + { + } + + class iterator + { + public: + explicit iterator(RingBuffer* buffer, s32 index = 0) : mIndex(index), mBuffer(buffer) {} + bool operator==(const iterator& rhs) const + { + return mIndex == rhs.mIndex && mBuffer == rhs.mBuffer; + } + bool operator!=(const iterator& rhs) const { return !operator==(rhs); } + iterator& operator++() + { + ++mIndex; + return *this; + } + T& operator*() const { return buffer()(mIndex); } + T* operator->() const { return &buffer()(mIndex); } + s32 getIndex() const { return mIndex; } + + private: + RingBuffer& buffer() const { return *mBuffer; } + + s32 mIndex; + RingBuffer* mBuffer; + }; + + class constIterator + { + public: + explicit constIterator(const RingBuffer* buffer, s32 index = 0) + : mIndex(index), mBuffer(buffer) + { + } + bool operator==(const constIterator& rhs) const + { + return mIndex == rhs.mIndex && mBuffer == rhs.mBuffer; + } + bool operator!=(const constIterator& rhs) const { return !operator==(rhs); } + constIterator& operator++() + { + ++mIndex; + return *this; + } + const T& operator*() const { return buffer()(mIndex); } + const T* operator->() const { return &buffer()(mIndex); } + s32 getIndex() const { return mIndex; } + + private: + const RingBuffer& buffer() const { return *mBuffer; } + + s32 mIndex; + const RingBuffer* mBuffer; + }; + + iterator begin() { return iterator(this); } + constIterator begin() const { return constIterator(this); } + iterator begin(s32 start_idx) { return iterator(this, wrapIndex(start_idx)); } + constIterator begin(s32 start_idx) const { return constIterator(this, wrapIndex(start_idx)); } + iterator end() { return iterator(this, mSize); } + constIterator end() const { return constIterator(this, mSize); } + + void allocBuffer(s32 capacity, s32 alignment) { void(tryAllocBuffer(capacity, alignment)); } + + void allocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) + { + static_cast(tryAllocBuffer(capacity, heap, alignment)); + } + + bool tryAllocBuffer(s32 capacity, s32 alignment = sizeof(void*)) + { + SEAD_ASSERT(mBuffer == nullptr); + if (capacity > 0) + { + T* buffer = new (alignment, std::nothrow) T[capacity]; + if (buffer) + { + mBuffer = buffer; + mHead = mSize = 0; + mCapacity = capacity; + SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)), + "don't set alignment for a class with destructor"); + return true; + } + return false; + } + SEAD_ASSERT_MSG(false, "numMax[%d] must be larger than zero", capacity); + return false; + } + + bool tryAllocBuffer(s32 capacity, Heap* heap, s32 alignment = sizeof(void*)) + { + SEAD_ASSERT(mBuffer == nullptr); + if (capacity > 0) + { + T* buffer = new (heap, alignment, std::nothrow) T[capacity]; + if (buffer) + { + mBuffer = buffer; + mHead = mSize = 0; + mCapacity = capacity; + SEAD_ASSERT_MSG(PtrUtil::isAlignedPow2(mBuffer, sead::Mathi::abs(alignment)), + "don't set alignment for a class with destructor"); + return true; + } + return false; + } + SEAD_ASSERT_MSG(false, "numMax[%d] must be larger than zero", capacity); + return false; + } + + void allocBufferAssert(s32 size, Heap* heap, s32 alignment = sizeof(void*)) + { + if (!tryAllocBuffer(size, heap, alignment)) + AllocFailAssert(heap, sizeof(T) * size, alignment); + } + + void freeBuffer() + { + if (mBuffer) + { + delete[] mBuffer; + mBuffer = nullptr; + mCapacity = 0; + mHead = 0; + mSize = 0; + } + } + + void setBuffer(s32 capacity, T* bufferptr) + { + if (capacity < 1) + { + SEAD_ASSERT_MSG(false, "numMax[%d] must be larger than zero", capacity); + return; + } + if (!bufferptr) + { + SEAD_ASSERT_MSG(false, "bufferptr is null"); + return; + } + mBuffer = bufferptr; + mHead = mSize = 0; + mCapacity = capacity; + } + + bool isBufferReady() const { return mBuffer != nullptr; } + + T& operator[](s32 idx) + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity); + return mBuffer[0]; + } + return *unsafeGet(idx); + } + + const T& operator[](s32 idx) const + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity); + return mBuffer[0]; + } + return *unsafeGet(idx); + } + + T* get(s32 idx) + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity); + return nullptr; + } + return unsafeGet(idx); + } + + const T* get(s32 idx) const + { + if (u32(mSize) <= u32(idx)) + { + SEAD_ASSERT_MSG(false, "index exceeded [%d/%d/%d]", idx, mSize, mCapacity); + return nullptr; + } + + return unsafeGet(idx); + } + + T& operator()(s32 idx) { return *unsafeGet(idx); } + const T& operator()(s32 idx) const { return *unsafeGet(idx); } + + T* unsafeGet(s32 idx) { return &mBuffer[calcRealIdx(idx)]; } + const T* unsafeGet(s32 idx) const { return &mBuffer[calcRealIdx(idx)]; } + + T& front() { return *unsafeGet(0); } + const T& front() const { return *unsafeGet(0); } + + T& back() + { + if (mSize < 1) + { + SEAD_ASSERT_MSG(false, "no element"); + return mBuffer[0]; + } + return *unsafeGet(mSize - 1); + } + + const T& back() const + { + if (mSize < 1) + { + SEAD_ASSERT_MSG(false, "no element"); + return mBuffer[0]; + } + return *unsafeGet(mSize - 1); + } + + s32 capacity() const { return mCapacity; } + s32 size() const { return mSize; } + + bool empty() const { return mSize == 0; } + explicit operator bool() const { return !empty(); } + + T* data() { return mBuffer; } + const T* data() const { return mBuffer; } + + void forcePushBack(const T& item) + { + if (mSize < mCapacity) + { + pushBack(item); + return; + } + + if (mSize >= 1) + popFront(); + pushBack(item); + } + + bool pushBack(const T& item) + { + if (mSize >= mCapacity) + return false; + *unsafeGet(mSize++) = item; + return true; + } + + void forcePushBackwards(const T& item, u32 offset = 1) + { + mHead = (mHead < 1 ? mCapacity : mHead) - offset; + ++mSize; + *unsafeGet(0) = item; + } + + bool pushBackwards(const T& item) + { + if (mSize >= mCapacity) + return false; + forcePushBackwards(item); + return true; + } + + T popFront() + { + if (mSize >= 1) + { + T item = *unsafeGet(0); + mHead = mHead + 1 < mCapacity ? mHead + 1 : 0; + --mSize; + return item; + } + SEAD_ASSERT_MSG(false, "no element"); + return {}; + } + + void clear() { mHead = mSize = 0; } + +protected: + s32 calcRealIdx(s32 idx) const + { + s32 real_idx = mHead + idx; + if (real_idx >= mCapacity) + real_idx -= mCapacity; + return real_idx; + } + + s32 wrapIndex(s32 idx) const { return u32(idx) > u32(mSize) ? 0 : idx; } + + T* mBuffer = nullptr; + s32 mCapacity = 0; + s32 mHead = 0; + s32 mSize = 0; +}; + +template +class FixedRingBuffer : public RingBuffer +{ +public: + FixedRingBuffer() { RingBuffer::setBuffer(N, mData); } + + void allocBuffer(s32 capacity, s32 alignment) = delete; + void allocBuffer(s32 capacity, Heap* heap, s32 alignment) = delete; + bool tryAllocBuffer(s32 capacity, s32 alignment) = delete; + bool tryAllocBuffer(s32 capacity, Heap* heap, s32 alignment) = delete; + void allocBufferAssert(s32 size, Heap* heap, s32 alignment) = delete; + void freeBuffer() = delete; + void setBuffer(s32 capacity, T* bufferptr) = delete; + +private: + T mData[N]; +}; +} // namespace sead diff --git a/include/sead/container/seadSafeArray.h b/include/sead/container/seadSafeArray.h new file mode 100644 index 0000000..2de1d8e --- /dev/null +++ b/include/sead/container/seadSafeArray.h @@ -0,0 +1,158 @@ +#pragma once + +#include +#include +#include +#include + +namespace sead +{ +/// A lightweight std::array like wrapper for a C style array. +template +class SafeArray +{ +public: + T mBuffer[N]; + + T& operator[](s32 idx) + { + if (u32(idx) < N) + return mBuffer[idx]; +#ifdef MATCHING_HACK_NX_CLANG + // Force __LINE__ to be an odd number that cannot be encoded as an immediate for ORR. + // Otherwise, LLVM's register coalescer can get rid of too many register copies, + // which messes up register allocation. +#line 44 +#endif + SEAD_ASSERT_MSG(false, "range over [0, %d) : %d", N, idx); + return mBuffer[0]; + } + + const T& operator[](s32 idx) const + { + if (u32(idx) < N) + return mBuffer[idx]; +#ifdef MATCHING_HACK_NX_CLANG + // Force __LINE__ to be an even number that can be encoded as an immediate for ORR. + // Otherwise, LLVM's register coalescer can fail to get rid of some register copies, + // which messes up register allocation. +#line 59 +#endif + SEAD_ASSERT_MSG(false, "range over [0, %d) : %d", N, idx); + return mBuffer[0]; + } + + T& operator()(s32 idx) { return mBuffer[idx]; } + const T& operator()(s32 idx) const { return mBuffer[idx]; } + + T& front() { return mBuffer[0]; } + const T& front() const { return mBuffer[0]; } + T& back() { return mBuffer[N - 1]; } + const T& back() const { return mBuffer[N - 1]; } + + int size() const { return N; } + u32 getByteSize() const { return N * sizeof(T); } + + T* getBufferPtr() { return mBuffer; } + const T* getBufferPtr() const { return mBuffer; } + + void fill(const T& value) + { + for (s32 i = 0; i < N; ++i) + mBuffer[i] = value; + } + + class iterator + { + public: + iterator(T* buffer, s32 idx) : mBuffer(buffer), mIdx(idx) {} + bool operator==(const iterator& rhs) const + { + return mBuffer == rhs.mBuffer && mIdx == rhs.mIdx; + } + bool operator!=(const iterator& rhs) const { return !(*this == rhs); } + iterator& operator++() + { + ++mIdx; + return *this; + } + iterator& operator--() + { + --mIdx; + return *this; + } + T* operator->() const { return &mBuffer[mIdx]; } + T& operator*() const { return mBuffer[mIdx]; } + + private: + T* mBuffer; + s32 mIdx; + }; + + iterator begin() { return iterator(mBuffer, 0); } + iterator end() { return iterator(mBuffer, N); } + + class constIterator + { + public: + constIterator(const T* buffer, s32 idx) : mBuffer(buffer), mIdx(idx) {} + bool operator==(const constIterator& rhs) const + { + return mBuffer == rhs.mBuffer && mIdx == rhs.mIdx; + } + bool operator!=(const constIterator& rhs) const { return !(*this == rhs); } + constIterator& operator++() + { + ++mIdx; + return *this; + } + constIterator& operator--() + { + --mIdx; + return *this; + } + const T* operator->() const { return &mBuffer[mIdx]; } + const T& operator*() const { return mBuffer[mIdx]; } + + private: + const T* mBuffer; + s32 mIdx; + }; + + constIterator constBegin() const { return constIterator(mBuffer, 0); } + constIterator constEnd() const { return constIterator(mBuffer, N); } + + constIterator begin() const { return constIterator(mBuffer, 0); } + constIterator end() const { return constIterator(mBuffer, N); } +}; + +namespace detail +{ +// From https://en.cppreference.com/w/cpp/container/array/to_array +template +constexpr SafeArray, N> to_array_impl(T (&a)[N], std::index_sequence) +{ + return {{a[I]...}}; +} + +template +constexpr SafeArray, N> to_array_impl(T(&&a)[N], std::index_sequence) +{ + return {{std::move(a[I])...}}; +} +} // namespace detail + +// Implementation of C++20 std::to_array for sead::SafeArray. +template +constexpr sead::SafeArray, N> toArray(T (&a)[N]) +{ + return detail::to_array_impl(a, std::make_index_sequence{}); +} + +// Implementation of C++20 std::to_array for sead::SafeArray. +template +constexpr sead::SafeArray, N> toArray(T(&&a)[N]) +{ + return detail::to_array_impl(std::move(a), std::make_index_sequence{}); +} +} // namespace sead diff --git a/include/sead/container/seadStrTreeMap.h b/include/sead/container/seadStrTreeMap.h new file mode 100644 index 0000000..e74ac80 --- /dev/null +++ b/include/sead/container/seadStrTreeMap.h @@ -0,0 +1,171 @@ +#pragma once + +#include "basis/seadTypes.h" +#include "container/seadFreeList.h" +#include "container/seadTreeMap.h" +#include "prim/seadDelegate.h" +#include "prim/seadSafeString.h" + +namespace sead +{ +/// Sorted associative container with fixed-length string keys. +/// This is essentially std::map +template +class StrTreeMap : public TreeMapImpl +{ +public: + using MapImpl = TreeMapImpl; + class Node : public MapImpl::Node + { + public: + Node(StrTreeMap* map, const SafeString& key, const Value& value) : mValue(value), mMap(map) + { + BufferedSafeString buffer(mKeyData, MaxKeyLength + 1); + buffer.copy(key); + this->mKey = buffer; + } + + void erase_() override; + + Value& value() { return mValue; } + const Value& value() const { return mValue; } + + private: + friend class StrTreeMap; + + Value mValue; + StrTreeMap* mMap; + char mKeyData[MaxKeyLength + 1]; + }; + + ~StrTreeMap(); + + void allocBuffer(s32 node_max, Heap* heap, s32 alignment = sizeof(void*)); + void setBuffer(s32 node_max, void* buffer); + void freeBuffer(); + + Value* insert(const SafeString& key, const Value& value); + void clear(); + + Node* find(const SafeString& key) const; + + // Callable must have the signature Key&, Value& + template + void forEach(const Callable& delegate) const; + +private: + void eraseNodeForClear_(typename MapImpl::Node* node); + + FreeList mFreeList; + s32 mSize = 0; + s32 mCapacity = 0; +}; + +template +inline void StrTreeMap::Node::erase_() +{ + StrTreeMap* const map = mMap; + void* const this_ = this; + // Note: Nintendo does not call the destructor, which is dangerous... + map->mFreeList.free(this_); + --map->mSize; +} + +template +inline StrTreeMap::~StrTreeMap() +{ + void* work = mFreeList.work(); + if (!work) + return; + + clear(); + freeBuffer(); +} + +template +inline void StrTreeMap::allocBuffer(s32 node_max, Heap* heap, s32 alignment) +{ + SEAD_ASSERT(mFreeList.work() == nullptr); + if (node_max <= 0) + { + SEAD_ASSERT_MSG(false, "node_max[%d] must be larger than zero", node_max); + AllocFailAssert(heap, node_max * sizeof(Node), alignment); + } + + void* work = AllocBuffer(node_max * sizeof(Node), heap, alignment); + if (work) + setBuffer(node_max, work); +} + +template +inline void StrTreeMap::setBuffer(s32 node_max, void* buffer) +{ + mCapacity = node_max; + mFreeList.setWork(buffer, sizeof(Node), node_max); +} + +template +inline void StrTreeMap::freeBuffer() +{ + void* buffer = mFreeList.work(); + if (!buffer) + return; + + ::operator delete[](buffer); + mCapacity = 0; + mFreeList.reset(); +} + +template +inline Value* StrTreeMap::insert(const SafeString& key, const Value& value) +{ + if (mSize >= mCapacity) + { + if (Node* node = find(key)) + { + node->value() = value; + return &node->value(); + } + SEAD_ASSERT_MSG(false, "map is full."); + return nullptr; + } + + Node* node = new (mFreeList.alloc()) Node(this, key, value); + ++mSize; + MapImpl::insert(node); + return &node->value(); +} + +template +inline void StrTreeMap::clear() +{ + Delegate1, typename MapImpl::Node*> delegate( + this, &StrTreeMap::eraseNodeForClear_); + MapImpl::forEach(delegate); + mSize = 0; + MapImpl::clear(); +} + +template +inline typename StrTreeMap::Node* StrTreeMap::find(const SafeString& key) const +{ + return static_cast(MapImpl::find(key)); +} + +template +template +inline void StrTreeMap::forEach(const Callable& delegate) const +{ + MapImpl::forEach([&delegate](auto* base_node) { + auto* node = static_cast(base_node); + delegate(node->key(), node->value()); + }); +} + +template +inline void StrTreeMap::eraseNodeForClear_(typename MapImpl::Node* node) +{ + // Note: Nintendo does not call the destructor, which is dangerous... + mFreeList.free(node); +} +} // namespace sead diff --git a/include/sead/container/seadTList.h b/include/sead/container/seadTList.h new file mode 100644 index 0000000..0dbf908 --- /dev/null +++ b/include/sead/container/seadTList.h @@ -0,0 +1,206 @@ +#ifndef SEAD_TLIST_H_ +#define SEAD_TLIST_H_ + +#include + +namespace sead +{ +template +class TListNode; + +template +class TList : public ListImpl +{ +public: + using CompareCallback = int (*)(const T*, const T*); + + TList() : ListImpl() {} + + void pushBack(TListNode* item) + { + item->erase(); + item->mList = this; + ListImpl::pushBack(item); + } + + void pushFront(TListNode* item) + { + item->erase(); + item->mList = this; + ListImpl::pushFront(item); + } + + TListNode* popBack() { return static_cast*>(ListImpl::popBack()); } + TListNode* popFront() { return static_cast*>(ListImpl::popFront()); } + + void insertBefore(TListNode* node, TListNode* node_to_insert) + { + ListImpl::insertBefore(node, node_to_insert); + } + + void insertAfter(TListNode* node, TListNode* node_to_insert) + { + ListImpl::insertAfter(node, node_to_insert); + } + + void erase(TListNode* item) + { + if (!item->mList) + return; + item->mList = nullptr; + ListImpl::erase(item); + } + + TListNode* front() const { return static_cast*>(ListImpl::front()); } + TListNode* back() const { return static_cast*>(ListImpl::back()); } + TListNode* nth(int n) const { return static_cast*>(ListImpl::nth(n)); } + s32 indexOf(const TListNode* node) const { return ListImpl::indexOf(node); } + + void swap(TListNode* n1, TListNode* n2) { ListImpl::swap(n1, n2); } + void moveAfter(TListNode* basis, TListNode* n) { ListImpl::moveAfter(basis, n); } + void moveBefore(TListNode* basis, TListNode* n) { ListImpl::moveBefore(basis, n); } + + void sort(s32 offset, CompareCallback cmp) { ListImpl::sort(offset, cmp); } + void mergeSort(s32 offset, CompareCallback cmp) { ListImpl::mergeSort(offset, cmp); } + + TListNode* find(const void* ptr, s32 offset, CompareCallback cmp) const + { + return static_cast*>(ListImpl::find(ptr, offset, cmp)); + } + void uniq(s32 offset, CompareCallback cmp) { ListImpl::uniq(offset, cmp); } + void clear() { ListImpl::clear(); } + + TListNode* prev(const TListNode* node) const + { + auto prev_node = static_cast*>(node->prev()); + if (prev_node == &mStartEnd) + return nullptr; + return prev_node; + } + + TListNode* next(const TListNode* node) const + { + auto next_node = static_cast*>(node->next()); + if (next_node == &mStartEnd) + return nullptr; + return next_node; + } + + class iterator + { + public: + iterator(TListNode* ptr) : mPtr(ptr) {} + + iterator& operator++() + { + mPtr = static_cast*>(mPtr->next()); + return *this; + } + + iterator& operator--() + { + mPtr = static_cast*>(mPtr->prev()); + return *this; + } + + T& operator*() const { return mPtr->mData; } + T* operator->() const { return &mPtr->mData; } + + friend bool operator==(iterator it1, iterator it2) { return it1.mPtr == it2.mPtr; } + friend bool operator!=(iterator it1, iterator it2) { return !(it1 == it2); } + + private: + TListNode* mPtr; + }; + + iterator begin() const { return iterator(static_cast*>(mStartEnd.next())); } + + iterator end() const + { + return iterator(static_cast*>(const_cast(&mStartEnd))); + } + + class robustIterator + { + public: + robustIterator(TListNode* ptr) : mPtr(ptr) + { + mPtrNext = static_cast*>(mPtr->next()); + } + + robustIterator& operator++() + { + mPtr = mPtrNext; + mPtrNext = static_cast*>(mPtrNext->next()); + return *this; + } + + T& operator*() const { return mPtr->mData; } + T* operator->() const { return &mPtr->mData; } + + friend bool operator==(robustIterator it1, robustIterator it2) + { + return it1.mPtr == it2.mPtr; + } + friend bool operator!=(robustIterator it1, robustIterator it2) { return !(it1 == it2); } + + private: + TListNode* mPtr; + TListNode* mPtrNext; + }; + + robustIterator robustBegin() const + { + return robustIterator(static_cast*>(mStartEnd.next())); + } + + robustIterator robustEnd() const + { + return robustIterator(static_cast*>(const_cast(&mStartEnd))); + } + + struct RobustRange + { + auto begin() const { return mList.robustBegin(); } + auto end() const { return mList.robustEnd(); } + const TList& mList; + }; + RobustRange robustRange() const { return {*this}; } + +private: + static int compareT(const T* a, const T* b) + { + if (*a < *b) + return -1; + if (*a > *b) + return 1; + return 0; + } +}; + +template +class TListNode : public ListNode +{ +public: + TListNode() : ListNode() + { + mData = nullptr; + mList = NULL; + } + + TListNode(T data) : ListNode(), mData(data), mList(nullptr) {} + + void erase() + { + TList* list = mList; + if (list != NULL) + list->erase(this); + } + + T mData; + TList* mList; +}; + +} // namespace sead + +#endif // SEAD_TLIST_H_ diff --git a/include/sead/container/seadTreeMap.h b/include/sead/container/seadTreeMap.h new file mode 100644 index 0000000..c6d7c77 --- /dev/null +++ b/include/sead/container/seadTreeMap.h @@ -0,0 +1,584 @@ +#pragma once + +#include "basis/seadRawPrint.h" +#include "container/seadFreeList.h" +#include "prim/seadBitUtil.h" +#include "prim/seadDelegate.h" +#include "prim/seadSafeString.h" + +namespace sead +{ +template +class TreeMapNode; + +/// Sorted associative container, implemented using a left-leaning red-black tree. +/// For an explanation of the algorithm, see https://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf +template +class TreeMapImpl +{ +public: + using Node = TreeMapNode; + + void insert(Node* node); + void erase(const Key& key); + void clear(); + + Node* find(const Key& key) const { return find(mRoot, key); } + + template + void forEach(const Callable& callable) const + { + if (mRoot) + forEach(mRoot, callable); + } + + Node* startIterating() const + { + if (!mRoot) + return nullptr; + return startIterating(mRoot); + } + + Node* nextNode(Node* node) const + { + if (!node) + return nullptr; + + // If there is a right child node, explore that branch first. + if (node->mRight) + { + node->mRight->setParent(node); + return startIterating(node->mRight); + } + + // Otherwise, walk back up to the node P from which we reached this node + // by following P's left child pointer. + while (auto* const parent = node->getParent()) + { + if (parent->mLeft == node) + return parent; + node = parent; + } + return nullptr; + } + +protected: + /// Returns the left most child of a given node, marking each node with its parent + /// along the way. + static Node* startIterating(Node* node) + { + while (node->mLeft) + { + node->mLeft->setParent(node); + node = node->mLeft; + } + return node; + } + + Node* insert(Node* root, Node* node); + Node* erase(Node* root, const Key& key); + Node* find(Node* root, const Key& key) const; + + static inline Node* rotateLeft(Node* node); + static inline Node* rotateRight(Node* node); + static inline Node* moveRedLeft(Node* node); + static inline Node* moveRedRight(Node* node); + static Node* findMin(Node* node); + static Node* eraseMin(Node* node); + static inline Node* fixUp(Node* node); + static bool isRed(const Node* node) { return node && node->isRed(); } + static inline void flipColors(Node* node); + + template + static void forEach(Node* start, const Callable& callable); + + Node* mRoot = nullptr; +}; + +/// Requires Key to have a compare() member function, which returns -1 if lhs < rhs, 0 if lhs = rhs +/// and 1 if lhs > rhs. +template +class TreeMapNode +{ +public: + TreeMapNode() + { + mLeft = mRight = nullptr; + mColorAndPtr = 0; + } + + virtual ~TreeMapNode() = default; + virtual void erase_() = 0; + + const Key& key() const { return mKey; } + +protected: + friend class TreeMapImpl; + + enum class Color + { + Red = 0, + Black = 1, + }; + + void flipColor() { BitUtil::bitCastWrite(mColorAndPtr ^ 1u, &mColorAndPtr); } + void setColor(Color color) { mColorAndPtr = uintptr_t(color); } + + void setParent(TreeMapNode* parent) { mColorAndPtr = (mColorAndPtr & 1) | uintptr_t(parent); } + /// @warning Only valid if setParent has been called! + TreeMapNode* getParent() const { return reinterpret_cast(mColorAndPtr & ~1); } + + bool isRed() const { return (mColorAndPtr & 1u) == bool(Color::Red); } + + TreeMapNode* mLeft; + TreeMapNode* mRight; + uintptr_t mColorAndPtr; + Key mKey; +}; + +/// Requires Key to have operator< defined +/// This can be specialized, but all specializations must define `compare` and `key` as follows. +template +struct TreeMapKeyImpl +{ + TreeMapKeyImpl() = default; + TreeMapKeyImpl(const Key& key_) : key(key_) {} + TreeMapKeyImpl& operator=(const Key& key_) + { + key = key_; + return *this; + } + + /// Returns -1 if mKey < rhs, 0 if mKey = rhs and 1 if mKey > rhs. + s32 compare(const TreeMapKeyImpl& rhs) const + { + if (key < rhs.key) + return -1; + if (rhs.key < key) + return 1; + return 0; + } + + Key key; +}; + +/// Sorted associative container. +/// This is essentially std::map +template +class TreeMap : public TreeMapImpl> +{ +public: + using MapImpl = TreeMapImpl>; + class Node : public MapImpl::Node + { + public: + Node(TreeMap* map, const Key& key, const Value& value) : mValue(value), mMap(map) + { + this->mKey = key; + } + + void erase_() override; + + Value& value() { return mValue; } + const Value& value() const { return mValue; } + + private: + friend class TreeMap; + + Value mValue; + TreeMap* mMap; + }; + + ~TreeMap(); + + void allocBuffer(s32 node_max, Heap* heap, s32 alignment = sizeof(void*)); + void setBuffer(s32 node_max, void* buffer); + void freeBuffer(); + + Value* insert(const Key& key, const Value& value); + void clear(); + + Node* find(const Key& key) const; + + // Callable must have the signature Key&, Value& + template + void forEach(const Callable& delegate) const; + + Node* startIterating() const { return static_cast(MapImpl::startIterating()); } + Node* nextNode(Node* node) const { return static_cast(MapImpl::nextNode(node)); } + +private: + void eraseNodeForClear_(typename MapImpl::Node* node); + + FreeList mFreeList; + s32 mSize = 0; + s32 mCapacity = 0; +}; + +template +class IntrusiveTreeMap : public TreeMapImpl +{ +public: + using MapImpl = TreeMapImpl; + + Node* find(const Key& key) const { return static_cast(MapImpl::find(key)); } + + // Callable must have the signature Node* + template + void forEach(const Callable& delegate) const + { + MapImpl::forEach([delegate](auto* base_node) { + auto* node = static_cast(base_node); + delegate(node); + }); + } + + Node* startIterating() const { return static_cast(MapImpl::startIterating()); } + Node* nextNode(Node* node) const { return static_cast(MapImpl::nextNode(node)); } +}; + +template +inline void TreeMapImpl::insert(Node* node) +{ + mRoot = insert(mRoot, node); + mRoot->setColor(Node::Color::Black); +} + +template +inline TreeMapNode* TreeMapImpl::insert(Node* root, Node* node) +{ + if (!root) + { + node->mLeft = node->mRight = nullptr; + node->setColor(Node::Color::Red); + return node; + } + + const s32 cmp = node->key().compare(root->key()); + + if (cmp < 0) + { + root->mLeft = insert(root->mLeft, node); + } + else if (cmp > 0) + { + root->mRight = insert(root->mRight, node); + } + else if (root != node) + { + node->mRight = root->mRight; + node->mLeft = root->mLeft; + node->mColorAndPtr = root->mColorAndPtr; + root->erase_(); + root = node; + } + + if (isRed(root->mRight) && !isRed(root->mLeft)) + root = rotateLeft(root); + + if (isRed(root->mLeft) && isRed(root->mLeft->mLeft)) + root = rotateRight(root); + + if (isRed(root->mLeft) && isRed(root->mRight)) + flipColors(root); + + return root; +} + +template +inline void TreeMapImpl::erase(const Key& key) +{ + mRoot = erase(mRoot, key); + if (mRoot) + mRoot->setColor(Node::Color::Black); +} + +template +inline TreeMapNode* TreeMapImpl::erase(Node* root, const Key& key) +{ + if (key.compare(root->key()) < 0) + { + if (!isRed(root->mLeft) && !isRed(root->mLeft->mLeft)) + root = moveRedLeft(root); + root->mLeft = erase(root->mLeft, key); + } + else + { + if (isRed(root->mLeft)) + root = rotateRight(root); + + if (key.compare(root->key()) == 0 && !root->mRight) + { + root->erase_(); + return nullptr; + } + + if (!isRed(root->mRight) && !isRed(root->mRight->mLeft)) + root = moveRedRight(root); + + if (key.compare(root->key()) == 0) + { + Node* const min_node = findMin(root->mRight); + + Node* target = root->mRight; + if (root->mRight) + target = find(root->mRight, min_node->key()); + + target->mRight = eraseMin(root->mRight); + target->mLeft = root->mLeft; + target->mColorAndPtr = root->mColorAndPtr; + root->erase_(); + root = target; + } + else + { + root->mRight = erase(root->mRight, key); + } + } + return fixUp(root); +} + +template +inline void TreeMapImpl::clear() +{ + mRoot = nullptr; +} + +template +inline TreeMapNode* TreeMapImpl::find(Node* root, const Key& key) const +{ + Node* node = root; + while (node) + { + const s32 cmp = key.compare(node->key()); + if (cmp < 0) + node = node->mLeft; + else if (cmp > 0) + node = node->mRight; + else + return node; + } + + return nullptr; +} + +template +template +inline void TreeMapImpl::forEach(Node* start, const Callable& callable) +{ + Node* i = start; + do + { + Node* node = i; + if (i->mLeft) + forEach(i->mLeft, callable); + i = i->mRight; + callable(node); + } while (i); +} + +template +inline TreeMapNode* TreeMapImpl::rotateLeft(Node* node) +{ + TreeMapNode* j = node->mRight; + node->mRight = j->mLeft; + j->mLeft = node; + j->mColorAndPtr = node->mColorAndPtr; + node->setColor(Node::Color::Red); + return j; +} + +template +inline TreeMapNode* TreeMapImpl::rotateRight(Node* node) +{ + TreeMapNode* j = node->mLeft; + node->mLeft = j->mRight; + j->mRight = node; + j->mColorAndPtr = node->mColorAndPtr; + node->setColor(Node::Color::Red); + return j; +} + +// NON_MATCHING: this version matches the LLRB tree implementation and is better optimized; +// there is a useless store to node->mRight in the original version +template +inline TreeMapNode* TreeMapImpl::moveRedLeft(Node* node) +{ + flipColors(node); + if (isRed(node->mRight->mLeft)) + { + node->mRight = rotateRight(node->mRight); + node = rotateLeft(node); + flipColors(node); + } + return node; +} + +template +inline TreeMapNode* TreeMapImpl::moveRedRight(Node* node) +{ + flipColors(node); + if (isRed(node->mLeft->mLeft)) + { + node = rotateRight(node); + flipColors(node); + } + return node; +} + +template +inline TreeMapNode* TreeMapImpl::findMin(Node* node) +{ + while (node->mLeft) + node = node->mLeft; + return node; +} + +// NON_MATCHING: this version matches the LLRB tree implementation and is better optimized +template +inline TreeMapNode* TreeMapImpl::eraseMin(Node* node) +{ + if (!node->mLeft) + return nullptr; + + if (!isRed(node->mLeft) && !isRed(node->mLeft->mLeft)) + node = moveRedLeft(node); + + node->mLeft = eraseMin(node->mLeft); +#ifdef MATCHING_HACK_NX_CLANG + asm(""); +#endif + return fixUp(node); +} + +template +inline TreeMapNode* TreeMapImpl::fixUp(Node* node) +{ + if (isRed(node->mRight)) + node = rotateLeft(node); + + if (isRed(node->mLeft) && isRed(node->mLeft->mLeft)) + node = rotateRight(node); + + if (isRed(node->mLeft) && isRed(node->mRight)) + flipColors(node); + + return node; +} + +template +inline void TreeMapImpl::flipColors(Node* node) +{ + node->flipColor(); + node->mLeft->flipColor(); + node->mRight->flipColor(); +} + +template +inline void TreeMap::Node::erase_() +{ + TreeMap* const map = mMap; + void* const this_ = this; + // Note: Nintendo does not call the destructor, which is dangerous... + map->mFreeList.free(this_); + --map->mSize; +} + +template +inline TreeMap::~TreeMap() +{ + void* work = mFreeList.work(); + if (!work) + return; + + clear(); + freeBuffer(); +} + +template +inline void TreeMap::allocBuffer(s32 node_max, Heap* heap, s32 alignment) +{ + SEAD_ASSERT(mFreeList.work() == nullptr); + if (node_max <= 0) + { + SEAD_ASSERT_MSG(false, "node_max[%d] must be larger than zero", node_max); + AllocFailAssert(heap, node_max * sizeof(Node), alignment); + } + + void* work = AllocBuffer(node_max * sizeof(Node), heap, alignment); + if (work) + setBuffer(node_max, work); +} + +template +inline void TreeMap::setBuffer(s32 node_max, void* buffer) +{ + mCapacity = node_max; + mFreeList.setWork(buffer, sizeof(Node), node_max); +} + +template +inline void TreeMap::freeBuffer() +{ + void* buffer = mFreeList.work(); + if (!buffer) + return; + + ::operator delete[](buffer); + mCapacity = 0; + mFreeList.reset(); +} + +template +inline Value* TreeMap::insert(const Key& key, const Value& value) +{ + if (mSize >= mCapacity) + { + if (Node* node = find(key)) + { + node->value() = value; + return &node->value(); + } + SEAD_ASSERT_MSG(false, "map is full."); + return nullptr; + } + + Node* node = new (mFreeList.alloc()) Node(this, key, value); + ++mSize; + MapImpl::insert(node); + return &node->value(); +} + +template +inline void TreeMap::clear() +{ + Delegate1, typename MapImpl::Node*> delegate(this, + &TreeMap::eraseNodeForClear_); + MapImpl::forEach(delegate); + mSize = 0; + MapImpl::clear(); +} + +template +inline typename TreeMap::Node* TreeMap::find(const Key& key) const +{ + return static_cast(MapImpl::find(key)); +} + +template +template +inline void TreeMap::forEach(const Callable& delegate) const +{ + MapImpl::forEach([&delegate](auto* base_node) { + auto* node = static_cast(base_node); + delegate(node->key(), node->value()); + }); +} + +template +inline void TreeMap::eraseNodeForClear_(typename MapImpl::Node* node) +{ + // Note: Nintendo does not call the destructor, which is dangerous... + mFreeList.free(node); +} +} // namespace sead diff --git a/include/sead/container/seadTreeNode.h b/include/sead/container/seadTreeNode.h new file mode 100644 index 0000000..1233dcf --- /dev/null +++ b/include/sead/container/seadTreeNode.h @@ -0,0 +1,64 @@ +#ifndef SEAD_TREENODE_H_ +#define SEAD_TREENODE_H_ + +#include + +namespace sead +{ +class TreeNode +{ +public: + TreeNode(); + + void clearLinks(); + s32 countChildren() const; + void detachAll(); + void detachSubTree(); + TreeNode* findRoot(); + const TreeNode* findRoot() const; + void insertAfterSelf(TreeNode* node); + void insertBeforeSelf(TreeNode* node); + void pushBackChild(TreeNode* node); + void pushBackSibling(TreeNode* node); + void pushFrontChild(TreeNode* node); + +protected: + void clearChildLinksRecursively_(); + + TreeNode* mParent; + TreeNode* mChild; + TreeNode* mNext; + TreeNode* mPrev; +}; + +template +class TTreeNode : public TreeNode +{ +public: + TTreeNode() = default; + explicit TTreeNode(T data) : mData(data) {} + + T& value() { return mData; } + const T& value() const { return mData; } + + TTreeNode* parent() const { return static_cast(mParent); } + TTreeNode* child() const { return static_cast(mChild); } + TTreeNode* next() const { return static_cast(mNext); } + TTreeNode* prev() const { return static_cast(mPrev); } + TTreeNode* findRoot() { return static_cast(TreeNode::findRoot()); } + const TTreeNode* findRoot() const { return static_cast(TreeNode::findRoot()); } + void insertAfterSelf(TTreeNode* node) { TreeNode::insertAfterSelf(node); } + void insertBeforeSelf(TTreeNode* node) { TreeNode::insertBeforeSelf(node); } + void pushBackChild(TTreeNode* node) { TreeNode::pushBackChild(node); } + void pushBackSibling(TTreeNode* node) { TreeNode::pushBackSibling(node); } + void pushFrontChild(TTreeNode* node) { TreeNode::pushFrontChild(node); } + + // TODO: probably iterators + +protected: + T mData; +}; + +} // namespace sead + +#endif // SEAD_TREENODE_H_ diff --git a/include/sead/controller/seadController.h b/include/sead/controller/seadController.h new file mode 100644 index 0000000..6247bac --- /dev/null +++ b/include/sead/controller/seadController.h @@ -0,0 +1,50 @@ +#pragma once + +#include "container/seadOffsetList.h" +#include "controller/seadControllerBase.h" + +namespace sead +{ +class ControllerMgr; +class ControllerAddon; + +namespace ControllerDefine +{ +enum AddonId : int +{ +}; +enum ControllerId : int +{ + _15 = 15, + _16 = 16 +}; +enum DeviceId : int +{ +}; + +} // namespace ControllerDefine + +class Controller : public ControllerBase +{ + SEAD_RTTI_OVERRIDE(Controller, ControllerBase) +public: + Controller(ControllerMgr*); + virtual ~Controller(); + virtual void calc(); + virtual bool isConnected(); + ControllerAddon* getAddonByOrder(ControllerDefine::AddonId, int); + ControllerAddon* getAddon(ControllerDefine::AddonId); + +protected: + virtual void calcImpl_() = 0; + virtual bool isIdle_(); + virtual void setIdle_(); + +private: + int mControllerId; + ControllerMgr* mMgr; + OffsetList mAddonList; + OffsetList _160; // unknown type +}; + +} // namespace sead diff --git a/include/sead/controller/seadControllerBase.h b/include/sead/controller/seadControllerBase.h new file mode 100644 index 0000000..9d78f9d --- /dev/null +++ b/include/sead/controller/seadControllerBase.h @@ -0,0 +1,68 @@ +#pragma once + +#include "math/seadBoundBox.h" +#include "math/seadVector.h" +#include "prim/seadBitFlag.h" +#include "prim/seadRuntimeTypeInfo.h" + +namespace sead +{ +class ControllerBase +{ + SEAD_RTTI_BASE(ControllerBase) +public: + ControllerBase(int, int, int, int); + + void setRightStickCrossThreshold(float, float); + void setPointerBound(const BoundBox2f& bound); + void setPadRepeat(u32, u8, u8); + void setLeftStickCrossThreshold(float, float); + // unknown return type + u32 getPadHoldCount(int) const; + + BitFlag32 getButtonsTrigger() const { return mButtonsTrigger; } + BitFlag32 getButtonsRelease() const { return mButtonsRelease; } + BitFlag32 getButtonsRepeat() const { return mButtonsRepeat; } + BitFlag32 getButtonsHold() const { return mButtonsHold; } + const Vector2f& getTouchScreenPos() const { return mTouchScreenPos; } + const Vector2f& getLeftJoy() const { return mLeftJoy; } + const Vector2f& getRightJoy() const { return mRightJoy; } + +protected: + void updateDerivativeParams_(u32, bool); + void setPointerWithBound_(bool, bool, const Vector2f& bound); + void setIdleBase_(); + bool isIdleBase_(); + // unknown return type + u32 getStickHold_(u32, const Vector2f&, float, float, int); + // unknown return type + u32 createStickCrossMask_(); + +private: + BitFlag32 mButtonsTrigger; + BitFlag32 mButtonsRelease; + BitFlag32 mButtonsRepeat; + unsigned int mFlags; + int _18; + int _1c; + BoundBox2f mPointerBound; + int mPadHoldCounts[32]; + char _b0[32]; + char _d0[32]; + float mLeftStickThresholdX; + float mRightStickThresholdX; + float mLeftStickThresholdY; + float mRightStickThresholdY; + int _100; + int _104; + int _108; + int _10c; + unsigned int mIdleCounter; + sead::BitFlag32 mButtonsHold; + Vector2f mTouchScreenPos; + Vector2f mLeftJoy; + Vector2f mRightJoy; + Vector2f _130; +}; + +} // namespace sead diff --git a/include/sead/controller/seadControllerMgr.h b/include/sead/controller/seadControllerMgr.h new file mode 100644 index 0000000..c7108f5 --- /dev/null +++ b/include/sead/controller/seadControllerMgr.h @@ -0,0 +1,45 @@ +#pragma once + +#include "container/seadOffsetList.h" +#include "container/seadPtrArray.h" +#include "controller/seadController.h" +#include "framework/seadCalculateTask.h" +#include "framework/seadTaskMgr.h" +#include "heap/seadDisposer.h" + +namespace sead +{ +class Controller; +class NinJoyNpadDevice; + +class ControllerMgr : public CalculateTask +{ + SEAD_TASK_SINGLETON(ControllerMgr) +public: + explicit ControllerMgr(const TaskConstructArg& arg); + ControllerMgr(); + + void calc() override; + void finalize(); + void finalizeDefault(); + int findControllerPort(const Controller* controller); + NinJoyNpadDevice* getControlDevice(ControllerDefine::DeviceId device_id); + // unknown return type + void* getControllerAddon(int, ControllerDefine::AddonId addon_id); + // unknown return type + void* getControllerAddonByOrder(int, ControllerDefine::AddonId addon_id, int); + Controller* getControllerByOrder(ControllerDefine::ControllerId controller_id, int); + // unknown return type, probably inherited from TaskBase + void* getFramework(); + void initialize(int, Heap* heap); + void initializeDefault(Heap* heap); + void prepare() override; + + Controller* getController(int port) { return mArray[port]; } + +private: + OffsetList mList; + PtrArray mArray; +}; + +} // namespace sead diff --git a/include/sead/devenv/seadAssertConfig.h b/include/sead/devenv/seadAssertConfig.h new file mode 100644 index 0000000..257e868 --- /dev/null +++ b/include/sead/devenv/seadAssertConfig.h @@ -0,0 +1,22 @@ +#pragma once + +#include "prim/seadDelegate.h" +#include "prim/seadDelegateEventSlot.h" + +namespace sead +{ +class AssertConfig +{ +public: + using AssertEvent = DelegateEvent; + + static void registerCallback(AssertEvent::Slot& slot); + static void unregisterCallback(AssertEvent::Slot& slot); + static void registerFinalCallback(IDelegate1* cb); + static void execCallbacks(const char* assertMessage); + +private: + static AssertEvent sAssertEvent; + static IDelegate1* sFinalCallback; +}; +} // namespace sead diff --git a/include/sead/devenv/seadDebugFontMgrNvn.h b/include/sead/devenv/seadDebugFontMgrNvn.h new file mode 100644 index 0000000..2da5df6 --- /dev/null +++ b/include/sead/devenv/seadDebugFontMgrNvn.h @@ -0,0 +1,73 @@ +#pragma once + +// #include "sead/heap/seadHeap.h" +#include "seadFontBase.h" +#include "types.h" + +namespace sead { + + struct Heap; + struct DrawContext; + + class DebugFontMgrNvn : public FontBase { + public: + DebugFontMgrNvn(void); + + static DebugFontMgrNvn *sInstance; + + void initialize(sead::Heap *, const char *, const char*, unsigned int); + void initializeFromBinary(sead::Heap *, void *, unsigned long, void *, unsigned long, unsigned int); + static sead::DebugFontMgrNvn *createInstance(sead::Heap *); + void swapUniformBlockBuffer(void); + void begin(sead::DrawContext *) const; + void end(sead::DrawContext *) const; + + float getHeight(void) const { + return 16.f; + }; + float getWidth(void) const { + return 8.f; + }; + float getCharWidth(char16_t) const { + return 8.f; + }; + int getMaxDrawNum(void) const { + return 0x80; + }; + int getEncoding(void) const { + return 2; + }; + + }; + + class DebugFontMgrJis1Nvn : public FontBase { + public: + DebugFontMgrJis1Nvn(void); + + static DebugFontMgrJis1Nvn *sInstance; + + void initialize(sead::Heap *, const char *, const char *, const char *, unsigned int); + void initializeFromBinary(sead::Heap *,void *, ulong,void *, ulong,void const*, uint); + static sead::DebugFontMgrJis1Nvn *createInstance(sead::Heap *); + void swapUniformBlockBuffer(void); + void begin(sead::DrawContext *) const; + void end(sead::DrawContext *) const; + + float getHeight(void) const { + return 16.f; + }; + float getWidth(void) const { + return 8.f; + }; + + float getCharWidth(char16_t) const; + + int getMaxDrawNum(void) const { + return 0x80; + }; + int getEncoding(void) const { + return 2; + }; + + }; +} \ No newline at end of file diff --git a/include/sead/devenv/seadEnvUtil.h b/include/sead/devenv/seadEnvUtil.h new file mode 100644 index 0000000..2e7a9d9 --- /dev/null +++ b/include/sead/devenv/seadEnvUtil.h @@ -0,0 +1,18 @@ +#pragma once + +#include "prim/seadEnum.h" +#include "prim/seadSafeString.h" + +namespace sead +{ +SEAD_ENUM(RegionLanguageID, JPja, USen, USes, USfr, USpt, EUen, EUes, EUfr, EUde, EUit, EUpt, EUnl, EUru, KRko, CNzh, TWzh) + +class EnvUtil +{ +public: + static const SafeString& getRomType(); + static RegionLanguageID getRegionLanguage(); + static s32 getEnvironmentVariable(BufferedSafeString* out, const SafeString& variable); + static s32 resolveEnvronmentVariable(BufferedSafeString* out, const SafeString& str); +}; +} // namespace sead diff --git a/include/sead/devenv/seadFontBase.h b/include/sead/devenv/seadFontBase.h new file mode 100644 index 0000000..bf85989 --- /dev/null +++ b/include/sead/devenv/seadFontBase.h @@ -0,0 +1,8 @@ +#pragma once + +namespace sead +{ + class FontBase { + // no clue whats in this + }; +} // namespace sead diff --git a/include/sead/devenv/seadGameConfig.h b/include/sead/devenv/seadGameConfig.h new file mode 100644 index 0000000..78b0b24 --- /dev/null +++ b/include/sead/devenv/seadGameConfig.h @@ -0,0 +1,24 @@ +#pragma once + +#include "heap/seadDisposer.h" +#include "hostio/seadHostIONode.h" +#include "prim/seadSafeString.h" + +namespace sead +{ +class GameConfig : public hostio::Node +{ + SEAD_SINGLETON_DISPOSER(GameConfig) + GameConfig(); + virtual ~GameConfig(); + + static const SafeString cNodeName; + +protected: + struct FileWriteCallback + { + virtual ~FileWriteCallback(); + virtual void save(); + }; +}; +} // namespace sead diff --git a/include/sead/devenv/seadStackTrace.h b/include/sead/devenv/seadStackTrace.h new file mode 100644 index 0000000..35d88c7 --- /dev/null +++ b/include/sead/devenv/seadStackTrace.h @@ -0,0 +1,55 @@ +#pragma once + +#include "basis/seadTypes.h" +#include "container/seadSafeArray.h" + +namespace sead +{ +class StackTraceBase +{ +public: + StackTraceBase(); + virtual ~StackTraceBase() = default; + + virtual uintptr_t get(s32 index) const = 0; + virtual s32 size() const = 0; + + void trace(const void*); + +protected: + virtual void clear_() = 0; + virtual void push_(uintptr_t addr) = 0; + virtual bool isFull_() = 0; +}; + +template +class StackTrace : public StackTraceBase +{ +public: + ~StackTrace() override = default; + + uintptr_t get(s32 index) const override + { + if (index >= mSize) + return 0; + return mBuffer[index]; + } + + s32 size() const override { return mSize; } + +protected: + void clear_() override { mSize = 0; } + + void push_(uintptr_t addr) override + { + mBuffer[mSize] = addr; + ++mSize; + } + + bool isFull_() override { return mSize >= mBuffer.size(); } + +private: + SafeArray mBuffer{}; + s32 mSize{}; +}; +} // namespace sead diff --git a/include/sead/filedevice/cafe/seadCafeFSAFileDeviceCafe.h b/include/sead/filedevice/cafe/seadCafeFSAFileDeviceCafe.h new file mode 100644 index 0000000..13f8f35 --- /dev/null +++ b/include/sead/filedevice/cafe/seadCafeFSAFileDeviceCafe.h @@ -0,0 +1,65 @@ +#ifndef SEAD_CAFE_FSA_FILEDEVICE_H_ +#define SEAD_CAFE_FSA_FILEDEVICE_H_ + +#include + +#include +#include +#include +#include + +namespace sead +{ +class CafeFSAFileDevice : public FileDevice +{ + SEAD_RTTI_OVERRIDE(CafeFSAFileDevice, FileDevice) + +public: + CafeFSAFileDevice(const SafeString& name, const SafeString& devicePath); + virtual ~CafeFSAFileDevice() {} + + virtual bool doIsAvailable_() const; + virtual FileDevice* doOpen_(FileHandle* handle, const SafeString& path, FileOpenFlag flag); + virtual bool doClose_(FileHandle* handle); + virtual bool doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead); + virtual bool doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer, + u32 bytesToWrite); + virtual bool doSeek_(FileHandle* handle, s32 offset, SeekOrigin origin); + virtual bool doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle); + virtual bool doGetFileSize_(u32* fileSize, const SafeString& path); + virtual bool doGetFileSize_(u32* fileSize, FileHandle* handle); + virtual bool doIsExistFile_(bool* exists, const SafeString& path); + virtual bool doIsExistDirectory_(bool* exists, const SafeString& path); + virtual FileDevice* doOpenDirectory_(DirectoryHandle* handle, const SafeString& path); + virtual bool doCloseDirectory_(DirectoryHandle* handle); + virtual bool doReadDirectory_(u32* entriesRead, DirectoryHandle* handle, + DirectoryEntry* entries, u32 entriesToRead); + virtual bool doMakeDirectory_(const SafeString& path, u32); + virtual s32 doGetLastRawError_() const; + virtual void doResolvePath_(BufferedSafeString* out, const SafeString& path) const; + virtual void formatPathForFSA_(BufferedSafeString* out, const SafeString& path) const; + + FSClient* getUsableFSClient_() const; + FSFileHandle* getFileHandleInner_(FileHandle* handle); + FSDirHandle* getDirHandleInner_(DirectoryHandle* handle); + + const char* devicePath; + FSStatus status; + FSRetFlag openErrHandling; + FSRetFlag closeErrHandling; + FSRetFlag readErrHandling; + FSClient* client; +}; + +class CafeContentFileDevice : public CafeFSAFileDevice +{ + SEAD_RTTI_OVERRIDE(CafeContentFileDevice, CafeFSAFileDevice) + +public: + CafeContentFileDevice(); + virtual ~CafeContentFileDevice() {} +}; + +} // namespace sead + +#endif // SEAD_CAFE_FSA_FILEDEVICE_H_ diff --git a/include/sead/filedevice/nin/seadNinAocFileDeviceNin.h b/include/sead/filedevice/nin/seadNinAocFileDeviceNin.h new file mode 100644 index 0000000..85da708 --- /dev/null +++ b/include/sead/filedevice/nin/seadNinAocFileDeviceNin.h @@ -0,0 +1,14 @@ +#pragma once + +#include "filedevice/nin/seadNinFileDeviceBaseNin.h" + +namespace sead +{ +class NinAocFileDevice : public NinFileDeviceBase +{ + SEAD_RTTI_OVERRIDE(NinAocFileDevice, NinFileDeviceBase) + +public: + explicit NinAocFileDevice(const SafeString& mount); +}; +} // namespace sead diff --git a/include/sead/filedevice/nin/seadNinContentFileDeviceNin.h b/include/sead/filedevice/nin/seadNinContentFileDeviceNin.h new file mode 100644 index 0000000..1f4c7e3 --- /dev/null +++ b/include/sead/filedevice/nin/seadNinContentFileDeviceNin.h @@ -0,0 +1,14 @@ +#pragma once + +#include "filedevice/nin/seadNinFileDeviceBaseNin.h" + +namespace sead +{ +class NinContentFileDevice : public NinFileDeviceBase +{ + SEAD_RTTI_OVERRIDE(NinContentFileDevice, NinFileDeviceBase) + +public: + NinContentFileDevice(); +}; +} // namespace sead diff --git a/include/sead/filedevice/nin/seadNinFileDeviceBaseNin.h b/include/sead/filedevice/nin/seadNinFileDeviceBaseNin.h new file mode 100644 index 0000000..80650f4 --- /dev/null +++ b/include/sead/filedevice/nin/seadNinFileDeviceBaseNin.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include "filedevice/seadFileDevice.h" +#include "prim/seadSafeString.h" + +namespace sead +{ +class NinFileDeviceBase : public FileDevice +{ + SEAD_RTTI_OVERRIDE(NinFileDeviceBase, FileDevice) + +public: + NinFileDeviceBase(const SafeString& name, const SafeString& mount_point); + +protected: + struct FileHandleInner; + struct DirectoryHandleInner; + + bool doIsAvailable_() const override; + FileDevice* doOpen_(FileHandle* handle, const SafeString& path, FileOpenFlag flag) override; + bool doClose_(FileHandle* handle) override; + bool doFlush_(FileHandle* handle) override; + bool doRemove_(const SafeString& path) override; + bool doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead) override; + bool doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer, + u32 bytesToWrite) override; + bool doSeek_(FileHandle* handle, s32 offset, SeekOrigin origin) override; + bool doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle) override; + bool doGetFileSize_(u32* fileSize, const SafeString& path) override; + bool doGetFileSize_(u32* fileSize, FileHandle* handle) override; + bool doIsExistFile_(bool* exists, const SafeString& path) override; + bool doIsExistDirectory_(bool* exists, const SafeString& path) override; + FileDevice* doOpenDirectory_(DirectoryHandle* handle, const SafeString& path) override; + bool doCloseDirectory_(DirectoryHandle* handle) override; + bool doReadDirectory_(u32* entriesRead, DirectoryHandle* handle, DirectoryEntry* entries, + u32 entriesToRead) override; + bool doMakeDirectory_(const SafeString& path, u32 u_32) override; + s32 doGetLastRawError_() const override; + void doResolvePath_(BufferedSafeString* out, const SafeString& path) const override; + + virtual bool formatPathForFS_(BufferedSafeString* out, const SafeString& path) const; + + FileHandleInner* getFileHandleInner_(HandleBase* handle, bool construct = false) const; + DirectoryHandleInner* getDirectoryHandleInner_(HandleBase* handle, + bool construct = false) const; + + nn::Result mLastError = nn::ResultSuccess{}; + SafeString mMountPoint; +}; +} // namespace sead diff --git a/include/sead/filedevice/nin/seadNinHostIOFileDevice.h b/include/sead/filedevice/nin/seadNinHostIOFileDevice.h new file mode 100644 index 0000000..baca488 --- /dev/null +++ b/include/sead/filedevice/nin/seadNinHostIOFileDevice.h @@ -0,0 +1,17 @@ +#pragma once + +#include "filedevice/nin/seadNinFileDeviceBaseNin.h" + +namespace sead +{ +class NinHostIOFileDevice : public NinFileDeviceBase +{ + SEAD_RTTI_OVERRIDE(NinHostIOFileDevice, NinFileDeviceBase) + +public: + NinHostIOFileDevice(); + + bool doIsAvailable_() const override; + bool formatPathForFS_(BufferedSafeString* out, const SafeString& path) const override; +}; +} // namespace sead diff --git a/include/sead/filedevice/nin/seadNinSDFileDeviceNin.h b/include/sead/filedevice/nin/seadNinSDFileDeviceNin.h new file mode 100644 index 0000000..7362ccd --- /dev/null +++ b/include/sead/filedevice/nin/seadNinSDFileDeviceNin.h @@ -0,0 +1,16 @@ +#pragma once + +#include "filedevice/nin/seadNinFileDeviceBaseNin.h" + +namespace sead +{ +class NinSDFileDevice : public NinFileDeviceBase +{ + SEAD_RTTI_OVERRIDE(NinSDFileDevice, NinFileDeviceBase) + +public: + NinSDFileDevice(); + + bool doIsAvailable_() const override; +}; +} // namespace sead diff --git a/include/sead/filedevice/nin/seadNinSaveFileDeviceNin.h b/include/sead/filedevice/nin/seadNinSaveFileDeviceNin.h new file mode 100644 index 0000000..e36daac --- /dev/null +++ b/include/sead/filedevice/nin/seadNinSaveFileDeviceNin.h @@ -0,0 +1,15 @@ +#pragma once + +#include "filedevice/nin/seadNinFileDeviceBaseNin.h" + +namespace sead +{ +class NinSaveFileDevice : public NinFileDeviceBase +{ + SEAD_RTTI_OVERRIDE(NinSaveFileDevice, NinFileDeviceBase) + +public: + explicit NinSaveFileDevice(const SafeString& mount); + bool tryCommit(); +}; +} // namespace sead diff --git a/include/sead/filedevice/seadArchiveFileDevice.h b/include/sead/filedevice/seadArchiveFileDevice.h new file mode 100644 index 0000000..4758e33 --- /dev/null +++ b/include/sead/filedevice/seadArchiveFileDevice.h @@ -0,0 +1,55 @@ +#pragma once + +#include "filedevice/seadFileDevice.h" + +namespace sead +{ +class ArchiveRes; + +class ArchiveFileDevice : public FileDevice +{ + SEAD_RTTI_OVERRIDE(ArchiveFileDevice, FileDevice) +public: + explicit ArchiveFileDevice(ArchiveRes* archive_res); + ~ArchiveFileDevice() override = default; + + u8* tryLoadWithEntryID(s32 id, LoadArg& arg); + FileDevice* tryOpenWithEntryID(FileHandle* handle, s32 id, FileOpenFlag flag, u32 div_size); + s32 tryConvertPathToEntryID(const SafeString& path); + bool setCurrentDirectory(const SafeString& dir); + +protected: + struct ArchiveFileHandle; + + bool doIsAvailable_() const override { return true; } + u8* doLoad_(LoadArg& arg) override; + FileDevice* doOpen_(FileHandle* handle, const SafeString& path, FileOpenFlag flag) override; + bool doClose_(FileHandle* handle) override; + bool doFlush_(FileHandle* handle) override; + bool doRemove_(const SafeString& str) override; + bool doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead) override; + bool doWrite_(u32*, FileHandle*, const u8*, u32) override { return false; } + bool doSeek_(FileHandle* handle, s32 offset, SeekOrigin origin) override; + bool doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle) override; + bool doGetFileSize_(u32* fileSize, const SafeString& path) override; + bool doGetFileSize_(u32* fileSize, FileHandle* handle) override; + bool doIsExistFile_(bool* exists, const SafeString& path) override; + bool doIsExistDirectory_(bool* exists, const SafeString& path) override; + FileDevice* doOpenDirectory_(DirectoryHandle* handle, const SafeString& path) override; + bool doCloseDirectory_(DirectoryHandle* handle) override; + bool doReadDirectory_(u32* entriesRead, DirectoryHandle* handle, DirectoryEntry* entry, + u32 entriesToRead) override; + bool doMakeDirectory_(const SafeString& path, u32 u_32) override; + s32 doGetLastRawError_() const override; + + virtual u8* doLoadWithEntryID_(s32 id, LoadArg& arg); + virtual FileDevice* doOpenWithEntryID_(FileHandle* handle, s32 id, FileOpenFlag flag); + virtual s32 doConvertPathToEntryID_(const SafeString& path); + virtual bool doSetCurrentDirectory_(const SafeString& path); + + ArchiveFileHandle* getArchiveFileHandle_(FileHandle* handle) const; + ArchiveFileHandle* constructArchiveFileHandle_(FileHandle* handle) const; + + ArchiveRes* mArchive; +}; +} // namespace sead diff --git a/include/sead/filedevice/seadFileDevice.h b/include/sead/filedevice/seadFileDevice.h new file mode 100644 index 0000000..79d0226 --- /dev/null +++ b/include/sead/filedevice/seadFileDevice.h @@ -0,0 +1,419 @@ +#ifndef SEAD_FILEDEVICE_H_ +#define SEAD_FILEDEVICE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace sead +{ +class FileDevice; + +using HandleBuffer = SafeArray; + +class HandleBase +{ +public: + HandleBase() = default; + HandleBase(const HandleBase&) = delete; + HandleBase& operator=(const HandleBase&) = delete; + virtual ~HandleBase() = default; + + FileDevice* getDevice() const { return mDevice; } + FileDevice* getOriginalDevice() const { return mOriginalDevice; } + bool isOpened() const { return mOriginalDevice != nullptr; } + +protected: + friend class FileDevice; + + FileDevice* mDevice = nullptr; + FileDevice* mOriginalDevice = nullptr; + HandleBuffer mHandleBuffer{}; +}; + +class FileHandle; +class DirectoryHandle; +struct DirectoryEntry; + +class FileDevice : public TListNode, public IDisposer +{ + SEAD_RTTI_BASE(FileDevice) + +public: + enum FileOpenFlag + { + cFileOpenFlag_ReadOnly = 0, // r + cFileOpenFlag_WriteOnly = 1, // w + cFileOpenFlag_ReadWrite = 2, // r+ + cFileOpenFlag_Create = 3 // w+ + }; + + enum SeekOrigin + { + cSeekOrigin_Begin = 0, + cSeekOrigin_Current = 1, + cSeekOrigin_End = 2 + }; + + struct LoadArg + { + SafeString path; + u8* buffer = nullptr; + u32 buffer_size = 0; + Heap* heap = nullptr; + s32 alignment = 0; + s32 buffer_size_alignment = 0; + /// Read chunk size + u32 div_size = 0; + bool assert_on_alloc_fail = true; + bool check_read_entire_file = true; + u32 read_size = 0; + u32 roundup_size = 0; + bool need_unload = false; + }; + + struct SaveArg + { + SafeString path = ""; + const u8* buffer = nullptr; + u32 buffer_size = 0; + u32 write_size = 0; + }; + +public: + FileDevice() : TListNode(this), IDisposer(), mDriveName(), mPermission(true) {} + + explicit FileDevice(const SafeString& name) + : TListNode(this), IDisposer(), mDriveName(), mPermission(true) + { + setDriveName(name); + } + + ~FileDevice() override; + + const SafeString& getDriveName() const { return mDriveName; } + void setDriveName(const SafeString& name) + { +#ifdef SEAD_DEBUG + if (name.include(':')) + SEAD_WARN("drive name should not include ':'. (in %s)", name.cstr()); +#endif + mDriveName = name; + } + + bool hasPermission() const { return mPermission; } + void setHasPermission(bool perm) { mPermission = perm; } + + bool isAvailable() const; + + u8* tryLoad(LoadArg& arg); + + bool save(SaveArg& arg) + { + if (!trySave(arg)) + { + SEAD_ASSERT_MSG(false, "file save error"); + return false; + } + return true; + } + bool trySave(SaveArg& arg); + + FileDevice* open(FileHandle* handle, const SafeString& path, FileOpenFlag flag, u32 divSize = 0) + { + auto* device = tryOpen(handle, path, flag, divSize); + if (!device) + { + SEAD_ASSERT_MSG(false, "file open error"); + return nullptr; + } + return device; + } + FileDevice* tryOpen(FileHandle* handle, const SafeString& path, FileOpenFlag flag, + u32 divSize = 0); + + bool close(FileHandle* handle) + { + if (!tryClose(handle)) + { + SEAD_ASSERT_MSG(false, "file close error"); + return false; + } + return true; + } + bool tryClose(FileHandle* handle); + + bool flush(FileHandle* handle) + { + if (!tryFlush(handle)) + { + SEAD_ASSERT_MSG(false, "file flush error"); + return false; + } + return true; + } + bool tryFlush(FileHandle* handle); + + bool remove(const SafeString& str) + { + if (!tryRemove(str)) + { + SEAD_ASSERT_MSG(false, "file remove error"); + return false; + } + return true; + } + bool tryRemove(const SafeString& str); + + u32 read(FileHandle* handle, u8* data, u32 size) + { + u32 bytes_read = 0; + if (!tryRead(&bytes_read, handle, data, size)) + SEAD_ASSERT_MSG(false, "file read error"); + return bytes_read; + } + bool tryRead(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead); + + u32 write(FileHandle* handle, const u8* data, u32 size) + { + u32 bytes_written = 0; + if (!tryWrite(&bytes_written, handle, data, size)) + SEAD_ASSERT_MSG(false, "file write error"); + return bytes_written; + } + bool tryWrite(u32* bytesWritten, FileHandle* handle, const u8* inBuffer, u32 bytesToWrite); + + bool seek(FileHandle* handle, s32 offset, SeekOrigin origin) + { + if (!trySeek(handle, offset, origin)) + { + SEAD_ASSERT_MSG(false, "file seek error"); + return false; + } + return true; + } + bool trySeek(FileHandle* handle, s32 offset, SeekOrigin origin); + + u32 getCurrentSeekPos(FileHandle* handle) + { + u32 seek_pos = 0; + if (!tryGetCurrentSeekPos(&seek_pos, handle)) + SEAD_ASSERT_MSG(false, "tryGetCurrentSeekPos error"); + return seek_pos; + } + bool tryGetCurrentSeekPos(u32* seekPos, FileHandle* handle); + + u32 getFileSize(const SafeString& path) + { + u32 file_size = 0; + if (!tryGetFileSize(&file_size, path)) + SEAD_ASSERT_MSG(false, "tryGetFileSize error"); + return file_size; + } + bool tryGetFileSize(u32* fileSize, const SafeString& path); + + u32 getFileSize(FileHandle* handle) + { + u32 file_size = 0; + if (!tryGetFileSize(&file_size, handle)) + SEAD_ASSERT_MSG(false, "tryGetFileSize error"); + return file_size; + } + bool tryGetFileSize(u32* size, FileHandle* handle); + + bool isExistFile(const SafeString& path) + { + bool exists = false; + if (!tryIsExistFile(&exists, path)) + SEAD_ASSERT_MSG(false, "tryIsExistFile error"); + return exists; + } + bool tryIsExistFile(bool* exists, const SafeString& path); + + bool isExistDirectory(const SafeString& path) + { + bool exists = false; + if (!tryIsExistDirectory(&exists, path)) + SEAD_ASSERT_MSG(false, "isExistDirectory failed"); + return exists; + } + bool tryIsExistDirectory(bool* exists, const SafeString& path); + + FileDevice* openDirectory(DirectoryHandle* handle, const SafeString& path) + { + auto* device = tryOpenDirectory(handle, path); + if (!device) + { + SEAD_ASSERT_MSG(false, "directory open error"); + return nullptr; + } + return device; + } + FileDevice* tryOpenDirectory(DirectoryHandle* handle, const SafeString& path); + + bool closeDirectory(DirectoryHandle* handle) + { + if (!tryCloseDirectory(handle)) + { + SEAD_ASSERT_MSG(false, "directory close error"); + return false; + } + return true; + } + bool tryCloseDirectory(DirectoryHandle* handle); + + u32 readDirectory(DirectoryHandle* handle, DirectoryEntry* entries, u32 num_entries) + { + u32 entries_read = 0; + if (!tryReadDirectory(&entries_read, handle, entries, num_entries)) + SEAD_ASSERT_MSG(false, "directory read error"); + return entries_read; + } + bool tryReadDirectory(u32* entriesRead, DirectoryHandle* handle, DirectoryEntry* entries, + + u32 entriesToRead); + + bool makeDirectory(const SafeString& path, u32 x) + { + if (!tryMakeDirectory(path, x)) + { + SEAD_ASSERT_MSG(false, "directory make error"); + return false; + } + return true; + } + bool tryMakeDirectory(const SafeString& path, u32); + + bool makeDirectoryWithParent(const SafeString& path, u32 x) + { + if (!tryMakeDirectoryWithParent(path, x)) + { + SEAD_ASSERT_MSG(false, "directory make with parent error"); + return false; + } + return true; + } + bool tryMakeDirectoryWithParent(const SafeString& path, u32); + + s32 getLastRawError() const; + + virtual void traceFilePath(const SafeString& path) const; + virtual void traceDirectoryPath(const SafeString& path) const; + virtual void resolveFilePath(BufferedSafeString* out, const SafeString& path) const; + virtual void resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const; + virtual bool isMatchDevice_(const HandleBase* handle) const; + +#ifdef SWITCH + static const s32 cBufferMinAlignment = 0x20; +#else + static const s32 cBufferMinAlignment = 0x40; +#endif + +protected: + virtual bool doIsAvailable_() const = 0; + virtual u8* doLoad_(LoadArg& arg); + virtual bool doSave_(SaveArg& arg); + virtual FileDevice* doOpen_(FileHandle* handle, const SafeString& path, FileOpenFlag flag) = 0; + virtual bool doClose_(FileHandle* handle) = 0; + virtual bool doFlush_(FileHandle* handle) = 0; + virtual bool doRemove_(const SafeString& str) = 0; + virtual bool doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead) = 0; + virtual bool doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer, + u32 bytesToWrite) = 0; + virtual bool doSeek_(FileHandle* handle, s32 offset, SeekOrigin origin) = 0; + virtual bool doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle) = 0; + virtual bool doGetFileSize_(u32* fileSize, const SafeString& path) = 0; + virtual bool doGetFileSize_(u32* fileSize, FileHandle* handle) = 0; + virtual bool doIsExistFile_(bool* exists, const SafeString& path) = 0; + virtual bool doIsExistDirectory_(bool* exists, const SafeString& path) = 0; + virtual FileDevice* doOpenDirectory_(DirectoryHandle* handle, const SafeString& path) = 0; + virtual bool doCloseDirectory_(DirectoryHandle* handle) = 0; + virtual bool doReadDirectory_(u32* entriesRead, DirectoryHandle* handle, + DirectoryEntry* entries, u32 entriesToRead) = 0; + virtual bool doMakeDirectory_(const SafeString& path, u32) = 0; + virtual s32 doGetLastRawError_() const = 0; + virtual void doTracePath_(const SafeString& path) const; + virtual void doResolvePath_(BufferedSafeString* out, const SafeString& path) const; + + void setFileHandleDivSize_(FileHandle* handle, u32 divSize) const; + void setHandleBaseFileDevice_(HandleBase* handle, FileDevice* device) const; + void setHandleBaseOriginalFileDevice_(HandleBase* handle, FileDevice* device) const; + HandleBuffer& getHandleBaseHandleBuffer_(HandleBase* handle) const; + + FixedSafeString<32> mDriveName; + bool mPermission; +}; + +class FileHandle : public HandleBase +{ +public: + FileHandle() : HandleBase(), mDivSize(0) {} + + virtual ~FileHandle() + { + FileDevice* _device = mOriginalDevice; + if (_device != NULL) + _device->tryClose(this); + } + + bool close(); + bool tryClose(); + + bool flush(); + bool tryFlush(); + + u32 read(u8* outBuffer, u32 bytesToRead); + bool tryRead(u32* actual_size, u8* data, u32 size); + + u32 write(const u8* data, u32 size); + bool tryWrite(u32* actual_size, const u8* data, u32 size); + + bool seek(s32 offset, FileDevice::SeekOrigin origin); + bool trySeek(s32 offset, FileDevice::SeekOrigin origin); + u32 getCurrentSeekPos(); + bool tryGetCurrentSeekPos(u32* pos); + + u32 getFileSize(); + bool tryGetFileSize(u32* size); + + u32 getDivSize() const { return mDivSize; } + +protected: + friend class FileDevice; + + s32 mDivSize; +}; + +class DirectoryHandle : public HandleBase +{ +public: + DirectoryHandle() : HandleBase() {} + + virtual ~DirectoryHandle() + { + FileDevice* _device = mOriginalDevice; + if (_device != NULL) + _device->tryCloseDirectory(this); + } + + bool close(); + bool tryClose(); + u32 read(DirectoryEntry* entries, u32 count); + bool tryRead(u32* actual_count, DirectoryEntry* entries, u32 count); +}; + +struct DirectoryEntry +{ + DirectoryEntry() : name(), is_directory(false) {} + + FixedSafeString<256> name; + bool is_directory; +}; + +} // namespace sead + +#endif // SEAD_FILEDEVICE_H_ diff --git a/include/sead/filedevice/seadFileDeviceMgr.h b/include/sead/filedevice/seadFileDeviceMgr.h new file mode 100644 index 0000000..89c2ece --- /dev/null +++ b/include/sead/filedevice/seadFileDeviceMgr.h @@ -0,0 +1,81 @@ +#ifndef SEAD_FILEDEVICEMGR_H_ +#define SEAD_FILEDEVICEMGR_H_ + +#ifdef cafe +#include +#endif // cafe + +#include +#include +#include +#include +#include +#include + +namespace sead +{ +class FileDeviceMgr +{ + SEAD_SINGLETON_DISPOSER(FileDeviceMgr) + FileDeviceMgr(); + ~FileDeviceMgr(); + +public: + void traceFilePath(const SafeString& path) const; + void traceDirectoryPath(const SafeString& path) const; + void resolveFilePath(BufferedSafeString* out, const SafeString& path) const; + void resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const; + + void mount(FileDevice* device, const SafeString& name = SafeString::cEmptyString); + void unmount(FileDevice* device); + void unmount(const SafeString& name); + FileDevice* findDeviceFromPath(const SafeString& path, BufferedSafeString* pathNoDrive) const; + FileDevice* findDevice(const SafeString& name) const; + + FileDevice* tryOpen(FileHandle* handle, const SafeString& path, FileDevice::FileOpenFlag flag, + u32 divSize); + FileDevice* tryOpenDirectory(DirectoryHandle* handle, const SafeString& path); + + u8* tryLoad(FileDevice::LoadArg& arg); + void unload(u8* data); + bool trySave(FileDevice::SaveArg& arg); + + void mountSaveDataForDebug(Heap* heap); + void unmountSaveDataForDebug(); + + FileDevice* getMainFileDevice() const { return mMainFileDevice; } + FileDevice* getDefaultFileDevice() const { return mDefaultFileDevice; } + void setDefaultFileDevice(FileDevice* device) { mDefaultFileDevice = device; } + +#ifdef NNSDK + bool hasMountedHost() const { return mMountedHost; } + bool hasMountedSd() const { return mMountedSd; } +#endif + +private: + typedef TList DeviceList; + + void mount_(Heap* heap); + void unmount_(); + + DeviceList mDeviceList{}; + FileDevice* mDefaultFileDevice = nullptr; + MainFileDevice* mMainFileDevice = nullptr; + +#ifdef cafe + static void stateChangeCallback_(FSClient* client, FSVolumeState state, void* context); + + FSClient client; + u8 _1724[128]; + u8 _17A4[128]; + u32 _1824; +#elif defined(NNSDK) + u8* mRomCache = nullptr; + bool mMountedHost = false; + bool mMountedSd = false; +#endif +}; + +} // namespace sead + +#endif // SEAD_FILEDEVICEMGR_H_ diff --git a/include/sead/filedevice/seadMainFileDevice.h b/include/sead/filedevice/seadMainFileDevice.h new file mode 100644 index 0000000..0acf826 --- /dev/null +++ b/include/sead/filedevice/seadMainFileDevice.h @@ -0,0 +1,112 @@ +#ifndef SEAD_MAIN_FILEDEVICE_H_ +#define SEAD_MAIN_FILEDEVICE_H_ + +#include +#include +#include +#include + +namespace sead +{ +class MainFileDevice : public FileDevice +{ + SEAD_RTTI_OVERRIDE(MainFileDevice, FileDevice) + +public: + explicit MainFileDevice(Heap* heap); + ~MainFileDevice() override; + + void traceFilePath(const SafeString& path) const override; + void traceDirectoryPath(const SafeString& path) const override; + void resolveFilePath(BufferedSafeString* out, const SafeString& path) const override; + void resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const override; + +protected: + bool doIsAvailable_() const override { return mFileDevice->isAvailable(); } + + FileDevice* doOpen_(FileHandle* handle, const SafeString& path, FileOpenFlag flag) override + { + return mFileDevice->tryOpen(handle, path, flag, handle->getDivSize()); + } + + bool doClose_(FileHandle* handle) override { return mFileDevice->tryClose(handle); } + + bool doFlush_(FileHandle* handle) override { return mFileDevice->tryFlush(handle); } + + bool doRemove_(const SafeString& str) override { return mFileDevice->tryRemove(str); } + + bool doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead) override + { + return mFileDevice->tryRead(bytesRead, handle, outBuffer, bytesToRead); + } + + bool doWrite_(u32* bytesWritten, FileHandle* handle, const u8* inBuffer, + u32 bytesToWrite) override + { + return mFileDevice->tryWrite(bytesWritten, handle, inBuffer, bytesToWrite); + } + + bool doSeek_(FileHandle* handle, s32 offset, SeekOrigin origin) override + { + return mFileDevice->trySeek(handle, offset, origin); + } + + bool doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle) override + { + return mFileDevice->tryGetCurrentSeekPos(seekPos, handle); + } + + bool doGetFileSize_(u32* fileSize, const SafeString& path) override + { + return mFileDevice->tryGetFileSize(fileSize, path); + } + + bool doGetFileSize_(u32* fileSize, FileHandle* handle) override + { + return mFileDevice->tryGetFileSize(fileSize, handle); + } + + bool doIsExistFile_(bool* exists, const SafeString& path) override + { + return mFileDevice->tryIsExistFile(exists, path); + } + + bool doIsExistDirectory_(bool* exists, const SafeString& path) override + { + return mFileDevice->tryIsExistDirectory(exists, path); + } + + FileDevice* doOpenDirectory_(DirectoryHandle* handle, const SafeString& path) override + { + return mFileDevice->tryOpenDirectory(handle, path); + } + + bool doCloseDirectory_(DirectoryHandle* handle) override + { + return mFileDevice->tryCloseDirectory(handle); + } + + bool doReadDirectory_(u32* entriesRead, DirectoryHandle* handle, DirectoryEntry* entries, + u32 entriesToRead) override + { + return mFileDevice->tryReadDirectory(entriesRead, handle, entries, entriesToRead); + } + + bool doMakeDirectory_(const SafeString& path, u32 x) override + { + return mFileDevice->tryMakeDirectory(path, x); + } + + s32 doGetLastRawError_() const override { return mFileDevice->getLastRawError(); } + + bool isMatchDevice_(const HandleBase* handle) const override + { + return mFileDevice->isMatchDevice_(handle); + } + + FileDevice* mFileDevice; +}; + +} // namespace sead + +#endif // SEAD_MAIN_FILEDEVICE_H_ diff --git a/include/sead/filedevice/seadPath.h b/include/sead/filedevice/seadPath.h new file mode 100644 index 0000000..e54ad18 --- /dev/null +++ b/include/sead/filedevice/seadPath.h @@ -0,0 +1,22 @@ +#ifndef SEAD_PATH_H_ +#define SEAD_PATH_H_ + +#include + +namespace sead +{ +class Path +{ +public: + static bool getDriveName(BufferedSafeString* drive_name, const SafeString& path); + static void getPathExceptDrive(BufferedSafeString* out, const SafeString& path); + static bool getExt(BufferedSafeString* ext, const SafeString& path); + static bool getFileName(BufferedSafeString* name, const SafeString& path); + static bool getBaseFileName(BufferedSafeString* name, const SafeString& path); + static bool getDirectoryName(BufferedSafeString* name, const SafeString& path); + static void join(BufferedSafeString* out, const char* path1, const char* path2); + static void changeDelimiter(BufferedSafeString* out, char delimiter); +}; +} // namespace sead + +#endif // SEAD_PATH_H_ diff --git a/include/sead/framework/seadCalculateTask.h b/include/sead/framework/seadCalculateTask.h new file mode 100644 index 0000000..91e8012 --- /dev/null +++ b/include/sead/framework/seadCalculateTask.h @@ -0,0 +1,32 @@ +#pragma once + +#include "framework/seadMethodTree.h" +#include "framework/seadTaskBase.h" + +namespace sead +{ +class CalculateTask : public TaskBase +{ + SEAD_RTTI_OVERRIDE(CalculateTask, TaskBase) +public: + explicit CalculateTask(const TaskConstructArg& arg); + CalculateTask(const TaskConstructArg& arg, const char* name); + ~CalculateTask() override; + void pauseCalc(bool b) override; + void pauseDraw(bool b) override; + void pauseCalcRec(bool b) override; + void pauseDrawRec(bool b) override; + void pauseCalcChild(bool b) override; + void pauseDrawChild(bool b) override; + void attachCalcImpl() override; + void attachDrawImpl() override; + void detachCalcImpl() override; + void detachDrawImpl() override; + const RuntimeTypeInfo::Interface* getCorrespondingMethodTreeMgrTypeInfo() const override; + MethodTreeNode* getMethodTreeNode(s32 method_type) override; + virtual void calc() {} + +protected: + MethodTreeNode mCalcNode{nullptr}; +}; +} // namespace sead diff --git a/include/sead/framework/seadFramework.h b/include/sead/framework/seadFramework.h new file mode 100644 index 0000000..37344ff --- /dev/null +++ b/include/sead/framework/seadFramework.h @@ -0,0 +1,91 @@ +#ifndef SEAD_FRAMEWORK_H_ +#define SEAD_FRAMEWORK_H_ + +//#include +#include +//#include +//#include +//#include +//#include +#include +#include +#include +#include <{g1;`={!1} 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 Xx^X9`G{pX(6q*LVGspD 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

UVaG`Evy*j11(xm*KW`o8xqNFj=5#0XUyXZ8 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

^#NF?|tVXkglHm1n0rLl3thUgqoH*f5> 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


m*k~{tx`F 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 FzXSb!;>7>} literal 0 HcmV?d00001 diff --git a/romfs/DebugData/Font/nvn_font_jis1_tbl.bin b/romfs/DebugData/Font/nvn_font_jis1_tbl.bin new file mode 100644 index 0000000000000000000000000000000000000000..49fc0616935dc74d84b661d162e6cac89b8f1aeb GIT binary patch literal 7442 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 literal 0 HcmV?d00001 diff --git a/romfs/DebugData/Font/nvn_font_shader.bin b/romfs/DebugData/Font/nvn_font_shader.bin new file mode 100644 index 0000000000000000000000000000000000000000..596ec38b93f7ff85f146bf70cc19c08b9d97455f GIT binary patch literal 8448 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 XWgCGYi%{bV3-Y{0h07gCG70}1G=Ofp literal 0 HcmV?d00001 diff --git a/scripts/calcJump.py b/scripts/calcJump.py new file mode 100644 index 0000000..51d7e9a --- /dev/null +++ b/scripts/calcJump.py @@ -0,0 +1,49 @@ +import sys, os + +subsdk0Offset = { + "310" : 0x48DF000 +} + +DEFAULT_VERS = "310" +SOURCE_DIR = "source" +HOOK_MAGIC = "// hook_from " + +def calcJump(from_addr_str, dest_func, vers=DEFAULT_VERS): + from_addr = int(from_addr_str, 16) + dest_func = dest_func + "(" + + mapFilePath = "build" + vers + "/starlight" + vers + ".map" + with open(mapFilePath, 'r') as f: + mapFile = f.read() + + foundPos = mapFile.find(dest_func) - 34 + foundLine = mapFile[foundPos:mapFile.find("\n", foundPos)] + + print("Found:") + print(foundLine) + + func_addr = int(foundLine[:foundLine.find(" ")], 0) + jump_offset = subsdk0Offset[vers] + func_addr - from_addr + print("Jump needed: " + hex(jump_offset)) + +if len(sys.argv) > 3: + calcJump(sys.argv[1], sys.argv[2], sys.argv[3]) +elif len(sys.argv) > 2: + calcJump(sys.argv[1], sys.argv[2]) +else: + hasOutput = False + for root, subdirs, files in os.walk(SOURCE_DIR): + for file in files: + with open(root+"/"+file, 'r') as f: + file_iter = iter(f.readlines()) + for line in file_iter: + if HOOK_MAGIC in line: + hook_addr = line[len(HOOK_MAGIC):-1] + line = next(file_iter) + hook_func = line[:line.find('(')] + hook_func = hook_func[hook_func.rfind(' ') + 1:] + calcJump(hook_addr, hook_func) + hasOutput = True + + if not hasOutput: + print("Usage: %s [from addr] [to func name] (s2 vers, like '310')" % sys.argv[0]) diff --git a/scripts/createActorTable.py b/scripts/createActorTable.py new file mode 100644 index 0000000..2ee2337 --- /dev/null +++ b/scripts/createActorTable.py @@ -0,0 +1,48 @@ +# createActorTable.py +# Generates an .h file for defining an array of actors and their creation functions. + +arr = [] +names = [] +classes = [] +classes_al = [] + +with open("createActorFuncs.txt", "r") as f: + lines = f.readlines() + +for line in lines: + sym = line.split("=") + + blah = sym[1].split("<") + + names.append(blah[1].strip("al::")) + + if "al::" in blah[1]: + classes_al.append(blah[1].strip("al::")) + else: + classes.append(blah[1]) + + # this line is awful + arr.append("\t\t{ (char*)" + "\"" + blah[1].strip("al::\n") + "\"," + " (void*)al::createActorFunction<" + blah[1].strip("\n") + "> },\n") + +with open("funcs.h", "w") as w: + w.write("namespace al\n") + w.write("{\n") + + for c in classes_al: + c = c.strip("\n") + w.write(f"\tclass {c};\n") + + w.write("};\n\n") + + for c in classes: + c = c.strip("\n") + w.write(f"class {c};\n") + + w.write("\n") + + w.write("ActorEntry entries[NUM_ACTORS] = {\n") + + for e in arr: + w.write(e) + + w.write("};") \ No newline at end of file diff --git a/scripts/genLinkerScript.py b/scripts/genLinkerScript.py new file mode 100644 index 0000000..f2048f1 --- /dev/null +++ b/scripts/genLinkerScript.py @@ -0,0 +1,83 @@ +# genLinkerScript.py +# Generates a linker script based on a given symbol map and base address offset +import sys + +symbolExceptions = [ "_init", + "_fini", + "__nnDetailNintendoSdkRuntimeObjectFileRefer", + "__nnDetailNintendoSdkRuntimeObjectFile", + "__nnDetailNintendoSdkNsoFileRefer" +] +symbolPrefixes = [ + "Curl", # some curl stuff + "curl", # moar curl + # "_ZG", # guards + # "_ZN", # regular variables + # "_ZT", # typeinfo / vtables + # "_ZZ", + "_Z", # literally everything + "pfnc_", + "mem", + "str" + "alloc", + "malloc", + "free", + "calloc", + "realloc", + "aligned_alloc", + "malloc_usable_size" +] + +if len(sys.argv) < 3: + print("Syntax: genLinkerScript.py \n") + sys.exit() + +# open our map file +with open(sys.argv[1], "r") as f: + lines = f.readlines() + +# open our new generated linker +with open("syms.ld", "w") as linker: + + # we need to skip a few lines first + curLine = 0 + for line in lines: + # each line is structured as such: + # 00000000:0000007100000160 _fini + firstSplit = line.split(" ") + if len(firstSplit) <= 1: + continue + + addressSplit = firstSplit[1].split(":") + if len(addressSplit) <= 1: + continue + + address = addressSplit[1] + + symbolName = firstSplit[8] + symbolName = symbolName.strip("\n\r") + + # the last few numbers are important, the higher ones are not + address = address[9:] + address = "0x" + address + + # we check to see if this symbol has a prefix that we need to keep + if (any([symbolName.startswith(symbolPrefix) + for symbolPrefix in symbolPrefixes]) + or symbolName in symbolExceptions): + + # skip the first 5 useless lines + if curLine < 5: + curLine += 1 + continue + + # sdk functions have a _0 at the end because they are duplicated + # due to the originals being imported, so IDA adds a _0 at the + # end... we need to strip this + if symbolName.endswith("_0"): + symbolName = symbolName[:-2] + + linker.write( + "{} = {} - {};\n".format(symbolName, address, sys.argv[2])) + +print("Linker script generated.") diff --git a/scripts/genPatch.py b/scripts/genPatch.py new file mode 100644 index 0000000..c385777 --- /dev/null +++ b/scripts/genPatch.py @@ -0,0 +1,268 @@ +import os, sys, re, ctypes, struct +from keystone import * + +class Patch: + def __init__(self, offset, length, content): + self.offset = offset + self.length = length + self.content = content + +# consts +PATCH_DIR = "patches" +PATCH_EXTENSION = ".slpatch" + +NSO_HEADER_LEN = 0x100 +PATCH_CONFIG_DIR = os.path.join(PATCH_DIR, "configs") +PATCH_NSO_MAP_DIR = os.path.join(PATCH_DIR, "maps") +PATCH_CONFIG_EXTENSION = ".config" + +IPS_OUT_DIR_NAME = "starlight_patch_{}" +IPS_FORMAT = ".ips" +IPS_HEADER_MAGIC = bytes("IPS32", 'ASCII') +IPS_EOF_MAGIC = bytes("EEOF", 'ASCII') + +# globals +buildVersion = None +patchConfig = { + "build_id" : {}, + "nso_load_addr" : {}, + "map_file" : {} +} +patchList = {} + +def initConfig(): + configPath = os.path.join(PATCH_CONFIG_DIR, buildVersion + PATCH_CONFIG_EXTENSION) + # read config file + with open(configPath) as configFile: + curConfigName = None + for line in configFile: + line = line.strip() + configNameLineMatch = re.match(r'\[(.+)\]', line) + if configNameLineMatch: + curConfigName = configNameLineMatch.group(1) + continue + + if '=' in line: + configNSO, configValue = line.split('=', 1) + if not configNSO.isalnum(): + continue + if '+' in configValue: + print("genPatch.py error:", line, "awaits implementation") + sys.exit(-1) + patchConfig[curConfigName][configNSO] = configValue + + # read nso map files + curVerMapDir = os.path.join(PATCH_NSO_MAP_DIR, buildVersion) + for file in os.listdir(curVerMapDir): + if file.endswith('.map'): + with open(os.path.join(curVerMapDir, file), 'r') as f: + patchConfig['map_file'][file[:-4]] = f.read() + +# returns symbol at it's loaded addr relative to main +def getSymAddrFromMap(target, regexStr, symStr): + mapFile = None + def getFoundPosAddr(start): + regexMatch = re.search(regexStr, mapFile[start:]) + if not regexMatch: + return -1, -1 + + foundLinePos = mapFile.rfind('\n', 0, regexMatch.span()[0] + start) + 1 + foundLineEndPos = mapFile.find('\n', regexMatch.span()[1] + start) + foundLine = mapFile[foundLinePos:foundLineEndPos].strip() + foundAddr = int(foundLine[:foundLine.find(" ")].replace("00000000:00000071", ''), 16) + + return foundLineEndPos, foundAddr + + if target in patchConfig['map_file']: + mapFile = patchConfig['map_file'][target] + loadAddrAdjustment = int(patchConfig["nso_load_addr"][target], 16) + else: + mapFile = SLMapFile + loadAddrAdjustment = int(patchConfig["nso_load_addr"]["subsdk1"], 16) + + foundPos, firstFoundAddr = getFoundPosAddr(0) + if foundPos == -1: + raise NameError + + # check uniqueness + while True: + foundPos, moreFoundAddr = getFoundPosAddr(foundPos) + if foundPos == -1: + break + + if moreFoundAddr != firstFoundAddr: + print("genPatch.py error:", symStr, "is ambiguous!") + sys.exit(-1) + + # map stores signed address relative to starlight as unsigned? + return loadAddrAdjustment + ctypes.c_long(firstFoundAddr).value + +def resolveAddressAndTarget(target, symbolStr): + resolvedAddr = 0 + + # resolve + offset + plusSplit = symbolStr.split('+', 1) + if len(plusSplit) > 1: + symbolStr = plusSplit[0] + resolvedAddr += int(plusSplit[1], 16) + + # resolve different nso target with '!' notation + targetSplit = symbolStr.split('!', 1) + if len(targetSplit) > 1: + target = targetSplit[0] + symbolStr = targetSplit[1] + else: + # if symbol is one of the nso targets + if symbolStr in patchConfig["nso_load_addr"]: + target = symbolStr + resolvedAddr += int(patchConfig["nso_load_addr"][symbolStr], 16) + return target, resolvedAddr + + # if symbol is in starlight + funcStr = symbolStr + r'\(' + try: + addrInStarlight = getSymAddrFromMap("starlight", funcStr, symbolStr) + # if no exception, then symbolStr is found is starlight + resolvedAddr += addrInStarlight + return target, resolvedAddr + except NameError: + pass + + # if symbolStr is already an address + try: + resolvedAddr += int(symbolStr, 16) + resolvedAddr += int(patchConfig["nso_load_addr"][target], 16) + return target, resolvedAddr + except ValueError: + pass + + # search symbol in map + regexStr = r'\w*'.join(re.findall(r'\w+', symbolStr)) + try: + resolvedAddr += getSymAddrFromMap(target, regexStr, symbolStr) + except NameError: + print("genPatch.py error: cannot resolve", symbolStr) + sys.exit(-1) + + return target, resolvedAddr + +def getPatchBin(target, patchAddress, patchValueStr): + # bytes patch + try: + patchBin = bytearray.fromhex(patchValueStr) + return patchBin + except ValueError: + pass + # string patch + stringMatch = re.search(r'"(.+)"', patchValueStr) + if stringMatch: + return bytearray(bytes(stringMatch.group(1), 'utf-8').decode('unicode_escape') + '\0', 'utf-8') + + # asm patch + branchNeedResolveMatch = re.match(r'([Bb][Ll]?\s+)([^\#]+$)', patchValueStr) + if branchNeedResolveMatch: + target, toAddr = resolveAddressAndTarget(target, branchNeedResolveMatch.group(2)) + patchValueStr = (branchNeedResolveMatch.group(1) + '#' + hex(toAddr - patchAddress)) + + ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN) + encodedBytes, insCount = ks.asm(patchValueStr) + return bytearray(encodedBytes) + +def addPatchToPatchlist(target, patchAddress, patchContent): + if target not in patchList: + patchList[target] = [] + patchList[target].append(Patch( + patchAddress - int(patchConfig["nso_load_addr"][target], 16) + NSO_HEADER_LEN, + len(patchContent), patchContent )) + +def addPatchFromFile(patchFilePath): + PATCH_VERSION_ALL = "all" + patchVars = { + "version" : None, + "target" : "main" + } + + with open(patchFilePath) as patchFile: + fileLinesIter = iter(patchFile) + isInMultiPatch = False + while True: + # read next line + if isInMultiPatch: + # multiPatch check already read new line; simply false the flag + isInMultiPatch = False + else: + try: + line = next(fileLinesIter) + except StopIteration: + break + line = line.split('/', 1)[0].strip() + + # if is patch variable line, e.g. [version=100] + patchVarLineMatch = re.match(r'\[(.+)\]', line) + if patchVarLineMatch: + patchVarMatches = re.findall(r'(\w+)=(\w+)', patchVarLineMatch.group(1)) + for match in patchVarMatches: + patchVars[match[0]] = match[1] + continue + + # skip all lines not for our version + if patchVars["version"] != buildVersion and patchVars["version"] != PATCH_VERSION_ALL: + continue + + # parse patches + addressSplit = line.split(' ', 1) + isInMultiPatch = addressSplit[0].endswith(':') + if len(addressSplit) < 2 and not isInMultiPatch: + continue + + patchTarget, patchAddress = resolveAddressAndTarget(patchVars["target"], + addressSplit[0] if not isInMultiPatch else addressSplit[0][:-1]) + patchContent = bytearray() + + if isInMultiPatch: + try: + line = next(fileLinesIter).split('/', 1)[0] + ident = re.search(r'\s+', line).group() + while True: + patchContent += getPatchBin(patchTarget, patchAddress + len(patchContent), line.strip()) + line = next(fileLinesIter).split('/', 1)[0] + if not line.startswith(ident): + break + except StopIteration: + addPatchToPatchlist(patchTarget, patchAddress, patchContent) + break + else: + patchContent = getPatchBin(patchTarget, patchAddress, addressSplit[1]) + + addPatchToPatchlist(patchTarget, patchAddress, patchContent) + +if len(sys.argv) < 2: + print('Usage: ' + sys.argv[0] + ' [version]') + sys.exit(-1) + +buildVersion = sys.argv[1] +initConfig() +SLMapFilePath = os.path.join("build" + buildVersion, os.path.basename(os.getcwd()) + buildVersion + ".map") +with open(SLMapFilePath, 'r') as f: + SLMapFile = f.read() + +for file in os.listdir(PATCH_DIR): + if file.endswith(PATCH_EXTENSION): + addPatchFromFile(os.path.join(PATCH_DIR, file)) + +ipsOutDir = IPS_OUT_DIR_NAME.format(buildVersion) +try: + os.mkdir(ipsOutDir) +except FileExistsError: + pass + +for nso in patchList: + ipsOutPath = os.path.join(ipsOutDir, patchConfig["build_id"][nso] + IPS_FORMAT) + with open(ipsOutPath, 'wb') as ipsFile: + ipsFile.write(IPS_HEADER_MAGIC) + for patch in patchList[nso]: + ipsFile.write(struct.pack('>I', patch.offset)) + ipsFile.write(struct.pack('>H', patch.length)) + ipsFile.write(patch.content) + ipsFile.write(IPS_EOF_MAGIC) + print("genPatch.py:", nso, "->", ipsOutPath, "successful") diff --git a/scripts/sendPatch.py b/scripts/sendPatch.py new file mode 100644 index 0000000..15d14b3 --- /dev/null +++ b/scripts/sendPatch.py @@ -0,0 +1,108 @@ +from ftplib import FTP +import os +import sys + +titleIdLookup = { + "US": '0100000000010000' +} + +def listdirs(connection,_path): + + file_list, dirs, nondirs = [], [], [] + try: + connection.cwd(_path) + except: + return [] + + connection.retrlines('LIST', lambda x: file_list.append(x.split())) + for info in file_list: + ls_type, name = info[0], info[-1] + if ls_type.startswith('d'): + dirs.append(name) + else: + nondirs.append(name) + return dirs + + +def ensuredirectory(connection,root,path): + print(f"Ensuring {os.path.join(root, path)} exists...") + if path not in listdirs(connection, root): + connection.mkd(f'{root}/{path}') + + +consoleIP = sys.argv[1] +if '.' not in consoleIP: + print(sys.argv[0], "ERROR: Please specify with `IP=[Your console's IP]`") + sys.exit(-1) + +isNeedOtherSwitch = True + +altSwitchIP = sys.argv[2] +if '.' not in altSwitchIP: + isNeedOtherSwitch = False + +consolePort = 5000 + +if len(sys.argv) < 4: + projName = 'StarlightBase' +else: + projName = sys.argv[3] + +curDir = os.curdir + +ftp = FTP() + +otherftp = FTP() + +print(f'Connecting to {consoleIP}... ', end='') +ftp.connect(consoleIP, consolePort) +print('logging into server...', end='') +ftp.login('crafty','boss') +print('Connected!') + +if isNeedOtherSwitch: + print(f'Connecting to {altSwitchIP}... ', end='') + otherftp.connect(altSwitchIP, consolePort) + print('logging into server...', end='') + otherftp.login('crafty','boss') + print('Connected!') + +patchDirectories = [] + +root, dirs, _ = next(os.walk(curDir)) + +for dir in dirs: + if dir.startswith("starlight_patch_"): + patchDirectories.append((os.path.join(root, f'{dir}/atmosphere/exefs_patches/{projName}'), projName)) + +# ensuredirectory(ftp, '', 'atmosphere') +# ensuredirectory(ftp, '/atmosphere', 'exefs_patches') + +for patchDir in patchDirectories: + dirPath = patchDir[0] + dirName = patchDir[1] + ensuredirectory(ftp, '/atmosphere/exefs_patches', patchDir[1]) + _, _, files = next(os.walk(dirPath)) + for file in files: + fullPath = os.path.join(dirPath, file) + if os.path.exists(fullPath): + sdPath = f'/atmosphere/exefs_patches/{projName}/{file}' + print(f'Sending "{sdPath}" to {consoleIP}.') + ftp.storbinary(f'STOR {sdPath}', open(fullPath, 'rb')) + if isNeedOtherSwitch: + print(f'Sending "{sdPath}" to {altSwitchIP}.') + otherftp.storbinary(f'STOR {sdPath}', open(fullPath, 'rb')) + +# ensuredirectory(ftp, '/atmosphere', 'contents') +# ensuredirectory(ftp, '/atmosphere/contents', '0100000000010000') +# ensuredirectory(ftp, f'/atmosphere/contents/0100000000010000', 'exefs') + +binaryPath = f'starlight_patch_100/atmosphere/contents/0100000000010000/exefs/subsdk1' + +if os.path.isfile(binaryPath): + sdPath = f'/atmosphere/contents/0100000000010000/exefs/subsdk1' + print(f'Sending "{sdPath}" to {consoleIP}.') + ftp.storbinary(f'STOR {sdPath}', open(binaryPath, 'rb')) + if isNeedOtherSwitch: + print(f'Sending "{sdPath}" to {altSwitchIP}.') + otherftp.storbinary(f'STOR {sdPath}', open(binaryPath, 'rb')) diff --git a/scripts/tcpServer.py b/scripts/tcpServer.py new file mode 100644 index 0000000..5f2c7b5 --- /dev/null +++ b/scripts/tcpServer.py @@ -0,0 +1,33 @@ +import socket +import sys + +# Super simple TCP server yoinked straight from google.com (http://pymotw.com/2/socket/tcp.html) + +# Create a TCP/IP socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +# Bind the socket to the port +server_address = (sys.argv[1], 3080) +print(f"Starting TCP Server with IP {server_address[0]} and Port {server_address[1]}.") +sock.bind(server_address) + +# Listen for incoming connections +sock.listen(1) + +while True: + # Wait for a connection + print('Waiting for Switch to Connect...') + connection, client_address = sock.accept() + try: + print(f'Switch Connected! IP: {client_address[0]} Port: {client_address[1]}') + while True: + data = connection.recv(1024) + if data: + print(data.decode("utf-8")) + else: + print(f'Connection Terminated.') + break + + finally: + # Clean up the connection + connection.close() \ No newline at end of file diff --git a/source/Factory.cpp b/source/Factory.cpp new file mode 100644 index 0000000..187babb --- /dev/null +++ b/source/Factory.cpp @@ -0,0 +1,10 @@ +#include "al/factory/ProjectActorFactory.h" + +#include "al/factory/ProjectCameraPoserFactory.h" +#include "al/factory/CameraPoserFactory.h" +#include "al/factory/CameraPoserFactoryEntries100.h" + +ProjectActorFactory::ProjectActorFactory() : ActorFactory("アクター生成") { + this->actorTable = actorEntries; + this->factoryCount = sizeof(actorEntries)/sizeof(actorEntries[0]); // 570 actors in factory in 1.0.0 +}; \ No newline at end of file diff --git a/source/Keyboard.cpp b/source/Keyboard.cpp new file mode 100644 index 0000000..47a5e00 --- /dev/null +++ b/source/Keyboard.cpp @@ -0,0 +1,63 @@ +#include "Keyboard.hpp" +#include "logger.hpp" +#include "nn/os.h" +#include "nn/swkbd/swkbd.h" + +Keyboard::Keyboard(ulong strSize) : mResultString(strSize) { + this->mThread = + new al::AsyncFunctorThread("Swkbd", al::FunctorV0M(this, &Keyboard::keyboardThread), 0, 0x2000, {0}); + + mWorkBufSize = nn::swkbd::GetRequiredWorkBufferSize(false); + mWorkBuf = (char*)malloc(mWorkBufSize); + + mTextCheckSize = 0x400; + mTextCheckBuf = (char*)malloc(mTextCheckSize); + + mCustomizeDicSize = 0x400; + mCustomizeDicBuf = (char*)malloc(mCustomizeDicSize); + + mIsDoneKeyboard = false; + +} + +void Keyboard::keyboardThread() { + + mIsDoneKeyboard = false; + + nn::swkbd::ShowKeyboardArg keyboardArg = nn::swkbd::ShowKeyboardArg(); + nn::swkbd::MakePreset(&keyboardArg.keyboardConfig, nn::swkbd::Preset::Default); + + keyboardArg.keyboardConfig.keyboardMode = nn::swkbd::KeyboardMode::ModeNumeric; + keyboardArg.keyboardConfig.leftOptionalSymbolKey = '.'; + keyboardArg.keyboardConfig.textMaxLength = 15; + keyboardArg.keyboardConfig.textMinLength = 1; + keyboardArg.keyboardConfig.isUseUtf8 = true; + keyboardArg.keyboardConfig.inputFormMode = nn::swkbd::InputFormMode::OneLine; + + nn::swkbd::SetHeaderText(&keyboardArg.keyboardConfig, mHeaderText); + nn::swkbd::SetSubText(&keyboardArg.keyboardConfig, mSubText); + + keyboardArg.workBufSize = mWorkBufSize; + keyboardArg.textCheckWorkBufSize = mTextCheckSize; + keyboardArg._customizeDicBufSize = mCustomizeDicSize; + + keyboardArg.workBuf = mWorkBuf; + keyboardArg.textCheckWorkBuf = mTextCheckBuf; + keyboardArg._customizeDicBuf = mCustomizeDicBuf; + + if (mInitialText.calcLength() > 0) { + nn::swkbd::SetInitialTextUtf8(&keyboardArg, mInitialText.cstr()); + } + + nn::swkbd::ShowKeyboard(&mResultString, keyboardArg); + + mIsDoneKeyboard = true; + +} + +void Keyboard::openKeyboard(const char* initialText) { + + mInitialText = initialText; + + mThread->start(); +} \ No newline at end of file diff --git a/source/cameras/CameraPoserCustom.cpp b/source/cameras/CameraPoserCustom.cpp new file mode 100644 index 0000000..5092318 --- /dev/null +++ b/source/cameras/CameraPoserCustom.cpp @@ -0,0 +1,135 @@ +#include "cameras/CameraPoserCustom.h" +#include +#include "al/camera/CameraPoser.h" +#include "al/camera/alCameraPoserFunction.h" +#include "al/util.hpp" +#include "al/util/MathUtil.h" +#include "al/util/VectorUtil.h" +#include "logger.hpp" +#include "sead/gfx/seadCamera.h" +#include "sead/math/seadVector.h" +#include "sead/math/seadVectorCalcCommon.h" + +namespace cc { + +CameraPoserCustom::CameraPoserCustom(const char* poserName) : CameraPoser(poserName) { + + this->initOrthoProjectionParam(); +} + +void CameraPoserCustom::init(void) { + alCameraPoserFunction::initSnapShotCameraCtrlZoomRollMove( + this); // this makes the snapshot camera have the abilities of the normal snapshot cam, but + // is locked rotationally + alCameraPoserFunction::initCameraVerticalAbsorber(this); + alCameraPoserFunction::initCameraAngleCtrl(this); +} + +void CameraPoserCustom::loadParam(al::ByamlIter const& paramIter) { + al::tryGetByamlF32(&mOffsetY, paramIter, "OffsetY"); + al::tryGetByamlF32(&mDist, paramIter, "Distance"); + al::tryGetByamlF32(&mSnapSpeed, paramIter, "GravitySnapSpeed"); + al::tryGetByamlBool(&mIsResetAngleIfSwitchTarget, paramIter, "IsResetAngleIfSwitchTarget"); +} + +void normalize2(sead::Vector3f &v, float inv) { + float len = sead::Vector3CalcCommon::length(v); + + if (len > 0) + { + float inv_len = inv / len; + v.x *= inv_len; + v.y *= inv_len; + v.z *= inv_len; + } +} + +void CameraPoserCustom::start(al::CameraStartInfo const&) { + + sead::Vector3f faceDir; + sead::Vector3f targetFront; + + if (alCameraPoserFunction::isSceneCameraFirstCalc(this)) { + alCameraPoserFunction::calcTargetTrans(&mTargetTrans, this); + mTargetTrans.y += mOffsetY; + targetFront = sead::Vector3f(0, 0, 0); + alCameraPoserFunction::calcTargetFront(&targetFront, this); + + faceDir = mTargetTrans - (mDist * targetFront); + + } else { + sead::LookAtCamera* curLookCam = alCameraPoserFunction::getLookAtCamera(this); + + sead::Vector3f curPos = curLookCam->mPos; + + sead::Vector3f curAt = curLookCam->mAt; + + targetFront = sead::Vector3f(curPos.x - curAt.x, 0.0, curPos.z - curAt.z); + + al::tryNormalizeOrDirZ(&targetFront); + + faceDir = (mDist * targetFront) + mTargetTrans; + } + + mPosition = faceDir; +} + +void CameraPoserCustom::update(void) { + sead::Vector3f targetDir; + + if (alCameraPoserFunction::isChangeSubTarget(this) && mIsResetAngleIfSwitchTarget) { + alCameraPoserFunction::calcTargetTrans(&mTargetTrans, this); + mTargetTrans.y += mOffsetY; + targetDir = sead::Vector3f(0,0,0); + alCameraPoserFunction::calcTargetFront(&targetDir, this); + + mPosition = mTargetTrans - (mDist * targetDir); + } + + sead::Vector3f targetGrav = sead::Vector3f::ey; + + alCameraPoserFunction::calcTargetGravity(&targetGrav, this); + + al::lerpVec(&mCameraUp, mCameraUp, -targetGrav, mSnapSpeed); + + alCameraPoserFunction::calcTargetTrans(&mTargetTrans, this); + mTargetTrans += mCameraUp * mOffsetY; + + targetDir = mPosition - mTargetTrans; + + al::tryNormalizeOrDirZ(&targetDir); + + sead::Vector2f playerInput(0, 0); + alCameraPoserFunction::calcCameraRotateStick(&playerInput, this); + + sead::Vector3f rightAxis; + rightAxis.setCross(targetDir, mCameraUp); + + float stickSpeed = alCameraPoserFunction::getStickSensitivityScale(this) * + alCameraPoserFunction::getStickSensitivityLevel(this); + + sead::Vector3f preLook; + alCameraPoserFunction::calcPreLookDir(&preLook, this); + + sead::Vector3f rotatedVec = targetDir; + + + // Horizontal Rotation + al::rotateVectorDegree(&rotatedVec, rotatedVec, mCameraUp, playerInput.x * -stickSpeed); + + // Vertical Rotation + al::rotateVectorDegree(&rotatedVec, rotatedVec, rightAxis, playerInput.y * -stickSpeed); + + mAngleH = al::calcAngleOnPlaneDegree(rotatedVec, mPrevTargetDir, mCameraUp); + mAngleV = al::calcAngleOnPlaneDegree(rotatedVec, mPrevTargetDir, rightAxis); + + mPosition = mTargetTrans + (rotatedVec * mDist); + + mPrevTargetDir = rotatedVec; +} + +void CameraPoserCustom::movement() { + al::CameraPoser::movement(); +} + +} \ No newline at end of file diff --git a/source/cameras/ProjectCameraPoserFactory.cpp b/source/cameras/ProjectCameraPoserFactory.cpp new file mode 100644 index 0000000..172e509 --- /dev/null +++ b/source/cameras/ProjectCameraPoserFactory.cpp @@ -0,0 +1,5 @@ +#include "al/factory/ProjectCameraPoserFactory.h" + +ProjectCameraPoserFactory::ProjectCameraPoserFactory() : CameraPoserFactory("プロジェクトカメラファクトリー") { + alCameraPoserFactoryFunction::initAndCreateTableFromOtherTable2(this, poserEntries, sizeof(poserEntries)/sizeof(poserEntries[0]), poserEntries2, sizeof(poserEntries2)/sizeof(poserEntries2[0])); +}; \ No newline at end of file diff --git a/source/crt0.s b/source/crt0.s new file mode 100644 index 0000000..7eb4618 --- /dev/null +++ b/source/crt0.s @@ -0,0 +1,18 @@ +.section ".text.crt0","ax" +.global __module_start +.extern __nx_module_runtime + +__module_start: + b . + .word __nx_mod0 - __module_start + +.section ".rodata.mod0" +.global __nx_mod0 +__nx_mod0: + .ascii "MOD0" + .word __dynamic_start__ - __nx_mod0 + .word __bss_start__ - __nx_mod0 + .word __bss_end__ - __nx_mod0 + .word 0 + .word 0 + .word __nx_module_runtime - __nx_mod0 diff --git a/source/debugMenu.cpp b/source/debugMenu.cpp new file mode 100644 index 0000000..79b8c80 --- /dev/null +++ b/source/debugMenu.cpp @@ -0,0 +1,46 @@ +#include "debugMenu.hpp" + +static const char *DBG_FONT_PATH = "DebugData/Font/nvn_font_jis1.ntx"; +static const char *DBG_SHADER_PATH = "DebugData/Font/nvn_font_shader_jis1.bin"; +static const char *DBG_TBL_PATH = "DebugData/Font/nvn_font_jis1_tbl.bin"; + +sead::TextWriter *gTextWriter; + +void setupDebugMenu(GameSystem *gSys) { + + sead::Heap *curHeap = al::getCurrentHeap(); + + agl::DrawContext *context = gSys->mSystemInfo->mDrawInfo->mDrawContext; + + if(curHeap) { + if (context) { + + sead::DebugFontMgrJis1Nvn::sInstance = sead::DebugFontMgrJis1Nvn::createInstance(curHeap); + + if(al::isExistFile(DBG_FONT_PATH) && al::isExistFile(DBG_SHADER_PATH) && al::isExistFile(DBG_TBL_PATH)) { + sead::DebugFontMgrJis1Nvn::sInstance->initialize(curHeap, DBG_SHADER_PATH, DBG_FONT_PATH, DBG_TBL_PATH, 0x100000); + sead::TextWriter::setDefaultFont(sead::DebugFontMgrJis1Nvn::sInstance); + gTextWriter = new sead::TextWriter(context); + gTextWriter->setupGraphics(context); + } + + sead::PrimitiveDrawer drawer(context); + } + } + + __asm("MOV W23, #0x3F800000"); + __asm("MOV W8, #0xFFFFFFFF"); +} + +void drawBackground(agl::DrawContext *context) { + sead::Vector3 p1(-1, .3, 0); // top left + sead::Vector3 p2(-.2, .3, 0); // top right + sead::Vector3 p3(-1, -1, 0); // bottom left + sead::Vector3 p4(-.2, -1, 0); // bottom right + sead::Color4f c(.1, .1, .1, .9); + + agl::utl::DevTools::beginDrawImm(context, sead::Matrix34::ident, sead::Matrix44::ident); + agl::utl::DevTools::drawTriangleImm(context, p1, p2, p3, c); + agl::utl::DevTools::drawTriangleImm(context, p3, p4, p2, c); + +} \ No newline at end of file diff --git a/source/helpers.cpp b/source/helpers.cpp new file mode 100644 index 0000000..c50522a --- /dev/null +++ b/source/helpers.cpp @@ -0,0 +1,197 @@ +#include "helpers.hpp" +#include "al/LiveActor/LiveActor.h" +#include "logger.hpp" +#include "sead/math/seadMathCalcCommon.h" +#include "sead/math/seadQuat.h" +#include "sead/math/seadVector.h" +#include "sead/time/seadTickSpan.h" +#include "sead/time/seadTickTime.h" + +bool isPartOf(const char* w1, const char* w2) { + + int i=0; + int j=0; + + if(strlen(w1) <= 0) { + return false; + } + + while(w1[i]!='\0'){ + if(w1[i] == w2[j]) + { + int init = i; + while (w1[i] == w2[j] && w2[j]!='\0') + { + j++; + i++; + } + if(w2[j]=='\0'){ + return true; + } + j=0; + } + i++; + } + return false; +} + +int indexOf(char *w1, char c1) { + + for (int i = 0; i < strlen(w1); i++) + { + if(w1[i] == c1) { + return i; + } + } + return -1; +} + +sead::Vector3f QuatToEuler(sead::Quatf *quat) { + + f32 x = quat->z; + f32 y = quat->y; + f32 z = quat->x; + f32 w = quat->w; + + f32 t0 = 2.0 * (w * x + y * z); + f32 t1 = 1.0 - 2.0 * (x * x + y * y); + f32 roll = atan2f(t0, t1); + + f32 t2 = 2.0 * (w * y - z * x); + t2 = t2 > 1.0 ? 1.0 : t2; + t2 = t2 < -1.0 ? -1.0 : t2; + f32 pitch = asinf(t2); + + f32 t3 = 2.0 * (w * z + x * y); + f32 t4 = 1.0 - 2.0 * (y * y + z * z); + f32 yaw = atan2f(t3, t4); + + return sead::Vector3f(yaw, pitch, roll); +} + +void logVector(const char *vectorName, sead::Vector3f vector) { + Logger::log("%s: \nX: %f\nY: %f\nZ: %f\n", vectorName, vector.x, vector.y, vector.z); +} + +void logQuat(const char *quatName, sead::Quatf &quat) { + Logger::log("%s: \nX: %f\nY: %f\nZ: %f\nW: %f\n", quatName, quat.x, quat.y, quat.z, quat.w); +} + +float vecMagnitude(sead::Vector3f const &input) { + return (input.x * input.x + input.y * input.y + input.z * input.z); +} + +float quatAngle(sead::Quatf const &q1, sead::Quatf &q2) { + float dot = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z) + (q1.w * q2.w); + float dotAngle = sead::Mathf::min(abs(dot), 1.0f); + + return dotAngle > 1.0f - 0.000001f ? 0.0f : DEG(sead::Mathf::acos(dotAngle) * 2.0f); +} + +bool isInCostumeList(const char *costumeName) { + for (size_t i = 0; i < sizeof(costumeNames)/sizeof(costumeNames[0]); i++) + { + if(al::isEqualString(costumeNames[i], costumeName)) { + return true; + } + } + return false; +} + +const char *tryGetPuppetCapName(PuppetInfo *info) { + if(info->costumeHead && isInCostumeList(info->costumeHead)) { + return info->costumeHead; + }else { + return "Mario"; + } +} + +const char *tryGetPuppetBodyName(PuppetInfo *info) { + if(info->costumeBody && isInCostumeList(info->costumeBody)) { + return info->costumeBody; + }else { + return "Mario"; + } +} + +const char *tryConvertName(const char *className) { + for (size_t i = 0; i < ACNT(classHackNames); i++) + { + if(al::isEqualString(classHackNames[i].className, className)) { + return classHackNames[i].hackName; + } + } + return className; +} + +// Unity Classes + +float VisualUtils::SmoothMove(Transform moveTransform, Transform targetTransform, float timeDelta, float closingSpeed, float maxAngularSpeed) +{ + + // Position + + sead::Vector3f posDiff = *targetTransform.position - *moveTransform.position; + + float posDiffMag = posDiff.dot(posDiff); + + if (posDiffMag > 0) { + + float diffSpeed = sead::Mathf::max(k_MinSmoothSpeed, posDiffMag / k_TargetCatchupTime); + + closingSpeed = sead::Mathf::max(closingSpeed, diffSpeed); + + float maxMove = timeDelta * closingSpeed; + float moveDist = sead::Mathf::min(maxMove, posDiffMag); + posDiff *= (moveDist / posDiffMag); + + moveTransform.position->x += posDiff.x; + moveTransform.position->y += posDiff.y; + moveTransform.position->z += posDiff.z; + + if( moveDist == posDiffMag ) + { + //we capped the move, meaning we exactly reached our target transform. Time to reset our velocity. + closingSpeed = 0; + } + } + else + { + closingSpeed = 0; + } + + // Rotation + + if (moveTransform.rotation) { + float angleDiff = quatAngle(*targetTransform.rotation, *moveTransform.rotation); + + // if rotation is over 150 degrees, snap to new rotation instead of interpolating to it + if (angleDiff > 0) + { + float maxAngleMove = timeDelta * maxAngularSpeed; + float angleMove = sead::Mathf::min(maxAngleMove, angleDiff); + float t = angleMove / angleDiff; + sead::QuatCalcCommon::slerpTo(*moveTransform.rotation, *moveTransform.rotation, *targetTransform.rotation, t); + } + } + + return closingSpeed; +} + +void killMainPlayer(al::LiveActor* actor) { + PlayerActorHakoniwa *mainPlayer = (PlayerActorHakoniwa*)al::getPlayerActor(actor, 0); + + GameDataFunction::killPlayer(GameDataHolderAccessor(actor)); + mainPlayer->startDemoPuppetable(); + al::setVelocityZero(mainPlayer); + mainPlayer->mPlayerAnimator->endSubAnim(); + mainPlayer->mPlayerAnimator->startAnimDead(); +} + +void killMainPlayer(PlayerActorHakoniwa* mainPlayer) { + GameDataFunction::killPlayer(GameDataHolderAccessor(mainPlayer)); + mainPlayer->startDemoPuppetable(); + al::setVelocityZero(mainPlayer); + mainPlayer->mPlayerAnimator->endSubAnim(); + mainPlayer->mPlayerAnimator->startAnimDead(); +} \ No newline at end of file diff --git a/source/hooks.cpp b/source/hooks.cpp new file mode 100644 index 0000000..83636b5 --- /dev/null +++ b/source/hooks.cpp @@ -0,0 +1,155 @@ +#include +#include "server/Client.hpp" +#include "al/LiveActor/LiveActor.h" +#include "al/actor/ActorInitInfo.h" +#include "al/actor/Placement.h" +#include "al/byaml/ByamlIter.h" +#include "al/nerve/Nerve.h" +#include "al/nerve/NerveExecutor.h" +#include "al/nerve/NerveKeeper.h" +#include "al/util.hpp" +#include "al/util/ControllerUtil.h" +#include "al/util/LiveActorUtil.h" +#include "al/util/NerveUtil.h" +#include "game/Actors/WorldEndBorderKeeper.h" +#include "game/Layouts/CoinCounter.h" +#include "game/Player/Actions/PlayerActionGroundMoveControl.h" +#include "game/Player/PlayerActorHakoniwa.h" +#include "game/Player/PlayerConst.h" +#include "game/Player/States/PlayerStateRunHakoniwa.h" +#include "game/StageScene/StageSceneStateOption.h" +#include "game/StageScene/StageSceneStatePauseMenu.h" +#include "game/StageScene/StageSceneStateServerConfig.hpp" +#include "logger.hpp" +#include "main.hpp" +#include "al/byaml/writer/ByamlWriter.h" +#include "math/seadVector.h" +#include "rs/util/InputUtil.h" +#include "sead/prim/seadSafeString.h" +#include "server/HideAndSeekMode.hpp" + +bool comboBtnHook(int port) { + return !al::isPadHoldL(port) && al::isPadTriggerDown(port); +} + +void saveWriteHook(al::ByamlWriter* saveByml) { + + const char *serverIP = Client::getCurrentIP(); + + if (serverIP) { + saveByml->addString("ServerIP", serverIP); + } else { + saveByml->addString("ServerIP", "0.0.0.0"); + } + + saveByml->pop(); +} + +bool saveReadHook(int* padRumbleInt, al::ByamlIter const& saveByml, char const* padRumbleKey) { + + const char *serverIP = ""; + + if (al::tryGetByamlString(&serverIP, saveByml, "ServerIP")) { + Client::setLastUsedIP(serverIP); + } + + return al::tryGetByamlS32(padRumbleInt, saveByml, padRumbleKey); +} + +bool registerShineToList(Shine* shineActor) { + + if (shineActor->shineId >= 0) { + Client::tryRegisterShine(shineActor); + } + + return al::isAlive(shineActor); +} + +void overrideNerveHook(StageSceneStatePauseMenu* thisPtr, al::Nerve* nrvSet) { + + if (al::isPadHoldZL(-1)) { + al::setNerve(thisPtr, &nrvStageSceneStatePauseMenuServerConfig); + } else { + al::setNerve(thisPtr, nrvSet); + } +} + +StageSceneStateServerConfig *sceneStateServerConfig = nullptr; + +void initStateHook(StageSceneStatePauseMenu *thisPtr, char const *stateName, al::Scene *host, al::LayoutInitInfo const &initInfo, FooterParts *footer, + GameDataHolder *data, bool unkBool) { + thisPtr->mStateOption = + new StageSceneStateOption(stateName, host, initInfo, footer, data, unkBool); + + sceneStateServerConfig = new StageSceneStateServerConfig("ServerConfig", host, initInfo, footer, data, unkBool); +} + +void initNerveStateHook(StageSceneStatePauseMenu* stateParent, StageSceneStateOption* stateOption, + al::Nerve const* executingNerve, char const* stateName) { + + al::initNerveState(stateParent, stateOption, executingNerve, stateName); + + al::initNerveState(stateParent, sceneStateServerConfig, &nrvStageSceneStatePauseMenuServerConfig, "CustomNerveOverride"); +} + +// skips starting both coin counters +void startCounterHook(CoinCounter* thisPtr) { + if (!Client::isModeActive()) { + thisPtr->tryStart(); + } +} + +// Simple hook that can be used to override isModeE3 checks to enable/disable certain behaviors +bool modeE3Hook() { + return Client::isModeActive(); +} + +// Skips ending the play guide layout if a mode is active, since the mode would have already ended it +void playGuideEndHook(al::SimpleLayoutAppearWaitEnd* thisPtr) { + if (!Client::isModeActive()) { + thisPtr->end(); + } +} + +// Gravity Hooks + +void initHackCapHook(al::LiveActor *cappy) { + al::initActorPoseTQGSV(cappy); +} + +al::PlayerHolder* createTicketHook(StageScene* curScene) { + // only creates custom gravity camera ticket if hide and seek mode is active + if (Client::isSelectedMode(GameMode::HIDEANDSEEK)) { + al::CameraDirector* director = curScene->getCameraDirector(); + if (director) { + if (director->mFactory) { + al::CameraTicket* gravityCamera = director->createCameraFromFactory( + "CameraPoserCustom", nullptr, 0, 5, sead::Matrix34f::ident); + + HideAndSeekMode* mode = Client::getMode(); + + mode->setCameraTicket(gravityCamera); + } + } + } + + return al::getScenePlayerHolder(curScene); +} + +bool borderPullBackHook(WorldEndBorderKeeper* thisPtr) { + + bool isFirstStep = al::isFirstStep(thisPtr); + + if (isFirstStep) { + if (Client::isSelectedMode(GameMode::HIDEANDSEEK) && Client::isModeActive()) { + + HideAndSeekMode* mode = Client::getMode(); + + if (mode->isUseGravity()) { + killMainPlayer(thisPtr->mActor); + } + } + } + + return isFirstStep; +} \ No newline at end of file diff --git a/source/layouts/HideAndSeekIcon.cpp b/source/layouts/HideAndSeekIcon.cpp new file mode 100644 index 0000000..7b8b579 --- /dev/null +++ b/source/layouts/HideAndSeekIcon.cpp @@ -0,0 +1,129 @@ +#include "layouts/HideAndSeekIcon.h" +#include +#include +#include "puppets/PuppetInfo.h" +#include "al/string/StringTmp.h" +#include "prim/seadSafeString.h" +#include "server/gamemode/GameModeTimer.hpp" +#include "server/HideAndSeekMode.hpp" +#include "server/Client.hpp" +#include "al/util.hpp" +#include "logger.hpp" +#include "rs/util.hpp" +#include "main.hpp" + +HideAndSeekIcon::HideAndSeekIcon(const char* name, const al::LayoutInitInfo& initInfo) : al::LayoutActor(name) { + + al::initLayoutActor(this, initInfo, "HideAndSeekIcon", 0); + + mInfo = (HideAndSeekInfo*)Client::getModeInfo(); + + initNerve(&nrvHideAndSeekIconEnd, 0); + + al::hidePane(this, "SeekingIcon"); + al::hidePane(this, "HidingIcon"); + + + kill(); + +} + +void HideAndSeekIcon::appear() { + + al::startAction(this, "Appear", 0); + + al::setNerve(this, &nrvHideAndSeekIconAppear); + + al::LayoutActor::appear(); +} + +bool HideAndSeekIcon::tryEnd() { + if (!al::isNerve(this, &nrvHideAndSeekIconEnd)) { + al::setNerve(this, &nrvHideAndSeekIconEnd); + return true; + } + return false; +} + +bool HideAndSeekIcon::tryStart() { + + if (!al::isNerve(this, &nrvHideAndSeekIconWait) && !al::isNerve(this, &nrvHideAndSeekIconAppear)) { + + appear(); + + return true; + } + + return false; +} + +void HideAndSeekIcon::exeAppear() { + if (al::isActionEnd(this, 0)) { + al::setNerve(this, &nrvHideAndSeekIconWait); + } +} + +void HideAndSeekIcon::exeWait() { + if (al::isFirstStep(this)) { + al::startAction(this, "Wait", 0); + } + + GameTime &curTime = mInfo->mHidingTime; + + if (curTime.mHours > 0) { + al::setPaneStringFormat(this, "TxtCounter", "%01d:%02d:%02d", curTime.mHours, curTime.mMinutes, + curTime.mSeconds); + } else { + al::setPaneStringFormat(this, "TxtCounter", "%02d:%02d", curTime.mMinutes, + curTime.mSeconds); + } + + + + int playerCount = Client::getConnectCount(); + + if (playerCount > 0) { + + char playerNameBuf[0x100] = {0}; // max of 16 player names if player name size is 0x10 + + sead::BufferedSafeStringBase playerList = + sead::BufferedSafeStringBase(playerNameBuf, 0x200); + + for (size_t i = 0; i < playerCount; i++) { + PuppetInfo* curPuppet = Client::getPuppetInfo(i); + if (curPuppet->isConnected && (curPuppet->isIt == mInfo->mIsPlayerIt)) { + playerList.appendWithFormat("%s\n", curPuppet->puppetName); + } + } + + al::setPaneStringFormat(this, "TxtPlayerList", playerList.cstr()); + } + +} + +void HideAndSeekIcon::exeEnd() { + + if (al::isFirstStep(this)) { + al::startAction(this, "End", 0); + } + + if (al::isActionEnd(this, 0)) { + kill(); + } +} + +void HideAndSeekIcon::showHiding() { + al::hidePane(this, "SeekingIcon"); + al::showPane(this, "HidingIcon"); +} + +void HideAndSeekIcon::showSeeking() { + al::hidePane(this, "HidingIcon"); + al::showPane(this, "SeekingIcon"); +} + +namespace { + NERVE_IMPL(HideAndSeekIcon, Appear) + NERVE_IMPL(HideAndSeekIcon, Wait) + NERVE_IMPL(HideAndSeekIcon, End) +} \ No newline at end of file diff --git a/source/layouts/NameTag.cpp b/source/layouts/NameTag.cpp new file mode 100644 index 0000000..5d42f81 --- /dev/null +++ b/source/layouts/NameTag.cpp @@ -0,0 +1,156 @@ +#include "layouts/NameTag.h" + +#include "actors/PuppetActor.h" +#include "al/layout/LayoutActor.h" +#include "al/util.hpp" +#include "al/util/NerveUtil.h" +#include "logger.hpp" +#include "sead/math/seadVector.h" + +NameTag::NameTag(PuppetActor* pupActor, const al::LayoutInitInfo& initInfo, float startDist, + float endDist, const char *playerName) + : al::LayoutActor("PNameTag"), mPuppet(pupActor), mStartDist(startDist), mEndDist(endDist) { + + al::initLayoutActor(this, initInfo, "BalloonSpeak", 0); + + mPaneName = "TxtMessage"; + + al::setPaneStringFormat(this, mPaneName, playerName); + + initNerve(&nrvNameTagWait, 0); + + end(); +} + +void NameTag::appear() { + if (!al::isNerve(this, &nrvNameTagEnd) && !al::isNerve(this, &nrvNameTagHide) && mIsAlive) { + LayoutActor::appear(); + al::startFreezeActionEnd(this, "End", 0); + al::setNerve(this, &nrvNameTagHide); + return; + } + + if (!isNearPlayerActor(mStartDist)) { + LayoutActor::appear(); + al::startFreezeActionEnd(this, "End", 0); + al::setNerve(this, &nrvNameTagHide); + return; + } + + setText(mPuppet->getPuppetName()); + + al::startAction(this, "Appear", 0); + LayoutActor::appear(); + al::setActionFrameRate(this, 1.0, 0); + al::setNerve(this, &nrvNameTagAppear); +} + +void NameTag::control() { + + update(); + + al::LiveActor *puppetModel = mPuppet->getCurrentModel(); + + if (!al::isNerve(this, &nrvNameTagEnd) && + !al::isNerve(this, &nrvNameTagHide) && + (al::isClipped(puppetModel) || al::isDead(puppetModel))) { + al::setNerve(this, &nrvNameTagEnd); + } else { + updateTrans(); + } +} + +void NameTag::updateTrans() { + sead::Vector2f newTrans = sead::Vector2f::zero; + + sead::Vector3f targetOffset(0, 130, 0); + + al::LiveActor *puppetModel = mPuppet->getCurrentModel(); + + al::calcLayoutPosFromWorldPos(&newTrans, puppetModel, al::getTrans(puppetModel) + targetOffset); + + al::setLocalTrans(this, newTrans); + + mNormalizedDist = + 1 - al::normalize(al::calcDistance(puppetModel, al::getPlayerActor(puppetModel, 0)), 200.0f, + mEndDist); + + al::setLocalScale(this, mNormalizedDist); + +} + +void NameTag::update() { + + if (al::isNerve(this, &nrvNameTagEnd) || al::isNerve(this, &nrvNameTagHide) || !mIsAlive) { + if (isNearPlayerActor(mStartDist)) { + appear(); + } + } + + if (!al::isNerve(this, &nrvNameTagEnd) && !al::isNerve(this, &nrvNameTagHide) && + mIsAlive) { + if (!isNearPlayerActor(mEndDist)) { + al::setNerve(this, &nrvNameTagEnd); + } + } +} + +void NameTag::end() { + if (!al::isNerve(this, &nrvNameTagEnd) && !al::isNerve(this, &nrvNameTagHide)) { + al::setNerve(this, &nrvNameTagEnd); + } +} + +void NameTag::setText(const char* text) { + al::setPaneStringFormat(this, mPaneName, text); +} + +bool NameTag::isNearPlayerActor(float dist) const { + return al::isNearPlayer(mPuppet->getCurrentModel(), dist); +} + +bool NameTag::isVisible() const { + return isNearPlayerActor(mStartDist); +} + +const char* NameTag::getCurrentState() { + if (al::isNerve(this, &nrvNameTagAppear)) { + return "Appear"; + } + if (al::isNerve(this, &nrvNameTagWait)) { + return "Wait"; + } + if (al::isNerve(this, &nrvNameTagEnd)) { + return "End"; + } + if (al::isNerve(this, &nrvNameTagHide)) { + return "Hide"; + } + return "Unknown"; +} + +void NameTag::exeAppear(void) { + if (al::isActionEnd(this, 0)) + al::setNerve(this, &nrvNameTagWait); +} +void NameTag::exeWait(void) { + if (al::isFirstStep(this)) + al::startAction(this, "Wait", 0); +} +void NameTag::exeEnd(void) { + if (al::isFirstStep(this)) + al::startAction(this, "End", 0); + + if (al::isActionEnd(this, 0)) + al::setNerve(this, &nrvNameTagHide); +} + +void NameTag::exeHide(void) { } + + +namespace { +NERVE_IMPL(NameTag, Appear) +NERVE_IMPL(NameTag, Wait) +NERVE_IMPL(NameTag, End) +NERVE_IMPL(NameTag, Hide) +} \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..49b0d07 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,399 @@ +#include "main.hpp" +#include +#include +#include "server/Client.hpp" +#include "puppets/PuppetInfo.h" +#include "actors/PuppetActor.h" +#include "al/LiveActor/LiveActor.h" +#include "al/util.hpp" +#include "al/util/AudioUtil.h" +#include "al/util/CameraUtil.h" +#include "al/util/ControllerUtil.h" +#include "al/util/LiveActorUtil.h" +#include "al/util/NerveUtil.h" +#include "debugMenu.hpp" +#include "game/GameData/GameDataFunction.h" +#include "game/HakoniwaSequence/HakoniwaSequence.h" +#include "game/Player/PlayerFunction.h" +#include "game/StageScene/StageScene.h" +#include "helpers.hpp" +#include "layouts/HideAndSeekIcon.h" +#include "logger.hpp" +#include "rs/util.hpp" +#include "server/gamemode/GameModeBase.hpp" +#include "server/HideAndSeekMode.hpp" + +static int pInfSendTimer = 0; +static int gameInfSendTimer = 0; + +void updatePlayerInfo(GameDataHolderAccessor holder, PlayerActorHakoniwa *p1) { + if(pInfSendTimer >= 3) { + Client::sendPlayerInfPacket(p1); + + Client::sendHackCapInfPacket(p1->mHackCap); + + Client::sendCaptureInfPacket(p1); + + pInfSendTimer = 0; + } + + if (gameInfSendTimer >= 60) { + Client::sendGameInfPacket(p1, holder); + + gameInfSendTimer = 0; + } + + pInfSendTimer++; + gameInfSendTimer++; +} + +// ------------- Hooks ------------- + +int debugPuppetIndex = 0; +int debugCaptureIndex = 0; +static int pageIndex = 0; + +static const int maxPages = 3; + +void drawMainHook(HakoniwaSequence *curSequence, sead::Viewport *viewport, sead::DrawContext *drawContext) { + + // sead::FrameBuffer *frameBuffer; + // __asm ("MOV %[result], X21" : [result] "=r" (frameBuffer)); + + // if(Application::sInstance->mFramework) { + // Application::sInstance->mFramework->mGpuPerf->drawResult((agl::DrawContext *)drawContext, frameBuffer); + // } + + Time::calcTime(); // this needs to be ran every frame, so running it here works + + if(!debugMode) { + al::executeDraw(curSequence->mLytKit, "2Dバック(メイン画面)"); + return; + } + + //int dispWidth = al::getLayoutDisplayWidth(); + int dispHeight = al::getLayoutDisplayHeight(); + + gTextWriter->mViewport = viewport; + + gTextWriter->mColor = sead::Color4f(1.f, 1.f, 1.f, 0.8f); + + drawBackground((agl::DrawContext *)drawContext); + + gTextWriter->beginDraw(); + gTextWriter->setCursorFromTopLeft(sead::Vector2f(10.f, 10.f)); + + gTextWriter->printf("FPS: %d\n", static_cast(round(Application::sInstance->mFramework->calcFps()))); + + gTextWriter->setCursorFromTopLeft(sead::Vector2f(10.f, (dispHeight / 3) + 30.f)); + gTextWriter->setScaleFromFontHeight(20.f); + + gTextWriter->printf("Client Socket Connection Status: %s\n", Client::sInstance->mSocket->getStateChar()); + gTextWriter->printf("Packet Queue Length: %d\n", Client::sInstance->mSocket->mPacketQueue.size()); + gTextWriter->printf("Total Connected Players: %d\n", Client::getConnectCount() + 1); + + al::Scene *curScene = curSequence->curScene; + + if(curScene && isInGame) { + + sead::LookAtCamera *cam = al::getLookAtCamera(curScene, 0); + sead::Projection* projection = al::getProjectionSead(curScene, 0); + + PlayerActorHakoniwa* p1 = rs::getPlayerActor(curScene); + + PuppetActor* curPuppet = Client::getPuppet(debugPuppetIndex); + + PuppetActor *debugPuppet = Client::getDebugPuppet(); + + if (debugPuppet) { + curPuppet = debugPuppet; + } + + sead::PrimitiveRenderer *renderer = sead::PrimitiveRenderer::instance(); + renderer->setDrawContext(drawContext); + renderer->setCamera(*cam); + renderer->setProjection(*projection); + + gTextWriter->printf("----------- Page %d ------------\n", pageIndex); + switch (pageIndex) + { + case 0: + { + // PuppetActor *curPuppet = Client::getDebugPuppet(); + + if(curPuppet) { + + al::LiveActor* curModel = curPuppet->getCurrentModel(); + + PuppetInfo* curPupInfo = curPuppet->getInfo(); + + // al::LiveActor *curCapture = curPuppet->getCapture(debugCaptureIndex); + + gTextWriter->printf("Puppet Index: %d\n", debugPuppetIndex); + gTextWriter->printf("Player Name: %s\n", curPupInfo->puppetName); + gTextWriter->printf("Connection Status: %s\n", curPupInfo->isConnected ? "Online" : "Offline"); + gTextWriter->printf("Is in Same Stage: %s\n", curPupInfo->isInSameStage ? "True" : "False"); + gTextWriter->printf("Is in Capture: %s\n", curPupInfo->isCaptured ? "True" : "False"); + gTextWriter->printf("Puppet Stage: %s\n", curPupInfo->stageName); + gTextWriter->printf("Puppet Scenario: %u\n", curPupInfo->scenarioNo); + //gTextWriter->printf("Packet Coords:\nX: %f\nY: %f\nZ: %f\n", curPupInfo->playerPos.x, curPupInfo->playerPos.y, curPupInfo->playerPos.z); + // if (curModel) { + // sead::Vector3f* pupPos = al::getTrans(curModel); + // gTextWriter->printf("In-Game Coords:\nX: %f\nY: %f\nZ: %f\n", pupPos->x, pupPos->y, pupPos->z); + // } + + if(curPupInfo->isCaptured) { + gTextWriter->printf("Current Capture: %s\n", curPupInfo->curHack); + gTextWriter->printf("Current Packet Animation: %s\n", curPupInfo->curAnimStr); + gTextWriter->printf("Animation Index: %d\n", curPupInfo->curAnim); + }else { + gTextWriter->printf("Current Packet Animation: %s\n", curPupInfo->curAnimStr); + gTextWriter->printf("Animation Index: %d\n", curPupInfo->curAnim); + if (curModel) { + gTextWriter->printf("Current Animation: %s\n", al::getActionName(curModel)); + } + } + } + } + break; + case 1: + { + PuppetActor* debugPuppet = Client::getDebugPuppet(); + PuppetInfo* debugInfo = Client::getDebugPuppetInfo(); + + if (debugPuppet && debugInfo) { + + al::LiveActor *curModel = debugPuppet->getCurrentModel(); + + gTextWriter->printf("Is Nametag Visible: %s\n", BTOC(debugPuppet->mNameTag->isVisible())); + gTextWriter->printf("Is Nametag Alive: %s\n", BTOC(debugPuppet->mNameTag->mIsAlive)); + gTextWriter->printf("Nametag Normalized Dist: %f\n", debugPuppet->mNameTag->mNormalizedDist); + gTextWriter->printf("Nametag State: %s\n", debugPuppet->mNameTag->getCurrentState()); + gTextWriter->printf("Is Current Model Clipped: %s\n", + BTOC(al::isClipped(curModel))); + gTextWriter->printf("Is Debug Puppet Tagged: %s\n", BTOC(debugInfo->isIt)); + + } + } + break; + case 2: + { + al::PlayerHolder *pHolder = al::getScenePlayerHolder(curScene); + PlayerActorHakoniwa *p1 = pHolder->tryGetPlayer(0); + + if (p1->mHackKeeper && p1->mHackKeeper->currentHackActor) { + + al::LiveActor *curHack = p1->mHackKeeper->currentHackActor; + + gTextWriter->printf("Current Hack Animation: %s\n", al::getActionName(curHack)); + gTextWriter->printf("Current Hack Name: %s\n", + p1->mHackKeeper->getCurrentHackName()); + sead::Quatf captureRot = curHack->mPoseKeeper->getQuat(); + gTextWriter->printf("Current Hack Rot: %f %f %f %f\n", captureRot.x, + captureRot.y, captureRot.z, captureRot.w); + sead::Quatf calcRot; + al::calcQuat(&calcRot, curHack); + gTextWriter->printf("Calc Hack Rot: %f %f %f %f\n", calcRot.x, + calcRot.y, calcRot.z, calcRot.w); + }else { + gTextWriter->printf("Cur Action: %s\n", p1->mPlayerAnimator->mAnimFrameCtrl->getActionName()); + gTextWriter->printf("Cur Sub Action: %s\n", p1->mPlayerAnimator->curSubAnim.cstr()); + gTextWriter->printf("Is Cappy Flying? %s\n", BTOC(p1->mHackCap->isFlying())); + if(p1->mHackCap->isFlying()) { + gTextWriter->printf("Cappy Action: %s\n", al::getActionName(p1->mHackCap)); + sead::Vector3f *capTrans = al::getTransPtr(p1->mHackCap); + sead::Vector3f *capRot = &p1->mHackCap->mJointKeeper->mJointRot; + gTextWriter->printf("Cap Coords:\nX: %f\nY: %f\nZ: %f\n", capTrans->x, capTrans->y, capTrans->z); + gTextWriter->printf("Cap Rot:\nX: %f\nY: %f\nZ: %f\n", capRot->x, capRot->y, capRot->z); + gTextWriter->printf("Cap Skew: %f\n", p1->mHackCap->mJointKeeper->mSkew); + } + } + } + break; + default: + break; + } + + renderer->begin(); + + //sead::Matrix34f mat = sead::Matrix34f::ident; + //mat.setBase(3, sead::Vector3f::zero); // Sets the position of the matrix. + // For cubes, you need to put this at the location. + // For spheres, you can leave this at 0 0 0 since you set it in its draw function. + renderer->setModelMatrix(sead::Matrix34f::ident); + + if (curPuppet) { + renderer->drawSphere4x8(curPuppet->getInfo()->playerPos, 20, sead::Color4f(1.f, 0.f, 0.f, 0.25f)); + renderer->drawSphere4x8(al::getTrans(curPuppet), 20, sead::Color4f(0.f, 0.f, 1.f, 0.25f)); + } + + renderer->end(); + + isInGame = false; + } + + gTextWriter->endDraw(); + + al::executeDraw(curSequence->mLytKit, "2Dバック(メイン画面)"); + +} + +void sendShinePacket(GameDataHolderWriter thisPtr, Shine* curShine) { + + if (!curShine->isGot()) { + Client::sendShineCollectPacket(curShine->shineId); + } + + GameDataFunction::setGotShine(thisPtr, curShine->curShineInfo); +} + +void stageInitHook(al::ActorInitInfo *info, StageScene *curScene, al::PlacementInfo const *placement, al::LayoutInitInfo const *lytInfo, al::ActorFactory const *factory, al::SceneMsgCtrl *sceneMsgCtrl, al::GameDataHolderBase *dataHolder) { + + al::initActorInitInfo(info, curScene, placement, lytInfo, factory, sceneMsgCtrl, + dataHolder); + + Client::clearArrays(); + + Client::setSceneInfo(*info, curScene); + + if (Client::getServerMode() != NONE) { + GameModeInitInfo initModeInfo(info, curScene); + + Client::initMode(initModeInfo); + } + + Client::sendGameInfPacket(info->mActorSceneInfo.mSceneObjHolder); + +} + +PlayerCostumeInfo *setPlayerModel(al::LiveActor *player, const al::ActorInitInfo &initInfo, const char *bodyModel, const char *capModel, al::AudioKeeper *keeper, bool isCloset) { + Client::sendCostumeInfPacket(bodyModel, capModel); + return PlayerFunction::initMarioModelActor(player, initInfo, bodyModel, capModel, keeper, isCloset); +} + +al::SequenceInitInfo* initInfo; + +ulong constructHook() { // hook for constructing anything we need to globally be accesible + + __asm("STR X21, [X19,#0x208]"); // stores WorldResourceLoader into HakoniwaSequence + + __asm("MOV %[result], X20" + : [result] "=r"( + initInfo)); // Save our scenes init info to a gloabl ptr so we can access it later + + Client::sInstance = new Client(playBufSize); + + return 0x20; +} + +bool threadInit(HakoniwaSequence *mainSeq) { // hook for initializing client class + + al::LayoutInitInfo lytInfo = al::LayoutInitInfo(); + + al::initLayoutInitInfo(&lytInfo, mainSeq->mLytKit, 0, mainSeq->mAudioDirector, initInfo->mSystemInfo->mLayoutSys, initInfo->mSystemInfo->mMessageSys, initInfo->mSystemInfo->mGamePadSys); + + Client::sInstance->init(lytInfo); + + return GameDataFunction::isPlayDemoOpening(mainSeq->mGameDataHolder); +} + +bool hakoniwaSequenceHook(HakoniwaSequence* sequence) { + StageScene* stageScene = (StageScene*)sequence->curScene; + + static bool isCameraActive = false; + + bool isFirstStep = al::isFirstStep(sequence); + + al::PlayerHolder *pHolder = al::getScenePlayerHolder(stageScene); + PlayerActorHakoniwa* p1 = (PlayerActorHakoniwa*)al::tryGetPlayerActor(pHolder, 0); + + if (isFirstStep) { + Client::tryRestartCurrentMode(); + } + + isInGame = !stageScene->isPause(); + + Client::setGameActive(!stageScene->isPause()); + Client::setStageInfo(stageScene->mHolder); + + Client::updateStates(); + + if (Client::isNeedUpdateShines()) { + Client::updateShines(); + } + + updatePlayerInfo(stageScene->mHolder, p1); + + static bool isDisableMusic = true; + + if (al::isPadHoldZR(-1)) { + if (al::isPadTriggerUp(-1)) debugMode = !debugMode; + if (al::isPadTriggerLeft(-1)) pageIndex--; + if (al::isPadTriggerRight(-1)) pageIndex++; + if(pageIndex < 0) { + pageIndex = maxPages - 1; + } + if(pageIndex >= maxPages) pageIndex = 0; + + } else if (al::isPadHoldZL(-1)) { + + if (debugMode) { + if (al::isPadTriggerLeft(-1)) debugPuppetIndex--; + if (al::isPadTriggerRight(-1)) debugPuppetIndex++; + + if(debugPuppetIndex < 0) { + debugPuppetIndex = playBufSize - 2; + } + if (debugPuppetIndex >= playBufSize) + debugPuppetIndex = 0; + } + + } else if (al::isPadHoldL(-1)) { + if (al::isPadTriggerLeft(-1)) Client::toggleCurrentMode(); + if (al::isPadTriggerRight(-1)) { + if (debugMode) { + PuppetInfo *debugPuppet = Client::getDebugPuppetInfo(); + if (debugPuppet) { + debugPuppet->playerPos = al::getTrans(p1); + al::calcQuat(&debugPuppet->playerRot, p1); + const char *hackName = p1->mHackKeeper->getCurrentHackName(); + debugPuppet->isCaptured = hackName != nullptr; + if (debugPuppet->isCaptured) { + strcpy(debugPuppet->curHack, hackName); + } else { + strcpy(debugPuppet->curHack, ""); + } + } + } + } + if (al::isPadTriggerUp(-1)) { + if (debugMode) { + PuppetInfo* debugPuppet = Client::getDebugPuppetInfo(); + if (debugPuppet) { + debugPuppet->isIt = !debugPuppet->isIt; + } + } else { + isDisableMusic = !isDisableMusic; + } + } + } + + if (isDisableMusic) { + if (al::isPlayingBgm(stageScene)) { + al::stopAllBgm(stageScene, 0); + } + } + + return isFirstStep; + +} + +void seadPrintHook(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + Logger::log(fmt, args); + + va_end(args); +} \ No newline at end of file diff --git a/source/module.cpp b/source/module.cpp new file mode 100644 index 0000000..ebe4c02 --- /dev/null +++ b/source/module.cpp @@ -0,0 +1,14 @@ +#define MODULE_NAME "Starlight" +#define MODULE_NAME_LEN 9 + +// rtld working object +__attribute__((section(".bss"))) char __nx_module_runtime[0xD0]; + +struct ModuleName { + int unknown; + int name_lengh; + char name[MODULE_NAME_LEN + 1]; +}; + +__attribute__((section(".rodata.module_name"))) +ModuleName module_name = {.unknown = 0, .name_lengh = MODULE_NAME_LEN, .name = MODULE_NAME}; diff --git a/source/puppets/PuppetActor.cpp b/source/puppets/PuppetActor.cpp new file mode 100644 index 0000000..20b4b5d --- /dev/null +++ b/source/puppets/PuppetActor.cpp @@ -0,0 +1,381 @@ +#include "server/Client.hpp" +#include "al/LiveActor/LiveActor.h" +#include "al/layout/BalloonMessage.h" +#include "al/layout/LayoutInitInfo.h" +#include "al/string/StringTmp.h" +#include "al/util.hpp" +#include "al/util/LiveActorUtil.h" +#include "algorithms/CaptureTypes.h" +#include "logger.hpp" +#include "actors/PuppetActor.h" +#include "math/seadQuat.h" +#include "math/seadVector.h" +#include "server/gamemode/GameModeBase.hpp" +#include "server/HideAndSeekMode.hpp" + +static const char *subActorNames[] = { + "顔", // Face + "目", // Eye + "頭", // Head + "左手", // Left Hand + "右手" // Right Hand +}; + +PuppetActor::PuppetActor(const char *name) : al::LiveActor(name) { + mPuppetCap = new PuppetCapActor(name); + mCaptures = new HackModelHolder(); + mModelHolder = new PlayerModelHolder(3); // Regular Model, 2D Model, 2D Mini Model +} + +void PuppetActor::init(al::ActorInitInfo const &initInfo) { + + mPuppetCap->init(initInfo); + + al::initActorWithArchiveName(this, initInfo, "PlayerActorHakoniwa", nullptr); + + const char *bodyName = "Mario"; + const char *capName = "Mario"; + + if(mInfo) { + bodyName = tryGetPuppetBodyName(mInfo); + capName = tryGetPuppetCapName(mInfo); + + mNameTag = new NameTag(this, *al::getLayoutInitInfo(initInfo), 4900.0f, 5000.0f, + mInfo->puppetName); + + } + + al::LiveActor *normalModel = new al::LiveActor("Normal"); + + mCostumeInfo = PlayerFunction::initMarioModelCommon(normalModel, initInfo, bodyName, capName, 0, false, nullptr, false, false); + + normalModel->mActorActionKeeper->mPadAndCamCtrl->mRumbleCount = 0; // set rumble count to zero so that no rumble actions will run + + mModelHolder->registerModel(normalModel, "Normal"); + + al::LiveActor *normal2DModel = new al::LiveActor("Normal2D"); + + PlayerFunction::initMarioModelActor2D(normal2DModel, initInfo, al::StringTmp<0x40>("%s2D", mCostumeInfo->mBodyInfo->costumeName).cstr(), PlayerFunction::isInvisibleCap(mCostumeInfo)); + + mModelHolder->registerModel(normal2DModel, "Normal2D"); + + al::setClippingInfo(normalModel, 50000.0f, 0); + al::setClippingNearDistance(normalModel, 50000.0f); + + al::hideSilhouetteModelIfShow(normalModel); + + // "頭" = Head + // "髪" = Hair + // "キャップの目" = Cap Eyes + // "[デモ用]キャップの目" = [Demo] Cap Eyes + + al::LiveActor* headModel = al::getSubActor(normalModel, "頭"); + al::getSubActor(headModel, "キャップの目")->kill(); + al::startVisAnimForAction(headModel, "CapOn"); + + mModelHolder->changeModel("Normal"); + + startAction("Wait"); + + al::validateClipping(normalModel); + al::validateClipping(normal2DModel); + +} + +void PuppetActor::initAfterPlacement() { al::LiveActor::initAfterPlacement(); } + +void PuppetActor::initOnline(PuppetInfo *pupInfo) { + + mInfo = pupInfo; + + mPuppetCap->initOnline(pupInfo); +} + +void PuppetActor::movement() { + al::LiveActor::movement(); +} + +void PuppetActor::control() { + if(mInfo) { + + al::LiveActor* curModel = getCurrentModel(); + + // Animation Updating + + if(!al::isActionPlaying(curModel, mInfo->curSubAnimStr)) { + startAction(mInfo->curAnimStr); + }else if(al::isActionEnd(curModel)) { + startAction(mInfo->curAnimStr); + } + + if(isNeedBlending()) { + for (size_t i = 0; i < 6; i++) + { + setBlendWeight(i, mInfo->blendWeights[i]); + } + } + + // Position & Rotation Handling + + sead::Vector3f* pPos = al::getTransPtr(this); + + sead::Quatf *pQuat = al::getQuatPtr(this); + + if (!mIs2DModel) { + mClosingSpeed = VisualUtils::SmoothMove({pPos, pQuat}, {&mInfo->playerPos, &mInfo->playerRot}, Time::deltaTime, mClosingSpeed, 1440.0f); + } else { + + // do not linearly interpolate rotation if model is 2D, and use basic lerp instead of visual util's smooth move + + if(*pPos != mInfo->playerPos) + { + al::lerpVec(pPos, *pPos, mInfo->playerPos, 0.25); + } + + al::setQuat(this, mInfo->playerRot); + } + + // Model Updating + + if (!mIs2DModel && mInfo->is2D) { + changeModel("Normal2D"); + mIs2DModel = true; + + } else if (mIs2DModel && !mInfo->is2D) { + changeModel("Normal"); + mIs2DModel = false; + } + + // Capture Updating + + if (mInfo->isCaptured && !mIsCaptureModel) { + + getCurrentModel()->makeActorDead(); // sets previous model to dead so we can try to + // switch to capture model + setCapture(mInfo->curHack); + mIsCaptureModel = true; + getCurrentModel()->makeActorAlive(); // make new model alive + + } else if (!mInfo->isCaptured && mIsCaptureModel) { + + getCurrentModel()->makeActorDead(); // make capture model dead + mModelHolder->changeModel("Normal"); // set player model to normal + mIsCaptureModel = false; + getCurrentModel()->makeActorAlive(); // make player model alive + + } + + // Visibility Updating + + if(mInfo->isCapThrow) { + if(al::isDead(mPuppetCap)) { + mPuppetCap->makeActorAlive(); + al::setTrans(mPuppetCap, mInfo->capPos); + } + }else { + if(al::isAlive(mPuppetCap)) { + + mPuppetCap->makeActorDead(); + + startAction(mInfo->curSubAnimStr); + + al::LiveActor* headModel = al::getSubActor(curModel, "頭"); + if (headModel) { al::startVisAnimForAction(headModel, "CapOn"); } + } + } + + if (mNameTag) { + if (Client::isSelectedMode(GameMode::HIDEANDSEEK) && Client::isModeActive()) { + mNameTag->mIsAlive = + Client::getMode()->isPlayerIt() && mInfo->isIt; + + } else { + if(!mNameTag->mIsAlive) + mNameTag->appear(); + } + } + + // Sub-Actor Updating + + mPuppetCap->update(); + + // Syncing + + syncPose(); + + } +} + +void PuppetActor::makeActorAlive() { + + al::LiveActor *curModel = getCurrentModel(); + + if (al::isDead(curModel)) { + curModel->makeActorAlive(); + } + + if (al::isDead(this)) { + + // update name tag when puppet becomes active again + if (mInfo) { + if (mNameTag) { + mNameTag->setText(mInfo->puppetName); + } + } + + al::LiveActor::makeActorAlive(); + } + +} + +void PuppetActor::makeActorDead() { + + al::LiveActor *curModel = getCurrentModel(); + + if (!al::isDead(curModel)) { + curModel->makeActorDead(); + } + + if (!al::isDead(this)) { + mPuppetCap->makeActorDead(); // make sure we kill the cap puppet along with regular puppet + + al::LiveActor::makeActorDead(); + } +} + +// this is more or less how nintendo does it with marios demo puppet +void PuppetActor::startAction(const char *actName) { + + al::LiveActor* curModel = getCurrentModel(); + + if(!actName) return; + + if(al::tryStartActionIfNotPlaying(curModel, actName)) { + const char *curActName = al::getActionName(curModel); + if(curActName) { + if(al::isSklAnimExist(curModel, curActName)) { + al::clearSklAnimInterpole(curModel); + } + } + } + + for (size_t i = 0; i < 5; i++) + { + al::LiveActor* subActor = al::getSubActor(curModel, subActorNames[i]); + const char *curActName = al::getActionName(curModel); + if(subActor && curActName) { + if (al::tryStartActionIfNotPlaying(subActor, curActName)) { + if(al::isSklAnimExist(curModel, curActName)) { + al::clearSklAnimInterpole(curModel); + } + } + } + } + + al::LiveActor* faceActor = al::tryGetSubActor(curModel, "顔"); + + if (faceActor) { + al::StringTmp<0x80> faceAnim("%sFullFace", actName); + if (al::tryStartActionIfNotPlaying(faceActor, faceAnim.cstr())) { + if(al::isSklAnimExist(faceActor, faceAnim.cstr())) { + al::clearSklAnimInterpole(faceActor); + } + } + } +} + +void PuppetActor::hairControl() { + + al::LiveActor *curModel = getCurrentModel(); + + if (mCostumeInfo->isNeedSyncBodyHair()) + { + PlayerFunction::syncBodyHairVisibility(al::getSubActor(curModel, "髪"), al::getSubActor(curModel, "頭")); + } + if (mCostumeInfo->isSyncFaceBeard()) + { + PlayerFunction::syncMarioFaceBeardVisibility(al::getSubActor(curModel, "顔"), al::getSubActor(curModel, "頭")); + } + if (mCostumeInfo->isSyncStrap()) + { + PlayerFunction::syncMarioHeadStrapVisibility(al::getSubActor(curModel, "頭")); + } + if (PlayerFunction::isNeedHairControl(mCostumeInfo->mBodyInfo, mCostumeInfo->mHeadInfo->costumeName)) + { + PlayerFunction::hideHairVisibility(al::getSubActor(curModel, "頭")); + } +} + +bool PuppetActor::isNeedBlending() { + const char *curActName = al::getActionName(getCurrentModel()); + if(curActName) { + return al::isEqualSubString(curActName, "Move") || al::isEqualSubString(curActName, "Sand") || al::isEqualSubString(curActName, "MotorcycleRide"); + }else { + return false; + } +} + +bool PuppetActor::isInCaptureList(const char *hackName) { + return mCaptures->getCapture(hackName) != nullptr; +} + +bool PuppetActor::addCapture(PuppetHackActor* capture, const char* hackType) { + + if (mCaptures->addCapture(capture, hackType)) { + return true; + } + + return false; +} + +void PuppetActor::changeModel(const char* newModel) { + getCurrentModel()->makeActorDead(); + mModelHolder->changeModel(newModel); + getCurrentModel()->makeActorAlive(); +} + +al::LiveActor* PuppetActor::getCurrentModel() { + if (mIsCaptureModel) { + al::LiveActor* curCapture = mCaptures->getCurrentActor(); + if (curCapture) { + return curCapture; + } + } + return mModelHolder->currentModel->mLiveActor; +} + +void PuppetActor::debugTeleportCaptures(const sead::Vector3f& pos) { + for (int i = 0; i < mCaptures->getEntryCount(); i++) { + al::LiveActor* capture = mCaptures->getCapture(i); + if (capture) { + al::setTrans(capture, al::getTrans(getCurrentModel())); + } + } +} + +void PuppetActor::debugTeleportCapture(const sead::Vector3f& pos, int index) { + al::LiveActor* capture = mCaptures->getCapture(index); + if (capture) { + al::setTrans(capture, al::getTrans(getCurrentModel())); + } +} + +bool PuppetActor::setCapture(const char* captureName) { + if (captureName && mCaptures->setCurrent(captureName)) { + mCurCapture = CaptureTypes::FindType(captureName); + return true; + } else { + mCurCapture = CaptureTypes::Type::Unknown; + return false; + } +} + +void PuppetActor::syncPose() { + + al::LiveActor* curModel = getCurrentModel(); + + curModel->mPoseKeeper->updatePoseQuat(al::getQuat(this)); // update pose using a quaternion instead of setting quaternion rotation + + al::setTrans(curModel, al::getTrans(this)); + +} diff --git a/source/puppets/PuppetCapActor.cpp b/source/puppets/PuppetCapActor.cpp new file mode 100644 index 0000000..b4ec376 --- /dev/null +++ b/source/puppets/PuppetCapActor.cpp @@ -0,0 +1,69 @@ +#include "actors/PuppetCapActor.h" +#include "al/util/MathUtil.h" + +PuppetCapActor::PuppetCapActor(const char *name) : al::LiveActor(name) {} + +void PuppetCapActor::init(al::ActorInitInfo const &initInfo) { + + sead::FixedSafeString<0x20> capModelName; + + PlayerFunction::createCapModelName(&capModelName, tryGetPuppetCapName(mInfo)); + + PlayerFunction::initCapModelActorDemo(this, initInfo, capModelName.cstr()); + + al::hideSilhouetteModelIfShow(this); + + al::initExecutorModelUpdate(this, initInfo); + + mJointKeeper = new HackCapJointControlKeeper(); + + mJointKeeper->initCapJointControl(this); + + makeActorDead(); +} + +void PuppetCapActor::initAfterPlacement() { + al::LiveActor::initAfterPlacement(); +} + +void PuppetCapActor::initOnline(PuppetInfo *pupInfo) { + mInfo = pupInfo; +} + +void PuppetCapActor::movement() { + al::LiveActor::movement(); +} + +void PuppetCapActor::control() { + if(mInfo->capAnim) { + startAction(mInfo->capAnim); + } + + sead::Vector3f *cPos = al::getTransPtr(this); + + if(*cPos != mInfo->capPos) + { + al::lerpVec(cPos, *cPos, mInfo->capPos, 0.45); + } + + mJointKeeper->mJointRot.x = al::lerpValue(mJointKeeper->mJointRot.x, mInfo->capRot.x, 0.85); + mJointKeeper->mJointRot.y = al::lerpValue(mJointKeeper->mJointRot.y, mInfo->capRot.y, 0.85); + mJointKeeper->mJointRot.z = al::lerpValue(mJointKeeper->mJointRot.z, mInfo->capRot.z, 0.85); + mJointKeeper->mSkew = al::lerpValue(mJointKeeper->mSkew, mInfo->capRot.w, 0.85); +} + +void PuppetCapActor::update() { + al::LiveActor::calcAnim(); + al::LiveActor::movement(); +} + +void PuppetCapActor::startAction(const char *actName) { + if(al::tryStartActionIfNotPlaying(this, actName)) { + const char *curActName = al::getActionName(this); + if(curActName) { + if(al::isSklAnimExist(this, curActName)) { + al::clearSklAnimInterpole(this); + } + } + } +} \ No newline at end of file diff --git a/source/puppets/PuppetHackActor.cpp b/source/puppets/PuppetHackActor.cpp new file mode 100644 index 0000000..cbebb52 --- /dev/null +++ b/source/puppets/PuppetHackActor.cpp @@ -0,0 +1,91 @@ +#include "actors/PuppetHackActor.h" +#include "al/util.hpp" +#include "logger.hpp" + +PuppetHackActor::PuppetHackActor(const char *name) : al::LiveActor(name) {} + +void PuppetHackActor::init(al::ActorInitInfo const &initInfo) { + + // Logger::log("Creating Hack Puppet: %s\n", mHackType.cstr()); + + al::initActorWithArchiveName(this, initInfo, mHackType, nullptr); + + al::hideSilhouetteModelIfShow(this); + + if(al::isExistDitherAnimator(this)) { + // Logger::log("Disabling Dither Animator.\n"); + al::invalidateDitherAnim(this); + } + + if (al::isExistCollisionParts(this)) { + // Logger::log(("Disabling Collision.\n")); + al::invalidateCollisionParts(this); + } + + al::invalidateHitSensors(this); + + al::invalidateClipping(this); + + al::offCollide(this); + + makeActorDead(); + + startHackAnim(true); // this hack puppet will always be captured so its Hack visibility should be true + + startAction("Wait"); +} + +void PuppetHackActor::initAfterPlacement() { + al::LiveActor::initAfterPlacement(); +} + +void PuppetHackActor::initOnline(PuppetInfo *pupInfo, const char *hackType) { + mInfo = pupInfo; + mHackType = hackType; +} + +void PuppetHackActor::movement() { + al::LiveActor::movement(); +} + +void PuppetHackActor::control() { + +} + +void PuppetHackActor::startAction(const char *actName) { + if(al::tryStartActionIfNotPlaying(this, actName)) { + const char *curActName = al::getActionName(this); + if(curActName) { + if(al::isSklAnimExist(this, curActName)) { + al::clearSklAnimInterpole(this); + } + } + } +} + +void PuppetHackActor::startHackAnim(bool isOn) { + + const char *animName = isOn ? "HackOn" : "HackOff"; + const char *capOffName = isOn ? "HackOnCapOff" : "HackOffCapOff"; + + if (al::isVisAnimExist(this, animName)) { + al::startVisAnim(this, animName); + } else if (al::isVisAnimExist(this, capOffName)) { + al::startVisAnim(this, capOffName); + } + + if (al::isMtpAnimExist(this, animName)) { + al::startMtpAnim(this, animName); + } else if (al::isMtpAnimExist(this, capOffName)) { + al::startMtpAnim(this, capOffName); + } + + if (al::isMclAnimExist(this, animName)) { + al::startMclAnim(this, animName); + } else if (al::isMclAnimExist(this, capOffName)) { + al::startMclAnim(this, capOffName); + } + + // note: we will need to handle special names for hack start anims + +} \ No newline at end of file diff --git a/source/puppets/PuppetHolder.cpp b/source/puppets/PuppetHolder.cpp new file mode 100644 index 0000000..5fe34a9 --- /dev/null +++ b/source/puppets/PuppetHolder.cpp @@ -0,0 +1,65 @@ +#include "puppets/PuppetHolder.hpp" +#include "al/util.hpp" +#include "logger.hpp" + +PuppetHolder::PuppetHolder(int size) { + mPuppetArr = sead::PtrArray(); + if(!mPuppetArr.tryAllocBuffer(size, nullptr)) { + Logger::log("Buffer Alloc Failed on Puppet Holder!\n"); + } +} + +bool PuppetHolder::tryRegisterPuppet(PuppetActor *puppet) { + if(!mPuppetArr.isFull()) { + mPuppetArr.pushBack(puppet); + return true; + }else { + return false; + } +} + +bool PuppetHolder::tryRegisterDebugPuppet(PuppetActor *puppet) { + mDebugPuppet = puppet; + return true; +} + +PuppetActor *PuppetHolder::getDebugPuppet() { + if(mDebugPuppet) { + return mDebugPuppet; + } + return nullptr; +} + +void PuppetHolder::update() { + + for (size_t i = 0; i < mPuppetArr.size(); i++) + { + PuppetActor *curPuppet = mPuppetArr[i]; + PuppetInfo *curInfo = curPuppet->getInfo(); + + curInfo->isInSameStage = checkInfoIsInStage(curInfo); + + if(curInfo->isInSameStage) { + curPuppet->makeActorAlive(); + }else if(!curInfo->isInSameStage) { + curPuppet->makeActorDead(); + } + } +} + +bool PuppetHolder::checkInfoIsInStage(PuppetInfo *info) { + if (info->isConnected) { + if (info->scenarioNo < 15) { + return al::isEqualString(mStageName.cstr(), info->stageName); + } else { + return al::isEqualString(mStageName.cstr(), info->stageName) && info->scenarioNo == mScenarioNo; + } + } + + return false; +} + +void PuppetHolder::setStageInfo(const char *stageName, u8 scenarioNo) { + mStageName = stageName; + mScenarioNo = scenarioNo; +} \ No newline at end of file diff --git a/source/puppets/PuppetMain.cpp b/source/puppets/PuppetMain.cpp new file mode 100644 index 0000000..d3de1c9 --- /dev/null +++ b/source/puppets/PuppetMain.cpp @@ -0,0 +1,73 @@ +#include "server/Client.hpp" +#include "logger.hpp" +#include "main.hpp" + +al::LiveActor *createPuppetActorFromFactory(al::ActorInitInfo const &rootInitInfo, al::PlacementInfo const &rootPlacementInfo, bool isDebug) { + al::ActorInitInfo actorInitInfo = al::ActorInitInfo(); + actorInitInfo.initViewIdSelf(&rootPlacementInfo, rootInitInfo); + + al::createActor createActor = actorInitInfo.mActorFactory->getCreator("PuppetActor"); + + if(createActor) { + PuppetActor *newActor = (PuppetActor*)createActor("PuppetActor"); + + if(!isDebug) { + if(Client::tryAddPuppet(newActor)) { + PuppetInfo *curInfo = Client::getLatestInfo(); + if(!curInfo) { + Logger::log("Puppet Info is Null!\n"); + }else { + + newActor->initOnline(curInfo); // set puppet info first before calling init so we can get costume info from the info + + newActor->init(actorInitInfo); + } + } + } else { + + Logger::log("Creating Test Puppet.\n"); + + newActor->initOnline(Client::getDebugPuppetInfo()); + + newActor->init(actorInitInfo); + + newActor->mIsDebug = true; + + newActor->makeActorAlive(); + + if (Client::tryAddDebugPuppet(newActor)) { + Logger::log("Debug Puppet Created!\n"); + } + + } + + return newActor; + }else { + return nullptr; + } +} + +// Hooks + +void initPuppetActors(al::Scene *scene, al::ActorInitInfo const &rootInfo, char const *listName) { + al::StageInfo *stageInfo = al::getStageInfoMap(scene, 0); + + int placementCount = 0; + al::PlacementInfo rootPlacement = al::PlacementInfo(); + al::tryGetPlacementInfoAndCount(&rootPlacement, &placementCount, stageInfo, "PlayerList"); + // check if default placement count for PlayerList is greater than 0 (so we can use the placement info of the player to init our custom puppet actors) + if(placementCount > 0) { + al::PlacementInfo playerPlacement = al::PlacementInfo(); + al::getPlacementInfoByIndex(&playerPlacement, rootPlacement, 0); + + for (size_t i = 0; i < Client::sInstance->maxPuppets; i++) + { + createPuppetActorFromFactory(rootInfo, playerPlacement, false); + } + + // create a debug puppet for testing purposes + // createPuppetActorFromFactory(rootInfo, playerPlacement, true); + } + + al::initPlacementObjectMap(scene, rootInfo, listName); // run init for ObjectList after we init our puppet actors +} \ No newline at end of file diff --git a/source/sead/seadNew.cpp b/source/sead/seadNew.cpp new file mode 100644 index 0000000..9b7844d --- /dev/null +++ b/source/sead/seadNew.cpp @@ -0,0 +1,172 @@ +#include + +#include "basis/seadNew.h" +#include "heap/seadHeap.h" +#include "heap/seadHeapMgr.h" + +namespace sead +{ + namespace system + { + void* NewImpl(Heap* heap, size_t size, s32 alignment, bool abortOnFailure); + void DeleteImpl(void* ptr); + } // namespace system +} // namespace sead + +// operator new(size_t) + +// operator new(size_t) + +void* operator new(size_t size) +{ + return sead::system::NewImpl(nullptr, size, 8, true); +} + +void* operator new[](size_t size) +{ + return sead::system::NewImpl(nullptr, size, 8, true); +} + +void* operator new(size_t size, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(nullptr, size, 8, false); +} + +void* operator new[](size_t size, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(nullptr, size, 8, false); +} + +// operator new(size_t, s32 alignment) + +void* operator new(size_t size, s32 alignment) +{ + return sead::system::NewImpl(nullptr, size, alignment, true); +} + +void* operator new[](size_t size, s32 alignment) +{ + return sead::system::NewImpl(nullptr, size, alignment, true); +} + +void* operator new(size_t size, s32 alignment, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(nullptr, size, alignment, false); +} + +void* operator new[](size_t size, s32 alignment, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(nullptr, size, alignment, false); +} + +// operator new(size_t, sead::Heap*, s32 alignment) + +void* operator new(size_t size, sead::Heap* heap, s32 alignment) +{ + return sead::system::NewImpl(heap, size, alignment, true); +} + +void* operator new[](size_t size, sead::Heap* heap, s32 alignment) +{ + return sead::system::NewImpl(heap, size, alignment, true); +} + +void* operator new(size_t size, sead::Heap* heap, s32 alignment, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(heap, size, alignment, false); +} + +void* operator new[](size_t size, sead::Heap* heap, s32 alignment, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(heap, size, alignment, false); +} + +// operator new(size_t, sead::Heap*, const std::nothrow_t&) + +void* operator new(size_t size, sead::Heap* heap, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(heap, size, 8, false); +} + +void* operator new[](size_t size, sead::Heap* heap, const std::nothrow_t&) noexcept +{ + return sead::system::NewImpl(heap, size, 8, false); +} + +// operator delete(void*) + +void operator delete(void* ptr) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete[](void* ptr) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete(void* ptr, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete[](void* ptr, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +// operator delete(void*, s32) + +void operator delete(void* ptr, s32) +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete[](void* ptr, s32) +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete(void* ptr, s32, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete[](void* ptr, s32, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +// operator delete(void*, sead::Heap*, const std::nothrow_t&) + +void operator delete(void* ptr, sead::Heap*, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete[](void* ptr, sead::Heap*, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +// operator delete(void*, sead::Heap*, s32) + +void operator delete(void* ptr, sead::Heap*, s32) +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete[](void* ptr, sead::Heap*, s32) +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete(void* ptr, sead::Heap*, s32, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} + +void operator delete[](void* ptr, sead::Heap*, s32, const std::nothrow_t&) noexcept +{ + sead::system::DeleteImpl(ptr); +} \ No newline at end of file diff --git a/source/sead/time/seadCalendarTime.cpp b/source/sead/time/seadCalendarTime.cpp new file mode 100644 index 0000000..f129b3f --- /dev/null +++ b/source/sead/time/seadCalendarTime.cpp @@ -0,0 +1,187 @@ +#include +#include +#include +#include + +namespace sead +{ +const CalendarTime::Month CalendarTime::cMonth_Jan = 1; +const CalendarTime::Month CalendarTime::cMonth_Feb = 2; +const CalendarTime::Month CalendarTime::cMonth_Mar = 3; +const CalendarTime::Month CalendarTime::cMonth_Apr = 4; +const CalendarTime::Month CalendarTime::cMonth_May = 5; +const CalendarTime::Month CalendarTime::cMonth_Jun = 6; +const CalendarTime::Month CalendarTime::cMonth_Jul = 7; +const CalendarTime::Month CalendarTime::cMonth_Aug = 8; +const CalendarTime::Month CalendarTime::cMonth_Sep = 9; +const CalendarTime::Month CalendarTime::cMonth_Oct = 10; +const CalendarTime::Month CalendarTime::cMonth_Nov = 11; +const CalendarTime::Month CalendarTime::cMonth_Dec = 12; + +const CalendarTime::Year CalendarTime::cDefaultYear = 1970; +const CalendarTime::Month CalendarTime::cDefaultMonth = 1; +const CalendarTime::Day CalendarTime::cDefaultDay = 1; +const CalendarTime::Hour CalendarTime::cDefaultHour = 0; +const CalendarTime::Minute CalendarTime::cDefaultMinute = 0; +const CalendarTime::Second CalendarTime::cDefaultSecond = 0; + +void CalendarTime::Year::setValue(u32 year) +{ + mValue = year; +} + +CalendarTime::Month::Month(u32 month) +{ + setValueOneOrigin(month); +} + +void CalendarTime::Month::setValueOneOrigin(u32 m) +{ + SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m); + mValue = m; +} + +s32 CalendarTime::Month::addSelf(u32 rhs) +{ + const s32 val = (s32(rhs) + mValue + -1) % 12; + mValue = val + 1; + SEAD_ASSERT(1 <= mValue && mValue <= 12); + return val; +} + +s32 CalendarTime::Month::subSelf(u32 rhs) +{ + const s32 val = (mValue - s32(rhs) % 12 + 12 - 1) % 12u; + mValue = val + 1; + SEAD_ASSERT(1 <= mValue && mValue <= 12); + return val; +} + +s32 CalendarTime::Month::sub(CalendarTime::Month rhs) const +{ + return s32(mValue) - rhs.getValueOneOrigin(); +} + +SafeString CalendarTime::Month::makeStringOneOrigin(u32 m) +{ + SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m); + switch (m) + { + case 1: + return "Jan"; + case 2: + return "Feb"; + case 3: + return "Mar"; + case 4: + return "Apr"; + case 5: + return "May"; + case 6: + return "Jun"; + case 7: + return "Jul"; + case 8: + return "Aug"; + case 9: + return "Sep"; + case 10: + return "Oct"; + case 11: + return "Nov"; + case 12: + default: + return "Dec"; + } +} + +CalendarTime::Month CalendarTime::Month::makeFromValueOneOrigin(u32 m) +{ + SEAD_ASSERT(1 <= m && m <= 12); + return Month(m); +} + +void CalendarTime::Day::setValue(u32 day) +{ + SEAD_ASSERT_MSG(1 <= day && day <= 31, "wrong day. correct range is [1, 31]. your param %d", + day); + mValue = day; +} + +void CalendarTime::Hour::setValue(u32 hour) +{ + SEAD_ASSERT_MSG(hour <= 23, "wrong hour. correct range is [0, 23]. your param %d", hour); + mValue = hour; +} + +void CalendarTime::Minute::setValue(u32 minute) +{ + SEAD_ASSERT_MSG(minute <= 59, "wrong minute. correct range is [0, 59]. your param %d", minute); + mValue = minute; +} + +void CalendarTime::Second::setValue(u32 second) +{ + SEAD_ASSERT_MSG(second <= 59, "wrong day. correct range is [0, 59]. your param %d", second); + mValue = second; +} + +CalendarTime::Date::Date(const CalendarTime::Year& y, const CalendarTime::Month& m, + const CalendarTime::Day& d) + : mYear(y), mMonth(m), mDay(d) +{ + mWeek = DateUtil::calcWeekDay(y, m, d); +} + +CalendarTime::Time::Time(const CalendarTime::Hour& h, const CalendarTime::Minute& m, + const CalendarTime::Second& s) + : mHour(h), mMinute(m), mSecond(s) +{ +} + +CalendarTime::CalendarTime(const CalendarTime::Date& date, const CalendarTime::Time& time) + : mDate(date), mTime(time) +{ +} + +CalendarTime::CalendarTime(const CalendarTime::Year& y, const CalendarTime::Month& m, + const CalendarTime::Day& d, const CalendarTime::Hour& hour, + const CalendarTime::Minute& minute, const CalendarTime::Second& second) + : mDate(y, m, d), mTime(hour, minute, second) +{ +} + +void CalendarTime::setDate(const CalendarTime::Date& date) +{ + mDate = date; + mDate.calcWeek(); +} + +u32 CalendarTime::getYearDays() const +{ + const u32 m = mDate.mMonth.getValueOneOrigin(); + SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m); + + static const u32 sCumulativeNumberOfDays[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + }; + u32 num_days = mDate.mDay.getValue() + sCumulativeNumberOfDays[m - 1]; + + if (m >= 3) + num_days += DateUtil::isLeapYear(mDate.mYear.getValue()); + + return num_days; +} + +void CalendarTime::Date::calcWeek() +{ + mWeek = DateUtil::calcWeekDay(mYear, mMonth, mDay); +} + +void CalendarTime::makeWeekDayNameLabel_(BufferedSafeString* out_str, CalendarTime::Week week) +{ + static const SafeArray labels = {{"日", "月", "火", "水", "木", "金", "土"}}; + out_str->format("曜日:%s", labels[s32(week)]); +} + +} // namespace sead diff --git a/source/sead/time/seadDateSpan.cpp b/source/sead/time/seadDateSpan.cpp new file mode 100644 index 0000000..e2e94fa --- /dev/null +++ b/source/sead/time/seadDateSpan.cpp @@ -0,0 +1,41 @@ +#include +#include + +namespace sead +{ +DateSpan::DateSpan(s64 span) : mSpan(span) {} + +DateSpan::DateSpan(const CalendarSpan::Day& d, const CalendarSpan::Hour& h, + const CalendarSpan::Minute& m, const CalendarSpan::Second& s) +{ + set(d, h, m, s); +} + +DateSpan::DateSpan(const CalendarSpan& span) +{ + set(span); +} + +s64 DateSpan::set(const CalendarSpan& span) +{ + return setTimeImpl_(span.getDays(), span.getHours(), span.getMinutes(), span.getSeconds()); +} + +s64 DateSpan::set(const CalendarSpan::Day& d, const CalendarSpan::Hour& h, + const CalendarSpan::Minute& m, const CalendarSpan::Second& s) +{ + return setTimeImpl_(d.getValue(), h.getValue(), m.getValue(), s.getValue()); +} + +void DateSpan::getCalendarSpan(CalendarSpan* out_span) const +{ + DateUtil::calcSecondToCalendarSpan(out_span, mSpan); +} + +s64 DateSpan::setTimeImpl_(s32 d, s32 h, s32 m, s32 s) +{ + mSpan = 86400ll * d + 3600ll * h + 60ll * m + s; + return mSpan; +} + +} // namespace sead diff --git a/source/sead/time/seadDateTime.cpp b/source/sead/time/seadDateTime.cpp new file mode 100644 index 0000000..709bf6f --- /dev/null +++ b/source/sead/time/seadDateTime.cpp @@ -0,0 +1,218 @@ +#include + +#include "sead/basis/seadRawPrint.h" +#include "sead/time/seadDateTime.h" +#include "sead/time/seadDateUtil.h" + +namespace sead +{ +namespace +{ +constexpr u32 sDaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +constexpr u32 sDaysSinceJan1[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + +u32 convertCalendarDateToDaysSince1970(const CalendarTime::Date& date) +{ + // 0-indexed day. + const u32 d0 = date.mDay.getValue() - 1; + + const u32 m = date.mMonth.getValueOneOrigin(); + SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m); + + const u32 y = date.mYear.getValue(); + SEAD_ASSERT_MSG(y >= 1970, "wrong year. your param %d, must after 1970.", y); + + const u32 days_since_jan1 = sDaysSinceJan1[m - 1]; + + u32 num_days = d0 + days_since_jan1; + if (m >= 3 && DateUtil::isLeapYear(date.mYear.getValue())) + num_days = d0 + days_since_jan1 + 1; + + u32 num_days_since_1970 = num_days + 365 * (date.mYear.getValue() - 1970); + if (date.mYear.getValue() > 1970) + { + u32 year = 1970; + do + { + num_days_since_1970 += DateUtil::isLeapYear(year); + ++year; + } while (year < date.mYear.getValue()); + } + + return num_days_since_1970; +} + +u64 convertCalendarTimeToSeconds(const CalendarTime::Time& time) +{ + return 60 * (60 * time.mHour.getValue() + time.mMinute.getValue()) + time.mSecond.getValue(); +} + +u64 convertCalendarDateTimeToSeconds(const CalendarTime::Date& date, const CalendarTime::Time& time) +{ + const u32 y = date.mYear.getValue(); + + const s32 m = date.mMonth.getValueOneOrigin(); + const u32 m_idx = m - 1; + SEAD_ASSERT_MSG(1 <= m && m <= 12, "wrong month. correct range is [1, 12]. your param %d", m); + + u32 num_days; + if (m == 2 && DateUtil::isLeapYear(y)) + num_days = sDaysOfMonth[m_idx] + 1; + else + num_days = sDaysOfMonth[m_idx]; + + const u32 d = date.mDay.getValue(); + SEAD_ASSERT_MSG(d <= num_days, "wrong day, correct range is [1, %d] (when year %4d month %2d)", + num_days, y, m); + + const u32 days_since_1970 = convertCalendarDateToDaysSince1970(date); + return 3600 * 24 * days_since_1970 + convertCalendarTimeToSeconds(time); +} + +u32 convertDaysToYears(u32* days) +{ + u32 days_to_remove; + u32 i = 0; + u32 year = 1969; + do + { + ++year; + days_to_remove = i; + i += DateUtil::isLeapYear(year) ? 366 : 365; + } while (i <= *days); + *days -= days_to_remove; + return year; +} + +s32 convertDaysToMonth(u32* days, u32 year) +{ + SEAD_ASSERT_MSG(*days <= 365, "wrong days. correct range is [0, 365]. your param %d", *days); + u32 days_to_remove; + u32 month_idx = 0; + u32 i = 0; + do + { + days_to_remove = i; + i += (month_idx == 1 && DateUtil::isLeapYear(year)) ? 29 : sDaysOfMonth[month_idx]; + if (*days < i) + break; + ++month_idx; + } while (month_idx < 12); + *days -= days_to_remove - 1; + return 1 + month_idx; +} +} // namespace + +bool DateTime::mIsInitialized = false; + +DateTime::DateTime(u64 unix_time) +{ + mUnixTime = unix_time; +} + +DateTime::DateTime(const CalendarTime& time) +{ + setUnixTime(time); +} + +DateTime::DateTime(const CalendarTime::Year& year, const CalendarTime::Month& month, + const CalendarTime::Day& day, const CalendarTime::Hour& hour, + const CalendarTime::Minute& minute, const CalendarTime::Second& second) +{ + setUnixTime(year, month, day, hour, minute, second); +} + +u64 DateTime::setNow() +{ + initializeSystemTimeModule(); + + nn::time::PosixTime now; + nn::time::CalendarTime ctime; + nn::time::StandardUserSystemClock::GetCurrentTime(&now); + nn::time::ToCalendarTime(&ctime, nullptr, now); + + const auto year = CalendarTime::Year(ctime.year); + const auto month = CalendarTime::Month::makeFromValueOneOrigin(ctime.month); + const auto day = CalendarTime::Day(ctime.day); + const auto hour = CalendarTime::Hour(ctime.hour); + const auto minute = CalendarTime::Minute(ctime.minute); + const auto second = CalendarTime::Second(ctime.second); + setUnixTime(year, month, day, hour, minute, second); + return mUnixTime; +} + +u64 DateTime::setUnixTime(const CalendarTime& time) +{ + mUnixTime = convertCalendarDateTimeToSeconds(time.getDate(), time.getTime()); + return mUnixTime; +} + +u64 DateTime::setUnixTime(const CalendarTime::Year& year, const CalendarTime::Month& month, + const CalendarTime::Day& day, const CalendarTime::Hour& hour, + const CalendarTime::Minute& minute, const CalendarTime::Second& second) +{ + CalendarTime::Date date(year, month, day); + CalendarTime::Time time(hour, minute, second); + mUnixTime = convertCalendarDateTimeToSeconds(date, time); + return mUnixTime; +} + +void DateTime::getCalendarTime(CalendarTime* calendar) const +{ + u32 d = mUnixTime / (3600 * 24); + const u32 y = convertDaysToYears(&d); + const u32 m = convertDaysToMonth(&d, y); + + CalendarTime::Time time; + const auto reduced_time = mUnixTime % (3600 * 24); + time.mHour.setValue(reduced_time / 3600); + time.mMinute.setValue((reduced_time % 3600) / 60); + time.mSecond.setValue(reduced_time % 60); + + if (calendar) + { + calendar->setDate(CalendarTime::Date(y, CalendarTime::Month::makeFromValueOneOrigin(m), d)); + calendar->setTime(time); + } +} + +DateSpan DateTime::diff(DateTime time) const +{ + return DateSpan(mUnixTime - time.mUnixTime); +} + +DateSpan DateTime::diffToNow() const +{ + DateTime now(0); + now.setNow(); + return now.diff(*this); +} + +void DateTime::initializeSystemTimeModule() +{ + if (mIsInitialized) + return; + +#ifdef NNSDK + if (!nn::time::IsInitialized()) + nn::time::Initialize(); +#endif + + mIsInitialized = true; +} + +DateSpan operator-(DateTime lhs, DateTime rhs) +{ + return DateSpan(lhs.getUnixTime() - rhs.getUnixTime()); +} + +DateTime operator-(DateTime time, DateSpan span) +{ + return DateTime(time.getUnixTime() - span.getSpan()); +} + +DateTime operator+(DateTime time, DateSpan span) +{ + return DateTime(time.getUnixTime() + span.getSpan()); +} +} // namespace sead diff --git a/source/sead/time/seadDateTimeUtc.cpp b/source/sead/time/seadDateTimeUtc.cpp new file mode 100644 index 0000000..329f183 --- /dev/null +++ b/source/sead/time/seadDateTimeUtc.cpp @@ -0,0 +1,124 @@ +#include + +#include "sead/basis/seadRawPrint.h" +#include "sead/time/seadDateTime.h" +#include "sead/time/seadDateUtil.h" + +namespace sead +{ +DateTimeUtc::DateTimeUtc(u64 unix_time) +{ + mUnixTime = unix_time; +} + +DateTimeUtc::DateTimeUtc(const DateTime& date_time) +{ + CalendarTime time; + date_time.getCalendarTime(&time); + + // Note: time sysmodule is not initialised here. + nn::time::CalendarTime nn_time; + nn_time.year = time.getYear(); + nn_time.month = time.getMonth().getValueOneOrigin(); + nn_time.day = time.getDay(); + nn_time.hour = time.getHour(); + nn_time.minute = time.getMinute(); + nn_time.second = time.getSecond(); + nn::time::PosixTime posix_time; + int dummy = 0; + nn::time::ToPosixTime(&dummy, &posix_time, 1, nn_time); + mUnixTime = posix_time.time; +} + +DateTimeUtc::DateTimeUtc(const CalendarTime& time) +{ + setUnixTime(time); +} + +DateTimeUtc::DateTimeUtc(const CalendarTime::Year& year, const CalendarTime::Month& month, + const CalendarTime::Day& day, const CalendarTime::Hour& hour, + const CalendarTime::Minute& minute, const CalendarTime::Second& second) +{ + setUnixTime(year, month, day, hour, minute, second); +} + +u64 DateTimeUtc::setNow() +{ + DateTime::initializeSystemTimeModule(); + nn::time::PosixTime now; + nn::time::StandardUserSystemClock::GetCurrentTime(&now); + mUnixTime = now.time; + return mUnixTime; +} + +u64 DateTimeUtc::setUnixTime(const CalendarTime& time) +{ + DateTime::initializeSystemTimeModule(); + nn::time::CalendarTime nn_time; + nn_time.year = time.getYear(); + nn_time.month = time.getMonth().getValueOneOrigin(); + nn_time.day = time.getDay(); + nn_time.hour = time.getHour(); + nn_time.minute = time.getMinute(); + nn_time.second = time.getSecond(); + mUnixTime = nn::time::ToPosixTimeFromUtc(nn_time).time; + return mUnixTime; +} + +u64 DateTimeUtc::setUnixTime(const CalendarTime::Year& year, const CalendarTime::Month& month, + const CalendarTime::Day& day, const CalendarTime::Hour& hour, + const CalendarTime::Minute& minute, const CalendarTime::Second& second) +{ + DateTime::initializeSystemTimeModule(); + CalendarTime time(year, month, day, hour, minute, second); + nn::time::CalendarTime nn_time; + nn_time.year = time.getYear(); + nn_time.month = time.getMonth().getValueOneOrigin(); + nn_time.day = time.getDay(); + nn_time.hour = time.getHour(); + nn_time.minute = time.getMinute(); + nn_time.second = time.getSecond(); + mUnixTime = nn::time::ToPosixTimeFromUtc(nn_time).time; + return mUnixTime; +} + +void DateTimeUtc::getCalendarTime(CalendarTime* out) const +{ + DateTime::initializeSystemTimeModule(); + const nn::time::CalendarTime ctime = nn::time::ToCalendarTimeInUtc({mUnixTime}); + + CalendarTime::Date date(ctime.year, CalendarTime::Month::makeFromValueOneOrigin(ctime.month), + ctime.day); + CalendarTime::Time time(ctime.hour, ctime.minute, ctime.second); + + out->setDate(date); + out->setTime(time); +} + +DateSpan DateTimeUtc::diff(DateTimeUtc time) const +{ + return DateSpan(mUnixTime - time.mUnixTime); +} + +DateSpan DateTimeUtc::diffToNow() const +{ + DateTimeUtc now(0); + now.setNow(); + return now.diff(*this); +} + +DateSpan operator-(DateTimeUtc lhs, DateTimeUtc rhs) +{ + return DateSpan(lhs.getUnixTime() - rhs.getUnixTime()); +} + +DateTimeUtc operator-(DateTimeUtc time, DateSpan span) +{ + return DateTimeUtc(time.getUnixTime() - span.getSpan()); +} + +DateTimeUtc operator+(DateTimeUtc time, DateSpan span) +{ + return DateTimeUtc(time.getUnixTime() + span.getSpan()); +} +} // namespace sead diff --git a/source/sead/time/seadDateUtil.cpp b/source/sead/time/seadDateUtil.cpp new file mode 100644 index 0000000..0fe5588 --- /dev/null +++ b/source/sead/time/seadDateUtil.cpp @@ -0,0 +1,196 @@ +#include +#include +#include + +namespace sead +{ +namespace DateUtil +{ +bool isLeapYear(u32 year) +{ +#ifdef MATCHING_HACK_NX_CLANG + bool div100, div4; + return (div100 = year % 100 == 0, div4 = year % 4 == 0, !div100 & div4) | (year % 400 == 0); +#else + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +#endif +} + +CalendarTime::Week calcWeekDay(const CalendarTime::Year& year, const CalendarTime::Month& month, + const CalendarTime::Day& day) +{ + int y = year.getValue(); + int m = month.getValueOneOrigin(); + int d = day.getValue(); + if (m < 3) + { + y -= 1; + m += 12; + } + d += y + (y / 4) - (y / 100) + (y / 400); + d += (26 * m + 16) / 10; + return CalendarTime::Week(d % 7); +} + +void calcSecondToCalendarSpan(CalendarSpan* out_span, u64 sec) +{ + if (!out_span) + return; + out_span->setDays(sec / (3600 * 24)); + out_span->setHours((sec % (3600 * 24)) / 3600); + out_span->setMinutes((sec % 3600) / 60); + out_span->setSeconds(sec % 60); +} + +bool parseW3CDTFSubString(bool* ok, u32* value, SafeString* str, s32* str_length, + char* out_separator, s32 parse_length, const SafeString& separators, + bool allow_null_separator, u32 value_min, u32 value_max) +{ + if (*str_length < parse_length) + { + *ok = false; + return true; + } + + const char c = str->at(parse_length); + if (!separators.include(c) && (!allow_null_separator || c != SafeString::cNullChar)) + { + *ok = false; + return true; + } + + FixedSafeString<8> buffer; + buffer.copy(*str, parse_length); + + if (!StringUtil::tryParseU32(value, buffer, StringUtil::CardinalNumber::Base10) || + *value < value_min || *value > value_max) + { + *ok = false; + return true; + } + + if (c == SafeString::cNullChar) + { + *ok = true; + return true; + } + + *str = str->getPart(parse_length + 1); + *str_length -= parse_length + 1; + if (out_separator) + *out_separator = c; + return false; +} + +static bool parseW3CDTFStringImpl(u32* year, u32* month, u32* day, u32* hour, u32* minute, + u32* second, s32* tz_hour, s32* tz_minute, + const SafeString& string) +{ + s32 len = string.calcLength(); + bool ok = true; + SafeString substr = string; + char separator; + + if (parseW3CDTFSubString(&ok, year, &substr, &len, &separator, 4, "-", true, 0, 0xFFFFFFFF)) + return ok; + if (parseW3CDTFSubString(&ok, month, &substr, &len, &separator, 2, "-", true, 1, 12)) + return ok; + if (parseW3CDTFSubString(&ok, day, &substr, &len, &separator, 2, "T", true, 1, 31)) + return ok; + if (parseW3CDTFSubString(&ok, hour, &substr, &len, &separator, 2, ":", false, 0, 23)) + return ok; + if (parseW3CDTFSubString(&ok, minute, &substr, &len, &separator, 2, ":+-Z", true, 0, 59)) + return ok; + + if (separator == ':') + { + if (parseW3CDTFSubString(&ok, second, &substr, &len, &separator, 2, ".+-Z", true, 0, 59)) + return ok; + + if (separator == '.') + { + if (len == 0) + return false; + + auto it = substr.tokenBegin("+-Z"); + ++it; + auto end = substr.tokenEnd("+-Z"); + + // No timezone information + if (it == end) + { + *tz_hour = 0; + *tz_minute = 0; + return true; + } + + separator = substr.at(it.getIndex() - 1); + substr = substr.getPart(it.getIndex()); + len -= it.getIndex(); + } + } + + if (separator != '+' && separator != '-') + checkLength: + return len == 0; + + bool done; + u32 tz_hour_abs = 0; + done = parseW3CDTFSubString(&ok, &tz_hour_abs, &substr, &len, nullptr, 2, ":", false, 0, 11); + if (ok) + { + *tz_hour = tz_hour_abs; + if (separator == '-') + *tz_hour = -tz_hour_abs; + } + if (done) + return ok; + + u32 tz_minute_abs = 0; + done = parseW3CDTFSubString(&ok, &tz_minute_abs, &substr, &len, nullptr, 2, "", true, 0, 59); + if (ok) + { + *tz_minute = tz_minute_abs; + if (separator == '-') + *tz_minute = -tz_minute_abs; + + if (done) + { + len -= 2; + goto checkLength; + } + } + + return false; +} + +bool parseW3CDTFString(CalendarTime* out_time, CalendarSpan* time_zone, const SafeString& string) +{ + u32 year = 1970; + u32 month = 1; + u32 day = 1; + u32 hour = 0; + u32 minute = 0; + u32 second = 0; + + s32 tz_hour = 0; + s32 tz_minute = 0; + + const bool ret = parseW3CDTFStringImpl(&year, &month, &day, &hour, &minute, &second, &tz_hour, + &tz_minute, string); + + if (ret) + { + out_time->setDate({year, CalendarTime::Month::makeFromValueOneOrigin(month), day}); + out_time->setTime({hour, minute, second}); + + time_zone->setDays(0); + time_zone->setHours(tz_hour); + time_zone->setMinutes(tz_minute); + time_zone->setSeconds(0); + } + + return ret; +} +} // namespace DateUtil +} // namespace sead diff --git a/source/sead/time/seadTickSpan.cpp b/source/sead/time/seadTickSpan.cpp new file mode 100644 index 0000000..ae8fe6c --- /dev/null +++ b/source/sead/time/seadTickSpan.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +#include + +/* +namespace sead +{ +s64 TickSpan::cFrequency = nn::os::GetSystemTickFrequency(); + +s64 TickSpan::toNanoSeconds() const +{ + const s64 abs_span = std::abs(mSpan); + const s64 max = std::numeric_limits::max(); + + if(cFrequency == 0) { + cFrequency = nn::os::GetSystemTickFrequency(); + } + + // Try to get as much precision as possible without overflowing. + + if (abs_span < max / 1'000'000'000) + return 1'000'000'000 * mSpan / cFrequency; + + if (abs_span < max / 1'000'000) + return 1000 * (1'000'000 * mSpan / cFrequency); + + if (abs_span < max / 1000) + return 1'000'000 * (1000 * mSpan / cFrequency); + + return 1'000'000'000 * (mSpan / cFrequency); +} + +void TickSpan::setNanoSeconds(s64 ns) +{ + const s64 threshold = std::numeric_limits::max() / cFrequency; + const s64 abs_ns = std::abs(ns); + + if (abs_ns <= threshold) + mSpan = cFrequency * ns / 1'000'000'000; + else if (abs_ns <= 1000 * threshold) + mSpan = cFrequency * (ns / 1000) / 1'000'000; + else if (abs_ns <= 1'000'000 * threshold) + mSpan = cFrequency * (ns / 1'000'000) / 1000; + else + mSpan = cFrequency * (ns / 1'000'000'000); +} + +} // namespace sead +*/ \ No newline at end of file diff --git a/source/server/Client.cpp b/source/server/Client.cpp new file mode 100644 index 0000000..381fae6 --- /dev/null +++ b/source/server/Client.cpp @@ -0,0 +1,1438 @@ +#include "server/Client.hpp" +#include +#include "al/actor/ActorSceneInfo.h" +#include "al/layout/WindowConfirmWait.h" +#include "al/util/LiveActorUtil.h" +#include "algorithms/PlayerAnims.h" +#include "game/GameData/GameDataFunction.h" +#include "game/GameData/GameDataHolderAccessor.h" +#include "game/Info/QuestInfo.h" +#include "game/Player/PlayerActorHakoniwa.h" +#include "game/StageScene/StageScene.h" +#include "heap/seadHeapMgr.h" +#include "helpers.hpp" +#include "layouts/HideAndSeekIcon.h" +#include "logger.hpp" +#include "math/seadVector.h" +#include "nn/err.h" +#include "nn/result.h" +#include "packets/ChangeStagePacket.h" +#include "packets/InitPacket.h" +#include "packets/Packet.h" +#include "packets/PlayerConnect.h" +#include "packets/TagInf.h" +#include "sead/basis/seadRawPrint.h" +#include "sead/math/seadQuat.h" +#include "server/gamemode/GameModeBase.hpp" +#include "server/gamemode/GameModeFactory.hpp" +#include "server/HideAndSeekConfigMenu.hpp" +#include "server/HideAndSeekMode.hpp" + +Client* Client::sInstance; + +typedef void (Client::*ClientThreadFunc)(void); + +/** + * @brief Construct a new Client:: Client object + * + * @param bufferSize defines the maximum amount of puppets the client can handle + */ +Client::Client(int bufferSize) { + this->mReadThread = new al::AsyncFunctorThread("ClientReadThread", al::FunctorV0M(this, &Client::readFunc), 0, 0x10000, {0}); + // this->recvThread = new al::AsyncFunctorThread("ClientRecvThread", + // al::FunctorV0M(this, &Client::recvFunc), 0, 0x10000, + // {0}); + + mKeyboard = new Keyboard(nn::swkbd::GetRequiredStringBufferSize()); + + mSocket = new SocketClient("SocketClient"); + + maxPuppets = bufferSize - 1; + + mPuppetHolder = new PuppetHolder(maxPuppets); + + for (size_t i = 0; i < bufferSize + 1; i++) + { + mPuppetInfoArr[i] = new PuppetInfo(); + + sprintf(mPuppetInfoArr[i]->puppetName, "Puppet%zu", i); + } + + strcpy(mDebugPuppetInfo.puppetName, "PuppetDebug"); + + puppetPlayerID.fill({0, 0}); + + mConnectCount = 0; + + curCollectedShines.fill(-1); + + collectedShineCount = 0; + + mShineArray.allocBuffer(100, nullptr); // max of 100 shine actors in buffer + + nn::account::GetLastOpenedUser(&mUserID); + + mUserID.print(); + + nn::account::Nickname playerName; + nn::account::GetNickname(&playerName, mUserID); + + mUsername = playerName.name; + + Logger::log("Player Name: %s\n", playerName.name); + + #ifdef BUILDVER + Logger::log("%s Build Number: %s\n", playerName.name, TOSTRING(BUILDVER)); + #endif + + Logger::setLogName(playerName.name); // set Debug logger name to player name + + mServerMode = GameMode::HIDEANDSEEK; // temp for testing + + if(!sInstance) { + sInstance = this; + } +} + +/** + * @brief initializes client class using initInfo obtained from StageScene::init + * + * @param initInfo init info used to create layouts used by client + */ +void Client::init(al::LayoutInitInfo const &initInfo) { + + mConnectionWait = new al::WindowConfirmWait("ServerWaitConnect", "WindowConfirmWait", initInfo); + + mConnectionWait->setTxtMessage(u"Connecting to Server."); + + mConnectionWait->setTxtMessageConfirm(u"Failed to Connect!"); + + StartThreads(); + + // mConnectionWait->tryEndForce(); +} + +/** + * @brief creates and initializes the gamemode selected by either server or client settings + * + * @param initInfo init info used to initialize gamemode + */ +void Client::initMode(GameModeInitInfo const& initInfo) { + + if (!sInstance) { + Logger::log("Static Instance is null!\n"); + return; + } + + GameModeInitInfo newInfo = initInfo; + + newInfo.initServerInfo(sInstance->mServerMode, sInstance->mPuppetHolder); + + GameModeFactory modeFactory("GameModeFactory"); + + const char* modeName = GameModeFactory::getModeString(newInfo.mMode); + + if (modeName) { + auto creator = modeFactory.getCreator(modeName); + if (creator) { + sInstance->mCurMode = creator(modeName); + } + } + + if (sInstance->mCurMode) { + sInstance->mCurMode->init(newInfo); + } else { + Logger::log("Failed to Create Gamemode! Mode: \n", modeName ? modeName : "Unknown"); + } +} +/** + * @brief starts client read thread + * + * @return true if read thread was sucessfully started + * @return false if read thread was unable to start, or thread was already started. + */ +bool Client::StartThreads() { + if(this->mReadThread->isDone()) { // && this->recvThread->isDone()) { + this->mReadThread->start(); + //this->recvThread->start(); + Logger::log("Threads Sucessfully Started.\n"); + return true; + }else { + Logger::log("Thread(s) has/have already started! Or other unknown reason.\n"); + return false; + } +} +/** + * @brief restarts currently active connection to server + * + */ +void Client::stopConnection() { + + if (!sInstance) { + Logger::log("Static Instance is null!\n"); + return; + } + + sInstance->mSocket->closeSocket(); + + sInstance->puppetPlayerID.fill({0,0}); + + sInstance->mConnectCount = 0; + + sInstance->mSocket->init(sInstance->mServerIP.cstr(), sInstance->mServerPort); + + if(sInstance->mSocket->getLogState() == SOCKET_LOG_CONNECTED) { + + Logger::log("Connected!\n"); + + PlayerConnect initPacket; + initPacket.mUserID = sInstance->mUserID; + strcpy(initPacket.clientName, sInstance->mUsername.cstr()); + initPacket.conType = ConnectionTypes::RECONNECT; + sInstance->mSocket->SEND(&initPacket); + } +} +/** + * @brief starts a connection using client's TCP socket class, pulling up the software keyboard for user inputted IP if save file does not have one saved. + * + * @return true if successful connection to server + * @return false if connection was unable to establish + */ +bool Client::startConnection() { + if (mServerIP.isEmpty()) { + + mKeyboard->setHeaderText(u"Save File does not contain an IP!"); + mKeyboard->setSubText(u"Please set a Server IP Below."); + + mKeyboard->openKeyboard("0.0.0.0"); + + while (true) { + if (mKeyboard->isThreadDone()) { + mServerIP = mKeyboard->getResult(); + break; + } + nn::os::YieldThread(); // allow other threads to run + } + } + + bool result = mSocket->init(mServerIP.cstr(), mServerPort).isSuccess(); + + if (result) { + // wait for client init packet + while (true) { + + if (mSocket->RECV()) { + + Packet* curPacket = mSocket->mPacketQueue.popFront(); + + if (curPacket->mType == PacketType::CLIENTINIT) { + InitPacket* initPacket = (InitPacket*)curPacket; + + Logger::log("Server Max Player Size: %d\n", initPacket->maxPlayers); + + maxPuppets = initPacket->maxPlayers - 1; + + }else { + Logger::log("First Packet was not Init!\n"); + result = false; + } + + free(curPacket); + + } + + break; + } + } + + + return result; +} + +/** + * @brief Opens up OS's software keyboard in order to change the currently used server IP. + * + */ +void Client::openKeyboardIP() { + + if (!sInstance) { + Logger::log("Static Instance is null!\n"); + return; + } + + sInstance->mKeyboard->openKeyboard(sInstance->mServerIP.cstr()); // opens swkbd with the initial text set to the last saved IP + + while (true) { + if (sInstance->mKeyboard->isThreadDone()) { + sInstance->mServerIP = sInstance->mKeyboard->getResult(); + break; + } + nn::os::YieldThread(); // allow other threads to run + } +} + +/** + * @brief main thread function for read thread, responsible for receiving and processing packets from server + * + */ +void Client::readFunc() { + + if (isFirstConnect) { + nn::os::YieldThread(); // sleep the thread for the first thing we do so that game init can finish + nn::os::SleepThread(nn::TimeSpan::FromSeconds(2)); + } + + // we can use the start of readFunc to display an al::WindowConfirmWait while the server + // connects + + mConnectionWait->appear(); + + mConnectionWait->playLoop(); + + if (!startConnection()) { + + al::hidePane(mConnectionWait, "Page01"); // hide A button prompt since connection message automatically hides after 0.25 seconds + + al::startAction(mConnectionWait, "Confirm", "State"); + + nn::os::SleepThread(nn::TimeSpan::FromNanoSeconds(250000000)); // sleep active thread for 0.25 seconds + + mConnectionWait->tryEnd(); + + return; + } + + if (isFirstConnect) { + sead::Heap* seqHeap = sead::HeapMgr::instance()->findHeapByName("SequenceHeap", 0); + + if (seqHeap) { + Logger::log("Current Heap Name: %s\n", seqHeap->getName().cstr()); + } + + + } + + PlayerConnect initPacket; + initPacket.mUserID = mUserID; + strcpy(initPacket.clientName, mUsername.cstr()); + + if (isFirstConnect) { + initPacket.conType = ConnectionTypes::INIT; + } else { + initPacket.conType = ConnectionTypes::RECONNECT; + } + + mSocket->SEND(&initPacket); // send initial packet + + nn::os::SleepThread(nn::TimeSpan::FromNanoSeconds(500000000)); // sleep for 0.5 seconds to let connection layout fully show (probably should find a better way to do this) + + mConnectionWait->tryEnd(); + + isFirstConnect = false; + + while(true) { + + if (mSocket->getLogState() != SOCKET_LOG_CONNECTED) { + + if (!mConnectionWait->mIsAlive) { + + mConnectionWait->appear(); + + mConnectionWait->playLoop(); + } + + // if we ever disconnect, reset all our values until we reconnect + + puppetPlayerID.fill({0,0}); + mConnectCount = 0; + + mSocket->closeSocket(); + + mSocket->init(mServerIP.cstr(), mServerPort); + + if (mSocket->getLogState() == SOCKET_LOG_CONNECTED) { + + Logger::log("Connected!\n"); + + initPacket.conType = ConnectionTypes::RECONNECT; + mSocket->SEND(&initPacket); // re-send init packet as reconnect packet + mConnectionWait->tryEnd(); + continue; + + } + + nn::os::YieldThread(); // if we're currently waiting on the socket to be initialized, wait until it is + nn::os::SleepThread(nn::TimeSpan::FromSeconds(5)); + + // TODO: if a reconnect is sucessful, we should let the server know that this client has already connected, and that their player ID is already active + + } + + if(mSocket->RECV()) { // will block until a packet has been recieved, or socket disconnected + + while(!mSocket->mPacketQueue.isEmpty()) { + + Packet *curPacket = mSocket->mPacketQueue.popFront(); + + switch (curPacket->mType) + { + case PacketType::PLAYERINF: + if(mIsInGame) { + updatePlayerInfo((PlayerInf*)curPacket); + } + break; + case PacketType::GAMEINF: + updateGameInfo((GameInf*)curPacket); + break; + case PacketType::HACKCAPINF: + updateHackCapInfo((HackCapInf *)curPacket); + break; + case PacketType::CAPTUREINF: + updateCaptureInfo((CaptureInf*)curPacket); + break; + case PacketType::PLAYERCON: + updatePlayerConnect((PlayerConnect*)curPacket); + // send game info packet when client recieves connection + + if (lastGameInfPacket.mUserID != mUserID) { + // assume game info packet is empty from first connection + lastGameInfPacket.mUserID = mUserID; + // leave rest blank + } + + mSocket->SEND(&lastGameInfPacket); + break; + case PacketType::COSTUMEINF: + updateCostumeInfo((CostumeInf*)curPacket); + break; + case PacketType::SHINECOLL: + updateShineInfo((ShineCollect*)curPacket); + break; + case PacketType::PLAYERDC: + Logger::log("Recieved Player Disconnect!\n"); + curPacket->mUserID.print(); + disconnectPlayer((PlayerDC*)curPacket); + break; + case PacketType::TAGINF: + updateTagInfo((TagInf*)curPacket); + break; + case PacketType::CHANGESTAGE: + sendToStage((ChangeStagePacket*)curPacket); + break; + case PacketType::CLIENTINIT: { + InitPacket* initPacket = (InitPacket*)curPacket; + Logger::log("Server Max Player Size: %d\n", initPacket->maxPlayers); + maxPuppets = initPacket->maxPlayers - 1; + break; + } + default: + break; + } + + free(curPacket); + + } + + }else { // if false, socket has errored or disconnected, so close the socket and end this thread. + Logger::log("Client Socket Encountered an Error!\n"); + } + + nn::os::YieldThread(); // allow other threads to run + } +} +/** + * @brief unused thread function for receiving thread + * + */ +void Client::recvFunc() {} + +/** + * @brief sends player info packet to current server + * + * @param player pointer to current player class, used to get translation, animation, and capture data + */ +void Client::sendPlayerInfPacket(const PlayerActorHakoniwa *player) { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + if(!player) { + Logger::log("Error: Null Player Reference\n"); + return; + } + + PlayerInf packet = PlayerInf(); + packet.mUserID = sInstance->mUserID; + + packet.playerPos = al::getTrans(player); + + al::calcQuat(&packet.playerRot, player); // calculate rotation based off pose instead of using quat rotation + + for (size_t i = 0; i < 6; i++) + { + packet.animBlendWeights[i] = player->mPlayerAnimator->getBlendWeight(i); + } + + const char *hackName = player->mHackKeeper->getCurrentHackName(); + + if (hackName != nullptr) { + + sInstance->isClientCaptured = true; + + const char* actName = al::getActionName(player->mHackKeeper->currentHackActor); + + if (actName) { + packet.actName = PlayerAnims::FindType(actName); + packet.subActName = PlayerAnims::Type::Unknown; + //strcpy(packet.actName, actName); + } else { + packet.actName = PlayerAnims::Type::Unknown; + packet.subActName = PlayerAnims::Type::Unknown; + } + } else { + packet.actName = PlayerAnims::FindType(player->mPlayerAnimator->mAnimFrameCtrl->getActionName()); + packet.subActName = PlayerAnims::FindType(player->mPlayerAnimator->curSubAnim.cstr()); + + sInstance->isClientCaptured = false; + } + + if(sInstance->lastPlayerInfPacket != packet) { + sInstance->mSocket->SEND(&packet); + } + + sInstance->lastPlayerInfPacket = packet; + +} +/** + * @brief sends info related to player's cap actor to server + * + * @param hackCap pointer to cap actor, used to get translation, animation, and state info + */ +void Client::sendHackCapInfPacket(const HackCap* hackCap) { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + bool isFlying = hackCap->isFlying(); + + // if cap is in flying state, send packet as often as this function is called + if (isFlying) { + HackCapInf packet = HackCapInf(); + packet.mUserID = sInstance->mUserID; + packet.capPos = al::getTrans(hackCap); + + packet.isCapVisible = hackCap->isFlying(); + + packet.capQuat.x = hackCap->mJointKeeper->mJointRot.x; + packet.capQuat.y = hackCap->mJointKeeper->mJointRot.y; + packet.capQuat.z = hackCap->mJointKeeper->mJointRot.z; + packet.capQuat.w = hackCap->mJointKeeper->mSkew; + + strcpy(packet.capAnim, al::getActionName(hackCap)); + + sInstance->mSocket->SEND(&packet); + + sInstance->isSentHackInf = true; + + } else if (sInstance->isSentHackInf) { // if cap is not flying, check to see if previous function call sent a packet, and if so, send one final packet resetting cap data. + HackCapInf packet = HackCapInf(); + packet.mUserID = sInstance->mUserID; + packet.isCapVisible = false; + packet.capPos = sead::Vector3f::zero; + packet.capQuat = sead::Quatf::unit; + sInstance->mSocket->SEND(&packet); + sInstance->isSentHackInf = false; + } +} + +/** + * @brief + * + * @param player + * @param holder + */ +void Client::sendGameInfPacket(const PlayerActorHakoniwa* player, GameDataHolderAccessor holder) { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + GameInf packet = GameInf(); + packet.mUserID = sInstance->mUserID; + + if (player) { + packet.is2D = player->mDimKeeper->is2DModel; + } else { + packet.is2D = false; + } + + packet.scenarioNo = holder.mData->mGameDataFile->getScenarioNo(); + + strcpy(packet.stageName, GameDataFunction::getCurrentStageName(holder)); + + if(packet != sInstance->lastGameInfPacket) { + sInstance->mSocket->SEND(&packet); + } + + sInstance->lastGameInfPacket = packet; +} +/** + * @brief + * + * @param holder + */ +void Client::sendGameInfPacket(GameDataHolderAccessor holder) { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + GameInf packet = GameInf(); + packet.mUserID = sInstance->mUserID; + + packet.is2D = false; + + packet.scenarioNo = holder.mData->mGameDataFile->getScenarioNo(); + + strcpy(packet.stageName, GameDataFunction::getCurrentStageName(holder)); + + sInstance->mSocket->SEND(&packet); + + sInstance->lastGameInfPacket = packet; +} + +/** + * @brief + * + */ +void Client::sendTagInfPacket() { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + if (sInstance->mServerMode != GameMode::HIDEANDSEEK) { + Logger::log("State is not Hide and Seek!\n"); + return; + } + + HideAndSeekMode* hsMode = (HideAndSeekMode*)sInstance->mCurMode; + HideAndSeekInfo* curInfo = (HideAndSeekInfo*)sInstance->mModeInfo; + + TagInf packet = TagInf(); + + packet.mUserID = sInstance->mUserID; + + packet.isIt = hsMode->isPlayerIt(); + + packet.minutes = curInfo->mHidingTime.mMinutes; + packet.seconds = curInfo->mHidingTime.mSeconds; + packet.updateType = static_cast(TagUpdateType::STATE | TagUpdateType::TIME); + + sInstance->mSocket->SEND(&packet); +} + +/** + * @brief + * + * @param body + * @param cap + */ +void Client::sendCostumeInfPacket(const char* body, const char* cap) { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + CostumeInf packet = CostumeInf(body, cap); + packet.mUserID = sInstance->mUserID; + sInstance->mSocket->SEND(&packet); +} + +/** + * @brief + * + * @param player + */ +void Client::sendCaptureInfPacket(const PlayerActorHakoniwa* player) { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + if (sInstance->isClientCaptured && !sInstance->isSentCaptureInf) { + CaptureInf packet = CaptureInf(); + packet.mUserID = sInstance->mUserID; + strcpy(packet.hackName, tryConvertName(player->mHackKeeper->getCurrentHackName())); + sInstance->mSocket->SEND(&packet); + sInstance->isSentCaptureInf = true; + } else if (!sInstance->isClientCaptured && sInstance->isSentCaptureInf) { + CaptureInf packet = CaptureInf(); + packet.mUserID = sInstance->mUserID; + strcpy(packet.hackName, ""); + sInstance->mSocket->SEND(&packet); + sInstance->isSentCaptureInf = false; + } +} + +/** + * @brief + * + * @param shineID + */ +void Client::sendShineCollectPacket(int shineID) { + + if (!sInstance) { + Logger::log("Static Instance is Null!\n"); + return; + } + + if(sInstance->lastCollectedShine != shineID) { + ShineCollect packet = ShineCollect(); + packet.mUserID = sInstance->mUserID; + packet.shineId = shineID; + + sInstance->lastCollectedShine = shineID; + + sInstance->mSocket->SEND(&packet); + } +} + +/** + * @brief + * + * @param packet + */ +void Client::updatePlayerInfo(PlayerInf *packet) { + + int puppetIndex = findPuppetID(packet->mUserID); + + if(puppetIndex >= 0) { + + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + return; + } + + if(!curInfo->isConnected) { + curInfo->isConnected = true; + } + + curInfo->playerPos = packet->playerPos; + + // check if rotation is larger than zero and less than or equal to 1 + if(abs(packet->playerRot.x) > 0.f || abs(packet->playerRot.y) > 0.f || abs(packet->playerRot.z) > 0.f || abs(packet->playerRot.w) > 0.f) { + if(abs(packet->playerRot.x) <= 1.f || abs(packet->playerRot.y) <= 1.f || abs(packet->playerRot.z) <= 1.f || abs(packet->playerRot.w) <= 1.f) { + curInfo->playerRot = packet->playerRot; + } + } + + if (packet->actName != PlayerAnims::Type::Unknown) { + strcpy(curInfo->curAnimStr, PlayerAnims::FindStr(packet->actName)); + } else { + strcpy(curInfo->curAnimStr, "Wait"); + } + + if(packet->subActName != PlayerAnims::Type::Unknown) { + strcpy(curInfo->curSubAnimStr, PlayerAnims::FindStr(packet->subActName)); + } else { + strcpy(curInfo->curSubAnimStr, ""); + } + + curInfo->curAnim = packet->actName; + curInfo->curSubAnim = packet->subActName; + + for (size_t i = 0; i < 6; i++) + { + // weights can only be between 0 and 1 + if(packet->animBlendWeights[i] >= 0.f && packet->animBlendWeights[i] <= 1.f) { + curInfo->blendWeights[i] = packet->animBlendWeights[i]; + } + } + + //TEMP + + if(!curInfo->isCapThrow) { + curInfo->capPos = packet->playerPos; + } + + } +} + +/** + * @brief + * + * @param packet + */ +void Client::updateHackCapInfo(HackCapInf *packet) { + + int puppetIndex = findPuppetID(packet->mUserID); + + if(puppetIndex >= 0) { + + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (curInfo) { + curInfo->capPos = packet->capPos; + curInfo->capRot = packet->capQuat; + + curInfo->isCapThrow = packet->isCapVisible; + + if (curInfo->capAnim && packet->capAnim) { + strcpy(curInfo->capAnim, packet->capAnim); + } + } else { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + } + } +} + +/** + * @brief + * + * @param packet + */ +void Client::updateCaptureInfo(CaptureInf *packet) { + int puppetIndex = findPuppetID(packet->mUserID); + if (puppetIndex >= 0) { + + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + return; + } + + curInfo->isCaptured = strlen(packet->hackName) > 0; + + if (curInfo->isCaptured) { + strcpy(curInfo->curHack, packet->hackName); + } + } +} + +/** + * @brief + * + * @param packet + */ +void Client::updateCostumeInfo(CostumeInf *packet) { + if(packet->bodyModel && packet->capModel) { + int puppetIndex = findPuppetID(packet->mUserID); + if(puppetIndex >= 0) { + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + return; + } + + strcpy(curInfo->costumeBody, packet->bodyModel); + strcpy(curInfo->costumeHead, packet->capModel); + + } + } +} + +/** + * @brief + * + * @param packet + */ +void Client::updateShineInfo(ShineCollect* packet) { + if(collectedShineCount < curCollectedShines.size() - 1) { + curCollectedShines[collectedShineCount] = packet->shineId; + collectedShineCount++; + } +} + +/** + * @brief + * + * @param packet + */ +void Client::updatePlayerConnect(PlayerConnect *packet) { + int puppetIndex = findPuppetID(packet->mUserID); + if(puppetIndex >= 0) { + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + return; + } + curInfo->playerID = packet->mUserID; + curInfo->isConnected = true; + strcpy(curInfo->puppetName, packet->clientName); + } +} +/** + * @brief + * + * @param packet + */ +void Client::updateGameInfo(GameInf *packet) { + int puppetIndex = findPuppetID(packet->mUserID); + + if(puppetIndex >= 0) { + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + return; + } + + if(curInfo->isConnected) { + + curInfo->scenarioNo = packet->scenarioNo; + + if(strcmp(packet->stageName, "") != 0 && strlen(packet->stageName) > 3) { + strcpy(curInfo->stageName, packet->stageName); + } + + curInfo->is2D = packet->is2D; + } + } +} + +/** + * @brief + * + * @param packet + */ +void Client::updateTagInfo(TagInf *packet) { + + // if the packet is for our player, edit info for our player + if (packet->mUserID == mUserID && mCurMode->getMode() == GameMode::HIDEANDSEEK) { + + HideAndSeekMode* mMode = (HideAndSeekMode*)mCurMode; + HideAndSeekInfo* curInfo = (HideAndSeekInfo*)mModeInfo; + + if (packet->updateType & TagUpdateType::STATE) { + mMode->setPlayerTagState(packet->isIt); + } + + if (packet->updateType & TagUpdateType::TIME) { + curInfo->mHidingTime.mSeconds = packet->seconds; + curInfo->mHidingTime.mMinutes = packet->minutes; + } + + return; + + } + + int puppetIndex = findPuppetID(packet->mUserID); + + if(puppetIndex >= 0) { + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + return; + } + + curInfo->isIt = packet->isIt; + curInfo->seconds = packet->seconds; + curInfo->minutes = packet->minutes; + } +} + +/** + * @brief + * + * @param packet + */ +void Client::sendToStage(ChangeStagePacket* packet) { + if (mSceneInfo && mSceneInfo->mSceneObjHolder) { + + GameDataHolderAccessor accessor(mSceneInfo->mSceneObjHolder); + + Logger::log("Sending Player to %s at Entrance %s in Scenario %d\n", packet->changeStage, + packet->changeID, packet->scenarioNo); + + ChangeStageInfo info(accessor.mData, packet->changeID, packet->changeStage, false, packet->scenarioNo, static_cast(packet->subScenarioType)); + GameDataFunction::tryChangeNextStage(accessor, &info); + } +} + +/** + * @brief + * + * @param packet + */ +void Client::disconnectPlayer(PlayerDC *packet) { + int puppetIndex = findPuppetID(packet->mUserID); + + if(puppetIndex >= 0) { + PuppetInfo* curInfo = mPuppetInfoArr[puppetIndex]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", puppetIndex); + return; + } + + curInfo->isConnected = false; + + curInfo->scenarioNo = -1; + strcpy(curInfo->stageName, ""); + curInfo->isInSameStage = false; + + mConnectCount--; + + if (mConnectCount < 0) { + Logger::log("Connection Count went Negative!\n"); + mConnectCount = 0; + } + } +} + +/** + * @brief + * + * @param shineId + * @return true + * @return false + */ +bool Client::isShineCollected(int shineId) { + + for (size_t i = 0; i < curCollectedShines.size(); i++) + { + if(curCollectedShines[i] >= 0) { + if(curCollectedShines[i] == shineId) { + return true; + } + } + } + + return false; + +} + +/** + * @brief + * + * @param id + * @return int + */ +int Client::findPuppetID(const nn::account::Uid &id) { + for (size_t i = 0; i < this->puppetPlayerID.size(); i++) + { + if(this->puppetPlayerID[i].uid == id) { + return this->puppetPlayerID[i].puppetIndex; + } + } + + if(this->puppetPlayerID.size() > mConnectCount) { + int newIndex = mConnectCount; + this->puppetPlayerID[newIndex].puppetIndex = newIndex; + this->puppetPlayerID[newIndex].uid = id; + mConnectCount++; + return newIndex; + } + + return -1; +} + +/** + * @brief + * + * @param holder + */ +void Client::setStageInfo(GameDataHolderAccessor holder) { + if (sInstance) { + + sInstance->mStageName = GameDataFunction::getCurrentStageName(holder); + sInstance->mScenario = holder.mData->mGameDataFile->getScenarioNo(); //holder.mData->mGameDataFile->getMainScenarioNoCurrent(); + + Client::sInstance->mPuppetHolder->setStageInfo(sInstance->mStageName.cstr(), sInstance->mScenario); + } +} + +/** + * @brief + * + * @param puppet + * @return true + * @return false + */ +bool Client::tryAddPuppet(PuppetActor *puppet) { + if(Client::sInstance) { + return Client::sInstance->mPuppetHolder->tryRegisterPuppet(puppet); + }else { + return false; + } +} + +/** + * @brief + * + * @param puppet + * @return true + * @return false + */ +bool Client::tryAddDebugPuppet(PuppetActor *puppet) { + if(Client::sInstance) { + return Client::sInstance->mPuppetHolder->tryRegisterDebugPuppet(puppet); + }else { + return false; + } +} + +/** + * @brief + * + * @param idx + * @return PuppetActor* + */ +PuppetActor *Client::getPuppet(int idx) { + if(Client::sInstance) { + return Client::sInstance->mPuppetHolder->getPuppetActor(idx); + }else { + return nullptr; + } +} + +/** + * @brief + * + * @return PuppetInfo* + */ +PuppetInfo *Client::getLatestInfo() { + if(Client::sInstance) { + return Client::getPuppetInfo(Client::sInstance->mPuppetHolder->getSize() - 1); + }else { + return nullptr; + } +} + +/** + * @brief + * + * @param idx + * @return PuppetInfo* + */ +PuppetInfo *Client::getPuppetInfo(int idx) { + if(Client::sInstance) { + // unsafe get + PuppetInfo *curInfo = Client::sInstance->mPuppetInfoArr[idx]; + + if (!curInfo) { + Logger::log("Attempting to Access Puppet Out of Bounds! Value: %d\n", idx); + return nullptr; + } + + return curInfo; + }else { + return nullptr; + } +} + +/** + * @brief + * + */ +void Client::resetCollectedShines() { + collectedShineCount = 0; + curCollectedShines.fill(-1); +} + +/** + * @brief + * + * @param shineId + */ +void Client::removeShine(int shineId) { + for (size_t i = 0; i < curCollectedShines.size(); i++) + { + if(curCollectedShines[i] == shineId) { + curCollectedShines[i] = -1; + collectedShineCount--; + } + } +} + +/** + * @brief + * + * @return true + * @return false + */ +bool Client::isNeedUpdateShines() { + return sInstance ? sInstance->collectedShineCount > 0 : false; +} + +/** + * @brief + * + */ +void Client::updateShines() { + if (!sInstance) { + Logger::log("Client Null!\n"); + return; + } + + // skip shine sync if player is in cap kingdom scenario zero (very start of the game) + if (sInstance->mStageName == "CapWorldHomeStage" && (sInstance->mScenario == 0 || sInstance->mScenario == 1)) { + return; + } + + GameDataHolderAccessor accessor(sInstance->mCurStageScene); + + for (size_t i = 0; i < sInstance->getCollectedShinesCount(); i++) + { + int shineID = sInstance->getShineID(i); + + if (shineID >= 0) { + Shine* stageShine = findStageShine(shineID); + + if (!GameDataFunction::isGotShine(accessor, shineID)) { + if (stageShine) { + + if (al::isDead(stageShine)) { + stageShine->makeActorAlive(); + } + + stageShine->getDirect(); + stageShine->onSwitchGet(); + + accessor.mData->mGameDataFile->setGotShine(shineID); + + // TODO: add a system for displaying moon get layout when this runs + + } else { + accessor.mData->mGameDataFile->setGotShine(shineID); + } + } + } + } + + sInstance->resetCollectedShines(); + sInstance->mCurStageScene->stageSceneLayout->startShineCountAnim(false); +} + +/** + * @brief + * + */ +void Client::updateStates() { + if(sInstance) { + sInstance->mPuppetHolder->update(); + + if(sInstance->mCurMode && sInstance->mCurMode->isModeActive()) + sInstance->mCurMode->update(); + } +} + +/** + * @brief + * + * @param state + */ +void Client::setGameActive(bool state) { + if (sInstance) { + + sInstance->mIsInGame = state; + + // only modify mode state if mode should be active + if (sInstance->mIsModeActive) { + + bool modeActive = sInstance->mCurMode->isModeActive(); + + if (state && !modeActive) { + Logger::log("Resuming Current Mode.\n"); + sInstance->mCurMode->begin(); + } else if (!state && modeActive) { + Logger::log("Pausing Current Mode.\n"); + sInstance->mCurMode->end(); + } + } + } +} + +/** + * @brief + * + */ +void Client::clearArrays() { + if(sInstance) { + sInstance->mPuppetHolder->clearPuppets(); + sInstance->mShineArray.clear(); + + } +} + +/** + * @brief + * + * @return PuppetInfo* + */ +PuppetInfo *Client::getDebugPuppetInfo() { + if(Client::sInstance) { + return &Client::sInstance->mDebugPuppetInfo; + }else { + return nullptr; + } +} + +/** + * @brief + * + * @return PuppetActor* + */ +PuppetActor *Client::getDebugPuppet() { + if(Client::sInstance) { + return Client::sInstance->mPuppetHolder->getDebugPuppet(); + }else { + return nullptr; + } +} + +/** + * @brief + * + * @return const char* + */ +const char* Client::getCurrentIP() { + if (sInstance) { + return sInstance->mServerIP.cstr(); + } + return nullptr; +} + +/** + * @brief sets server IP to supplied string, used specifically for loading IP from the save file. + * + * @param ip + */ +void Client::setLastUsedIP(const char* ip) { + if (sInstance) { + sInstance->mServerIP = ip; + } +} + +/** + * @brief creates new scene info and copies supplied info to the new info, as well as stores a const ptr to the current stage scene. + * + * @param initInfo + * @param stageScene + */ +void Client::setSceneInfo(const al::ActorInitInfo& initInfo, const StageScene *stageScene) { + + if (!sInstance) { + Logger::log("Client Null!\n"); + return; + } + + sInstance->mSceneInfo = new al::ActorSceneInfo(); + + memcpy(sInstance->mSceneInfo, &initInfo.mActorSceneInfo, sizeof(al::ActorSceneInfo)); + + sInstance->mCurStageScene = stageScene; +} + +/** + * @brief stores shine pointer supplied into a ptr array if space is available, and shine is not collected. + * + * @param shine + * @return true if shine was able to be successfully stored + * @return false if shine is already collected, or ptr array is full + */ +bool Client::tryRegisterShine(Shine* shine) { + if (sInstance) { + if (!sInstance->mShineArray.isFull()) { + if (!shine->isGot()) { + sInstance->mShineArray.pushBack(shine); + return true; + } + } + } + return false; +} + +/** + * @brief finds the actor pointer stored in the shine ptr array based off shine ID + * + * @param shineID Unique ID used for shine actor + * @return Shine* if shine ptr array contains actor with supplied shine ID. + */ +Shine* Client::findStageShine(int shineID) { + if (sInstance) { + for (int i = 0; i < sInstance->mShineArray.size(); i++) { + Shine* curShine = sInstance->mShineArray[i]; + if (curShine) { + if (curShine->shineId == shineID) { + return curShine; + } + } + } + } + return nullptr; +} + +/** + * @brief gets the client's currently selected gamemode + * + * @return GameMode + */ +GameMode Client::getCurrentMode() { + return sInstance && sInstance->mCurMode ? sInstance->mCurMode->getMode() : GameMode::NONE; +} + +/** + * @brief enables or disables currently selected gamemode + * + */ +void Client::toggleCurrentMode() { + if (!sInstance || !sInstance->mCurMode) { + return; + } + + GameModeBase* curMode = sInstance->mCurMode; + + if (curMode->isModeActive()) { + Logger::log("Ending Gamemode: %s\n", curMode->getName()); + curMode->end(); + sInstance->mIsModeActive = false; + } else { + Logger::log("Starting Gamemode: %s\n", curMode->getName()); + curMode->begin(); + sInstance->mIsModeActive = true; + } +} + +/** + * @brief re-enables current gamemode if previously activated (if a stage reload occurs while the gamemode was previously active, this will turn it back on after initialized) + * + */ +void Client::tryRestartCurrentMode() { + if (!sInstance || !sInstance->mCurMode) { + return; + } + // restart mode if previous scene had one active + if (sInstance->mIsModeActive) { + sInstance->mCurMode->begin(); + } +} + +GameModeConfigMenu* Client::tryCreateModeMenu() { + if (!sInstance) + return nullptr; + + switch (sInstance->mServerMode) { + case HIDEANDSEEK: { + return new HideAndSeekConfigMenu(); + } + default: + return nullptr; + } +} diff --git a/source/server/DeltaTime.cpp b/source/server/DeltaTime.cpp new file mode 100644 index 0000000..2659b73 --- /dev/null +++ b/source/server/DeltaTime.cpp @@ -0,0 +1,11 @@ +#include "server/DeltaTime.hpp" + +sead::TickTime Time::prevTime; +sead::TickSpan Time::deltaSpan; +float Time::deltaTime; + +void Time::calcTime() { + Time::deltaSpan = Time::prevTime.diffToNow(); + Time::prevTime.setNow(); + Time::deltaTime = fabsf((double)Time::deltaSpan.toNanoSeconds() / 1000000000.0); +} \ No newline at end of file diff --git a/source/server/GameModeTimer.cpp b/source/server/GameModeTimer.cpp new file mode 100644 index 0000000..95e364a --- /dev/null +++ b/source/server/GameModeTimer.cpp @@ -0,0 +1,119 @@ +#include "server/gamemode/GameModeTimer.hpp" +#include +#include "al/util/ControllerUtil.h" +#include "server/DeltaTime.hpp" + +GameModeTimer::GameModeTimer(bool isCountUp, float milli, int seconds, int minutes, int hours) { + mIsCountUp = isCountUp; + setTime(milli, seconds, minutes, hours); +} + +GameModeTimer::GameModeTimer(float milli, int seconds, int minutes, int hours) { + mIsCountUp = false; + setTime(milli, seconds, minutes, hours); +} + +GameModeTimer::GameModeTimer(GameTime const& time) { + mIsCountUp = true; + setTime(time); +} + +GameModeTimer::GameModeTimer() { + mIsCountUp = true; + resetTime(); +} + +void GameModeTimer::setTime(float milli, int seconds, int minutes, int hours) { + if(milli >= 0) mTime.mMilliseconds = milli; + if(seconds >= 0) mTime.mSeconds = seconds; + if(minutes >= 0) mTime.mMinutes = minutes; + if(hours >= 0) mTime.mHours = hours; +} + +void GameModeTimer::setTime(GameTime const& time) { + mTime = time; +} + +void GameModeTimer::updateTimer() { + + if (mIsUseControl) { + timerControl(); + } + + if (mIsEnabled) { + if (mIsCountUp) { + mTime.mMilliseconds += Time::deltaTime; + + if(mTime.mMilliseconds >= 1) { + mTime.mMilliseconds--; + mTime.mSeconds++; + if(mTime.mSeconds >= 60) { + mTime.mSeconds = 0; + mTime.mMinutes++; + if (mTime.mMinutes >= 60) { + mTime.mMinutes = 0; + mTime.mHours++; + } + } + } + } else { + mTime.mMilliseconds += Time::deltaTime; + if (mTime.mMilliseconds >= 1) { + mTime.mMilliseconds--; + mTime.mSeconds--; + if(mTime.mSeconds < 0) { + mTime.mSeconds = 59; + mTime.mMinutes--; + if (mTime.mMinutes < 0) { + if (mTime.mHours > 0) { + mTime.mMinutes = 59; + mTime.mHours--; + } else { + mIsEnabled = false; + } + } + } + } + } + } +} + +void GameModeTimer::timerControl() { + + if(al::isPadHoldRight(-1)) { + mTime.mMilliseconds = 0; + mTime.mSeconds++; + if(mTime.mSeconds >= 60) { + mTime.mSeconds = 0; + mTime.mMinutes++; + if (mTime.mMinutes >= 60) { + mTime.mMinutes = 0; + mTime.mHours++; + } + } + } + + if(al::isPadTriggerLeft(-1)) { + mTime.mMilliseconds = 0; + mTime.mSeconds--; + if(mTime.mSeconds < 0) { + mTime.mSeconds = 59; + mTime.mMinutes--; + if (mTime.mMinutes < 0) { + if (mTime.mHours > 0) { + mTime.mMinutes = 59; + mTime.mHours--; + } else { + mTime.mMinutes = 0; + } + } + } + } + + if(al::isPadHoldL(-1) && al::isPadTriggerDown(-1)) { + mTime.mMilliseconds = 0; + mTime.mSeconds = 0; + mTime.mMinutes = 0; + mTime.mHours = 0; + } +} \ No newline at end of file diff --git a/source/server/HackModelHolder.cpp b/source/server/HackModelHolder.cpp new file mode 100644 index 0000000..7d2ed1b --- /dev/null +++ b/source/server/HackModelHolder.cpp @@ -0,0 +1,79 @@ +#include "puppets/HackModelHolder.hpp" + +PuppetHackActor *HackModelHolder::getCapture(const char *hackName) { + for (size_t i = 0; i < mCaptureCount; i++) + { + if(al::isEqualString(mOnlineCaptures[i].className, hackName)) { + return mOnlineCaptures[i].actor; + } + } + return nullptr; +}; + +PuppetHackActor *HackModelHolder::getCapture(int index) { + return index <= mCaptureCount ? mOnlineCaptures[index].actor : nullptr; +}; + +const char *HackModelHolder::getCaptureClass(int index) { + return index <= mCaptureCount ? mOnlineCaptures[index].className : "Unknown"; +} + +bool HackModelHolder::addCapture(PuppetHackActor *capture, const char *hackName) { + if(mCaptureCount < ACNT(mOnlineCaptures)) { + mOnlineCaptures[mCaptureCount].actor = capture; + strcpy(mOnlineCaptures[mCaptureCount].className, hackName); + mCaptureCount++; + return true; + } + return false; +}; + +bool HackModelHolder::removeCapture(const char *hackName) { + for (size_t i = 0; i < mCaptureCount; i++) + { + if(al::isEqualString(mOnlineCaptures[i].className, hackName)) { + mOnlineCaptures[i].actor = nullptr; + strcpy(mOnlineCaptures[i].className, ""); + return true; + } + } + return false; +}; + +void HackModelHolder::resetList() { + + for (size_t i = 0; i < mCaptureCount; i++) + { + mOnlineCaptures[i].actor = nullptr; + strcpy(mOnlineCaptures[i].className, ""); + } + + mCaptureCount = 0; +}; + +bool HackModelHolder::setCurrent(const char* hackName) { + for (size_t i = 0; i < mCaptureCount; i++) + { + if(al::isEqualString(mOnlineCaptures[i].className, hackName)) { + mCurCapture = &mOnlineCaptures[i]; + return true; + } + } + mCurCapture = nullptr; // if we cant find the name of the current capture, assume its not in the list and set the current reference to null. + return false; +} + +PuppetHackActor* HackModelHolder::getCurrentActor() { + if (mCurCapture) { + return mCurCapture->actor; + } else { + return nullptr; + } +} +const char* HackModelHolder::getCurrentActorName() { + if (mCurCapture) { + return mCurCapture->className; + } else { + return nullptr; + } +} \ No newline at end of file diff --git a/source/server/HideAndSeekConfigMenu.cpp b/source/server/HideAndSeekConfigMenu.cpp new file mode 100644 index 0000000..3e5c203 --- /dev/null +++ b/source/server/HideAndSeekConfigMenu.cpp @@ -0,0 +1,52 @@ +#include "server/HideAndSeekConfigMenu.hpp" +#include +#include "logger.hpp" +#include "server/HideAndSeekMode.hpp" +#include "server/Client.hpp" + +HideAndSeekConfigMenu::HideAndSeekConfigMenu() : GameModeConfigMenu() {} + +void HideAndSeekConfigMenu::initMenu(const al::LayoutInitInfo &initInfo) { + +} + +const sead::WFixedSafeString<0x200> *HideAndSeekConfigMenu::getStringData() { + sead::SafeArray, mItemCount>* gamemodeConfigOptions = + new sead::SafeArray, mItemCount>(); + + gamemodeConfigOptions->mBuffer[0].copy(u"Toggle H&S Gravity On"); + gamemodeConfigOptions->mBuffer[1].copy(u"Toggle H&S Gravity Off"); + + return gamemodeConfigOptions->mBuffer; +} + +bool HideAndSeekConfigMenu::updateMenu(int selectIndex) { + + HideAndSeekInfo *curMode = (HideAndSeekInfo*)Client::getModeInfo(); + + Logger::log("Setting Gravity Mode.\n"); + + if (!curMode) { + Logger::log("Unable to Load Mode info!\n"); + return true; + } + + switch (selectIndex) { + case 0: { + if (Client::isSelectedMode(GameMode::HIDEANDSEEK)) { + curMode->mIsUseGravity = true; + } + return true; + } + case 1: { + if (Client::isSelectedMode(GameMode::HIDEANDSEEK)) { + curMode->mIsUseGravity = false; + } + return true; + } + default: + Logger::log("Failed to interpret Index!\n"); + return false; + } + +} \ No newline at end of file diff --git a/source/server/HideAndSeekMode.cpp b/source/server/HideAndSeekMode.cpp new file mode 100644 index 0000000..032965c --- /dev/null +++ b/source/server/HideAndSeekMode.cpp @@ -0,0 +1,208 @@ +#include "server/HideAndSeekMode.hpp" +#include +#include "al/async/FunctorV0M.hpp" +#include "al/util.hpp" +#include "al/util/ControllerUtil.h" +#include "game/GameData/GameDataHolderAccessor.h" +#include "game/Layouts/CoinCounter.h" +#include "game/Layouts/MapMini.h" +#include "game/Player/PlayerActorHakoniwa.h" +#include "layouts/HideAndSeekIcon.h" +#include "logger.hpp" +#include "rs/util.hpp" +#include "server/gamemode/GameModeBase.hpp" +#include "server/Client.hpp" +#include "server/gamemode/GameModeTimer.hpp" + +#include "basis/seadNew.h" +#include "server/HideAndSeekConfigMenu.hpp" + +HideAndSeekMode::HideAndSeekMode(const char* name) : GameModeBase(name) {} + +void HideAndSeekMode::init(const GameModeInitInfo& info) { + mSceneObjHolder = info.mSceneObjHolder; + mMode = info.mMode; + mCurScene = (StageScene*)info.mScene; + mPuppetHolder = info.mPuppetHolder; + + GameModeInfoBase* curGameInfo = Client::getModeInfo(); + + if (curGameInfo && curGameInfo->mMode == mMode) { + mInfo = (HideAndSeekInfo*)curGameInfo; + mModeTimer = new GameModeTimer(mInfo->mHidingTime); + } else { + sead::system::DeleteImpl( + curGameInfo); // attempt to destory previous info before creating new one + + mInfo = createModeInfo(); + Client::setModeInfo(mInfo); + mModeTimer = new GameModeTimer(); + } + + mModeLayout = new HideAndSeekIcon("HideAndSeekIcon", *info.mLayoutInitInfo); + + mModeLayout->showSeeking(); + + mModeTimer->disableTimer(); + +} + +void HideAndSeekMode::begin() { + + mModeLayout->appear(); + + mIsFirstFrame = true; + + if (!mInfo->mIsPlayerIt) { + mModeTimer->enableTimer(); + mModeLayout->showHiding(); + } else { + mModeTimer->disableTimer(); + mModeLayout->showSeeking(); + } + + CoinCounter *coinCollect = mCurScene->stageSceneLayout->mCoinCollectLyt; + CoinCounter* coinCounter = mCurScene->stageSceneLayout->mCoinCountLyt; + MapMini* compass = mCurScene->stageSceneLayout->mMapMiniLyt; + al::SimpleLayoutAppearWaitEnd* playGuideLyt = mCurScene->stageSceneLayout->mPlayGuideMenuLyt; + + if(coinCounter->mIsAlive) + coinCounter->tryEnd(); + if(coinCollect->mIsAlive) + coinCollect->tryEnd(); + if (compass->mIsAlive) + compass->end(); + if (playGuideLyt->mIsAlive) + playGuideLyt->end(); + + GameModeBase::begin(); +} + +void HideAndSeekMode::end() { + + mModeLayout->tryEnd(); + + mModeTimer->disableTimer(); + + CoinCounter *coinCollect = mCurScene->stageSceneLayout->mCoinCollectLyt; + CoinCounter* coinCounter = mCurScene->stageSceneLayout->mCoinCountLyt; + MapMini* compass = mCurScene->stageSceneLayout->mMapMiniLyt; + al::SimpleLayoutAppearWaitEnd* playGuideLyt = mCurScene->stageSceneLayout->mPlayGuideMenuLyt; + + if(!coinCounter->mIsAlive) + coinCounter->tryStart(); + if(!coinCollect->mIsAlive) + coinCollect->tryStart(); + if (!compass->mIsAlive) + compass->appearSlideIn(); + if (!playGuideLyt->mIsAlive) + playGuideLyt->appear(); + + GameModeBase::end(); +} + +void HideAndSeekMode::update() { + + PlayerActorHakoniwa* mainPlayer = rs::getPlayerActor(mCurScene); + + if (mIsFirstFrame) { + + if (mInfo->mIsUseGravityCam && mTicket) { + al::startCamera(mCurScene, mTicket, -1); + } + + mIsFirstFrame = false; + } + + if (!mInfo->mIsPlayerIt) { + if (mInvulnTime >= 5) { + + if (mainPlayer) { + for (size_t i = 0; i < mPuppetHolder->getSize(); i++) + { + PuppetInfo *curInfo = Client::getPuppetInfo(i); + + if(curInfo->isConnected && curInfo->isInSameStage && curInfo->isIt) { + + float pupDist = al::calcDistance(mainPlayer, curInfo->playerPos); // TODO: remove distance calculations and use hit sensors to determine this + + if(pupDist < 200.f && mainPlayer->mDimKeeper->is2DModel == curInfo->is2D) { + if(!PlayerFunction::isPlayerDeadStatus(mainPlayer)) { + + GameDataFunction::killPlayer(GameDataHolderAccessor(this)); + mainPlayer->startDemoPuppetable(); + al::setVelocityZero(mainPlayer); + rs::faceToCamera(mainPlayer); + mainPlayer->mPlayerAnimator->endSubAnim(); + mainPlayer->mPlayerAnimator->startAnimDead(); + + mInfo->mIsPlayerIt = true; + mModeTimer->disableTimer(); + mModeLayout->showSeeking(); + + Client::sendTagInfPacket(); + } + } else if (PlayerFunction::isPlayerDeadStatus(mainPlayer)) { + + mInfo->mIsPlayerIt = true; + mModeTimer->disableTimer(); + mModeLayout->showSeeking(); + + Client::sendTagInfPacket(); + + } + } + } + } + + }else { + mInvulnTime += Time::deltaTime; + } + + mModeTimer->updateTimer(); + } + + if (mInfo->mIsUseGravity) { + sead::Vector3f gravity; + if (rs::calcOnGroundNormalOrGravityDir(&gravity, mainPlayer, mainPlayer->mPlayerCollider)) { + gravity = -gravity; + al::normalize(&gravity); + al::setGravity(mainPlayer, gravity); + al::setGravity(mainPlayer->mHackCap, gravity); + } + + if (al::isPadHoldL(-1)) { + if (al::isPadTriggerRight(-1)) { + if (al::isActiveCamera(mTicket)) { + al::endCamera(mCurScene, mTicket, -1, false); + mInfo->mIsUseGravityCam = false; + } else { + al::startCamera(mCurScene, mTicket, -1); + mInfo->mIsUseGravityCam = true; + } + } + } else if (al::isPadTriggerZL(-1)) { + if (al::isPadTriggerLeft(-1)) { + killMainPlayer(mainPlayer); + } + } + } + + if (al::isPadTriggerUp(-1) && !al::isPadHoldZL(-1)) + { + mInfo->mIsPlayerIt = !mInfo->mIsPlayerIt; + + mModeTimer->toggleTimer(); + + if(!mInfo->mIsPlayerIt) { + mInvulnTime = 0; + mModeLayout->showHiding(); + } else { + mModeLayout->showSeeking(); + } + + Client::sendTagInfPacket(); + } + + mInfo->mHidingTime = mModeTimer->getTime(); +} \ No newline at end of file diff --git a/source/server/SocketBase.cpp b/source/server/SocketBase.cpp new file mode 100644 index 0000000..4dbe7ac --- /dev/null +++ b/source/server/SocketBase.cpp @@ -0,0 +1,82 @@ +#include "SocketBase.hpp" +#include +#include "nn/result.h" +#include "types.h" + +SocketBase::SocketBase(const char *name) +{ + strcpy(this->sockName, name); + + this->sock_flags = 0; +} + +const char *SocketBase::getStateChar() { + + switch (this->socket_log_state) + { + case SOCKET_LOG_CONNECTED: + return "Socket Connected"; + case SOCKET_LOG_UNAVAILABLE: + return "Socket Unavailable"; + case SOCKET_LOG_UNINITIALIZED: + return "Socket Unitialized"; + case SOCKET_LOG_DISCONNECTED: + return "Socket Disconnected"; + default: + return "Unknown State"; + } +} + +u8 SocketBase::getLogState() { + return this->socket_log_state; +} + +void SocketBase::set_sock_flags(int flags) { + this->sock_flags = flags; +} + +s32 SocketBase::socket_log(const char* str) +{ + if (this->socket_log_state != SOCKET_LOG_CONNECTED) + return -1; + + return nn::socket::Send(this->socket_log_socket, str, strlen(str), 0); +} + +s32 SocketBase::socket_read_char(char *out) { + + if (this->socket_log_state != SOCKET_LOG_CONNECTED) + return -2; + + char buf[0x1000]; + + int valread = nn::socket::Recv(this->socket_log_socket, buf, sizeof(buf), this->sock_flags); + + if(valread > 0) { + buf[valread] = '\0'; + } + strncat(out, buf, valread); + return valread; +} + +s32 SocketBase::getSocket() { + if(this->socket_log_state == SOCKET_LOG_CONNECTED) { + return this->socket_log_socket; + }else { + return -1; + } +} + +bool SocketBase::closeSocket() { + + nn::Result result = nn::socket::Close(this->socket_log_socket); + + if (result.isSuccess()) { + this->socket_log_state = SOCKET_LOG_DISCONNECTED; + } + + return result.isSuccess(); +} + + + diff --git a/source/server/SocketClient.cpp b/source/server/SocketClient.cpp new file mode 100644 index 0000000..7943ec1 --- /dev/null +++ b/source/server/SocketClient.cpp @@ -0,0 +1,177 @@ +#include "server/SocketClient.hpp" +#include +#include + +#include "logger.hpp" +#include "nn/result.h" +#include "nn/socket.h" +#include "packets/Packet.h" +#include "types.h" + +nn::Result SocketClient::init(const char* ip, u16 port) { + + sock_ip = ip; + + this->port = port; + + in_addr hostAddress = { 0 }; + sockaddr serverAddress = { 0 }; + + if (socket_log_state != SOCKET_LOG_UNINITIALIZED && socket_log_state != SOCKET_LOG_DISCONNECTED) + return -1; + + nn::nifm::Initialize(); + nn::nifm::SubmitNetworkRequest(); + + while (nn::nifm::IsNetworkRequestOnHold()) { } + + // emulators (ryujinx) make this return false always, so skip it during init + #ifndef EMU + if (!nn::nifm::IsNetworkAvailable()) { + Logger::log("Network Unavailable.\n"); + this->socket_log_state = SOCKET_LOG_UNAVAILABLE; + return -1; + } + #endif + + if ((this->socket_log_socket = nn::socket::Socket(2, 1, 0)) < 0) { + + Logger::log("Socket Unavailable.\n"); + + this->socket_log_state = SOCKET_LOG_UNAVAILABLE; + return -1; + } + + + nn::socket::InetAton(this->sock_ip, &hostAddress); + + serverAddress.address = hostAddress; + serverAddress.port = nn::socket::InetHtons(this->port); + serverAddress.family = 2; + + bool sockOptValue = true; + + nn::socket::SetSockOpt(this->socket_log_socket, 0, TCP_NODELAY, &sockOptValue, sizeof(bool)); + + nn::Result result; + + if((result = nn::socket::Connect(this->socket_log_socket, &serverAddress, sizeof(serverAddress))).isFailure()) { + this->socket_log_state = SOCKET_LOG_UNAVAILABLE; + return result; + } + + this->socket_log_state = SOCKET_LOG_CONNECTED; + + return result; + +} + +bool SocketClient::SEND(Packet *packet) { + + if (this->socket_log_state != SOCKET_LOG_CONNECTED) + return false; + + char* buffer = reinterpret_cast(packet); + + int valread = 0; + + //Logger::log("Sending Packet Size: %d Sending Type: %s\n", packet->mPacketSize, packetNames[packet->mType]); + + if ((valread = nn::socket::Send(this->socket_log_socket, buffer, packet->mPacketSize + sizeof(Packet), this->sock_flags) > 0)) { + return true; + } else { + Logger::log("Failed to Fully Send Packet! Result: %d Type: %s Packet Size: %d\n", valread, packetNames[packet->mType], packet->mPacketSize); + this->closeSocket(); + return false; + } + return true; +} + +bool SocketClient::RECV() { + + if (this->socket_log_state != SOCKET_LOG_CONNECTED) { + Logger::log("Unable To Recieve! Socket Not Connected.\n"); + return false; + } + + int headerSize = sizeof(Packet); + char headerBuf[sizeof(Packet)] = {}; + int valread = 0; + + // read only the size of a header + while(valread < headerSize) { + int result = nn::socket::Recv(this->socket_log_socket, headerBuf + valread, headerSize - valread, this->sock_flags); + if(result > 0) { + valread += result; + } else { + Logger::log("Header Read Failed! Value: %d Total Read: %d\n", result, valread); + this->closeSocket(); + return false; + } + } + + if(valread > 0) { + Packet* header = reinterpret_cast(headerBuf); + + int fullSize = header->mPacketSize + sizeof(Packet); + + if (header->mType != PacketType::UNKNOWN && fullSize <= MAXPACKSIZE && fullSize > 0) { + + char* packetBuf = (char*)malloc(fullSize); + + if (packetBuf) { + + memcpy(packetBuf, headerBuf, sizeof(Packet)); + + while (valread < fullSize) { + + int result = nn::socket::Recv(this->socket_log_socket, packetBuf + valread, fullSize - valread, this->sock_flags); + + if (result > 0) { + valread += result; + }else { + free(packetBuf); + Logger::log("Packet Read Failed! Value: %d\nPacket Size: %d\nPacket Type: %s\n", result, header->mPacketSize, packetNames[header->mType]); + this->closeSocket(); + return false; + } + } + + Packet *packet = reinterpret_cast(packetBuf); + + if(mPacketQueue.size() < maxBufSize - 1) { + mPacketQueue.pushBack(packet); + } else { + free(packetBuf); + } + } else { + // Logger::log("Heap Allocation Failed! Returned nullptr\n"); + } + } else { + // Logger::log("Recieved Unknown Packet Type! Size: %d\n", header->mPacketSize); + } + + return true; + } else { // if we error'd, close the socket + Logger::log("valread was zero! Disconnecting.\n"); + this->closeSocket(); + return false; + } +} + +// prints packet to debug logger +void SocketClient::printPacket(Packet *packet) { + packet->mUserID.print(); + Logger::log("Type: %s\n", packetNames[packet->mType]); + + switch (packet->mType) + { + case PacketType::PLAYERINF: + Logger::log("Pos X: %f Pos Y: %f Pos Z: %f\n", ((PlayerInf*)packet)->playerPos.x, ((PlayerInf*)packet)->playerPos.y, ((PlayerInf*)packet)->playerPos.z); + Logger::log("Rot X: %f Rot Y: %f Rot Z: %f\nRot W: %f\n", ((PlayerInf*)packet)->playerRot.x, ((PlayerInf*)packet)->playerRot.y, ((PlayerInf*)packet)->playerRot.z, ((PlayerInf*)packet)->playerRot.w); + break; + default: + break; + } +} + diff --git a/source/server/captureSync.cpp b/source/server/captureSync.cpp new file mode 100644 index 0000000..1a0d0b3 --- /dev/null +++ b/source/server/captureSync.cpp @@ -0,0 +1,101 @@ +#include "server/Client.hpp" +#include "actors/PuppetActor.h" +#include "al/util.hpp" +#include "algorithms/CaptureTypes.h" +#include "logger.hpp" +#include "main.hpp" +#include "actors/PuppetHackActor.h" +#include "al/actor/alPlacementFunction.h" + + +// Helper Methods + +bool isInCaptureList(const char* capture) { + return CaptureTypes::FindType(capture) != CaptureTypes::Type::Unknown; +} + +PuppetHackActor *createPuppetHackActorFromFactory(al::ActorInitInfo const &rootInitInfo, const al::PlacementInfo *rootPlacementInfo, PuppetInfo *curInfo, const char *hackType) { + al::ActorInitInfo actorInitInfo = al::ActorInitInfo(); + actorInitInfo.initViewIdSelf(rootPlacementInfo, rootInitInfo); + + int serverMaxPlayers = Client::getMaxPlayerCount(); // TODO: Find a way around needing to do this, such as creating a single hack actor per puppet that can dynamically switch models + + // only use this if player count is 8 + if (serverMaxPlayers == 8) { + const char *stageName = ""; + if (actorInitInfo.mPlacementInfo._0.tryGetStringByKey(&stageName, "PlacementFileName")) { + if (al::isEqualString(stageName, "ForestWorldHomeStage")) { + return nullptr; + } + } + } + + if (serverMaxPlayers > 8) { // disable capture sync if dealing with more than 8 players + return nullptr; + } + + al::createActor createActor = actorInitInfo.mActorFactory->getCreator("PuppetHackActor"); + + if(createActor) { + PuppetHackActor *newActor = (PuppetHackActor*)createActor("PuppetHackActor"); + + newActor->initOnline(curInfo, hackType); // set puppet info first before calling init so we can get costume info from the info + + newActor->init(actorInitInfo); + + return newActor; + }else { + return nullptr; + } +} + +// Hooks + +al::LiveActor *initObjHook(al::ActorInitInfo const &initInfo, al::PlacementInfo const *placement) { + + al::ActorInitInfo newInfo = al::ActorInitInfo(); + newInfo.initViewIdSelf(placement, initInfo); + + const char *className = ""; + al::tryGetClassName(&className, newInfo); + + if(isInCaptureList(className)) + { + + int serverMaxPlayers = Client::getMaxPlayerCount(); + + for (size_t i = 0; i < serverMaxPlayers - 1; i++) + { + PuppetActor* curPuppet = Client::getPuppet(i); + if (curPuppet) { + + const char* hackName = tryConvertName(className); + + // make sure we only make as many unique puppet hack actors as needed + if(!curPuppet->isInCaptureList(hackName)) { + + PuppetHackActor* dupliActor = createPuppetHackActorFromFactory( + initInfo, placement, curPuppet->getInfo(), hackName); + if (dupliActor) { + curPuppet->addCapture(dupliActor, hackName); + } + } + } + } + + PuppetActor* debugPuppet = Client::getDebugPuppet(); + + if (debugPuppet) { + const char* hackName = tryConvertName(className); + if(!debugPuppet->isInCaptureList(hackName)) { + + PuppetHackActor *dupliActor = createPuppetHackActorFromFactory(initInfo, placement, debugPuppet->getInfo(), hackName); + if (dupliActor) { + debugPuppet->addCapture(dupliActor, hackName); + } + } + } + } + + return al::createPlacementActorFromFactory(initInfo, placement); +} diff --git a/source/server/logger.cpp b/source/server/logger.cpp new file mode 100644 index 0000000..d2e804a --- /dev/null +++ b/source/server/logger.cpp @@ -0,0 +1,111 @@ +#include "logger.hpp" +#include "helpers.hpp" +#include "nn/result.h" + +Logger* Logger::sInstance = nullptr; + +void Logger::createInstance() { + #ifdef SERVERIP + sInstance = new Logger(TOSTRING(SERVERIP), 3080, "MainLogger"); + #else + sInstance = new Logger(0, 3080, "MainLogger"); + #endif +} + +nn::Result Logger::init(const char* ip, u16 port) { + + sock_ip = ip; + + this->port = port; + + in_addr hostAddress = { 0 }; + sockaddr serverAddress = { 0 }; + + if (this->socket_log_state != SOCKET_LOG_UNINITIALIZED) + return -1; + + nn::nifm::Initialize(); + nn::nifm::SubmitNetworkRequest(); + + while (nn::nifm::IsNetworkRequestOnHold()) { } + + // emulators make this return false always, so skip it during init + #ifndef EMU + + if (!nn::nifm::IsNetworkAvailable()) { + this->socket_log_state = SOCKET_LOG_UNAVAILABLE; + return -1; + } + + #endif + + if ((this->socket_log_socket = nn::socket::Socket(2, 1, 0)) < 0) { + this->socket_log_state = SOCKET_LOG_UNAVAILABLE; + return -1; + } + + nn::socket::InetAton(this->sock_ip, &hostAddress); + + serverAddress.address = hostAddress; + serverAddress.port = nn::socket::InetHtons(this->port); + serverAddress.family = 2; + + nn::Result result; + + if ((result = nn::socket::Connect(this->socket_log_socket, &serverAddress, sizeof(serverAddress))).isFailure()) { + this->socket_log_state = SOCKET_LOG_UNAVAILABLE; + return result; + } + + this->socket_log_state = SOCKET_LOG_CONNECTED; + + this->isDisableName = false; + + return 0; +} + +void Logger::log(const char *fmt, va_list args) { // impl for replacing seads system::print + if (!sInstance) + return; + char buf[0x500]; + if (nn::util::VSNPrintf(buf, sizeof(buf), fmt, args) > 0) { + sInstance->socket_log(buf); + } +} + +s32 Logger::read(char *out) { + return this->socket_read_char(out); +} + +void Logger::log(const char* fmt, ...) { + if (!sInstance) + return; + va_list args; + va_start(args, fmt); + + char buf[0x500]; + + if (nn::util::VSNPrintf(buf, sizeof(buf), fmt, args) > 0) { + if (!sInstance->isDisableName) { + char prefix[0x510]; + nn::util::SNPrintf(prefix, sizeof(prefix), "[%s] %s", sInstance->sockName, buf); + sInstance->socket_log(prefix); + } else { + sInstance->socket_log(buf); + } + } + + va_end(args); +} + +bool Logger::pingSocket() { + return socket_log("ping") > 0; // if value is greater than zero, than the socket recieved our message, otherwise the connection was lost. +} + +void tryInitSocket() { + __asm("STR X20, [X8,#0x18]"); + #if DEBUGLOG + Logger::createInstance(); // creates a static instance for debug logger + #endif +} + diff --git a/source/states/StageSceneStatePauseMenu.cpp b/source/states/StageSceneStatePauseMenu.cpp new file mode 100644 index 0000000..f7b0025 --- /dev/null +++ b/source/states/StageSceneStatePauseMenu.cpp @@ -0,0 +1,24 @@ +#include "game/StageScene/StageSceneStatePauseMenu.h" + +void StageSceneStatePauseMenu::exeServerConfig(void) { + if (al::isFirstStep(this)) { + Logger::log("Start Server Config Nerve.\n"); + } + + al::updateKitListPrev(mHost); + rs::requestGraphicsPresetAndCubeMapPause(mHost); + al::updateKitList(mHost, "2D(ポーズ無視)"); + al::updateKitListPost(mHost); + + if (al::updateNerveState(this)) { + if (mStateOption->isChangeLanguage() || mStateOption->field_0x180) { + kill(); + } else { + mSelectParts->appearWait(); + mFooterParts->tryChangeTextFade( + al::getSystemMessageString(mMenuGuide, "Footer", "MenuMessage_Footer")); + + al::setNerve(this, &nrvStageSceneStatePauseMenuWait); + } + } +} \ No newline at end of file diff --git a/source/states/StageSceneStateServerConfig.cpp b/source/states/StageSceneStateServerConfig.cpp new file mode 100644 index 0000000..d28db9d --- /dev/null +++ b/source/states/StageSceneStateServerConfig.cpp @@ -0,0 +1,279 @@ +#include "game/StageScene/StageSceneStateServerConfig.hpp" +#include +#include +#include "server/Client.hpp" +#include "al/util.hpp" +#include "al/util/NerveUtil.h" +#include "container/seadPtrArray.h" +#include "container/seadSafeArray.h" +#include "logger.hpp" +#include "prim/seadSafeString.h" +#include "prim/seadStringUtil.h" +#include "rs/util/InputUtil.h" +#include "server/gamemode/GameModeBase.hpp" +#include "server/gamemode/GameModeFactory.hpp" +#include "server/HideAndSeekMode.hpp" + +StageSceneStateServerConfig::StageSceneStateServerConfig(const char *name, al::Scene *scene, const al::LayoutInitInfo &initInfo, FooterParts *footerParts, GameDataHolder *dataHolder, bool) : al::HostStateBase(name, scene) { + mFooterParts = footerParts; + mGameDataHolder = dataHolder; + + mMsgSystem = initInfo.getMessageSystem(); + + mInput = new InputSeparator(mHost, true); + + // page 0 menu + mMainOptions = new SimpleLayoutMenu("ServerConfigMenu", "OptionSelect", initInfo, 0, false); + mMainOptionsList = new CommonVerticalList(mMainOptions, initInfo, true); + + al::setPaneString(mMainOptions, "TxtOption", u"Server Configuration", 0); + + mMainOptionsList->initDataNoResetSelected(4); + + sead::SafeArray, 4>* mainMenuOptions = + new sead::SafeArray, 4>(); + + mainMenuOptions->mBuffer[0].copy(u"Gamemode Config"); + mainMenuOptions->mBuffer[1].copy(u"Change Gamemode"); + mainMenuOptions->mBuffer[2].copy(u"Reconnect to Server"); + mainMenuOptions->mBuffer[3].copy(u"Change Server IP"); + + mMainOptionsList->addStringData(mainMenuOptions->mBuffer, "TxtContent"); + + // gamemode select menu + + mModeSelect = new SimpleLayoutMenu("GamemodeSelectMenu", "OptionSelect", initInfo, 0, false); + mModeSelectList = new CommonVerticalList(mModeSelect, initInfo, true); + + al::setPaneString(mModeSelect, "TxtOption", u"Gamemode Selection", 0); + + const int modeCount = GameModeFactory::getModeCount(); + + mModeSelectList->initDataNoResetSelected(modeCount); + + sead::SafeArray, modeCount>* modeSelectOptions = + new sead::SafeArray, modeCount>(); + + for (size_t i = 0; i < modeCount; i++) { + const char* modeName = GameModeFactory::getModeName(i); + modeSelectOptions->mBuffer[i].convertFromMultiByteString(modeName, strlen(modeName)); + } + + mModeSelectList->addStringData(modeSelectOptions->mBuffer, "TxtContent"); + + // gamemode config menu + mGamemodeConfig = new SimpleLayoutMenu("GameModeConfigMenu", "OptionSelect", initInfo, 0, false); + mGameModeConfigList = new CommonVerticalList(mGamemodeConfig, initInfo, true); + + al::setPaneString(mGamemodeConfig, "TxtOption", u"Gamemode Configuration", 0); + + mGamemodeConfigMenu = Client::tryCreateModeMenu(); + + if (mGamemodeConfigMenu) { + mGameModeConfigList->initDataNoResetSelected(mGamemodeConfigMenu->getMenuSize()); + + mGameModeConfigList->addStringData(mGamemodeConfigMenu->getStringData(), "TxtContent"); + } + + mCurrentList = mMainOptionsList; + mCurrentMenu = mMainOptions; +} + +void StageSceneStateServerConfig::init() { + initNerve(&nrvStageSceneStateServerConfigMainMenu, 0); +} + +void StageSceneStateServerConfig::appear(void) { + mCurrentMenu->startAppear("Appear"); + al::NerveStateBase::appear(); +} + +void StageSceneStateServerConfig::kill(void) { + mCurrentMenu->startEnd("End"); + al::NerveStateBase::kill(); +} + +al::MessageSystem* StageSceneStateServerConfig::getMessageSystem(void) const { + return mMsgSystem; +} + +void StageSceneStateServerConfig::exeMainMenu() { + if (al::isFirstStep(this)) { + + mInput->reset(); + + mCurrentList->activate(); + + mCurrentList->appearCursor(); + + mIsDecideConfig = false; + + } + + mInput->update(); + + mCurrentList->update(); + + if (mInput->isTriggerUiUp()) { + mCurrentList->up(); } + + if (mInput->isTriggerUiDown()) { + mCurrentList->down(); + } + + if (rs::isTriggerUiCancel(mHost)) { + kill(); + } + + if (rs::isTriggerUiDecide(mHost)) { + al::startHitReaction(mCurrentMenu, "決定", 0); + mCurrentList->endCursor(); + mCurrentList->decide(); + mIsDecideConfig = true; + } + + if (mIsDecideConfig && mCurrentList->isDecideEnd()) { + switch (mCurrentList->mCurSelected) { + case ServerConfigOption::GAMEMODECONFIG: { + al::setNerve(this, &nrvStageSceneStateServerConfigGamemodeConfig); + break; + } + case ServerConfigOption::GAMEMODESWITCH: { + al::setNerve(this, &nrvStageSceneStateServerConfigGamemodeSelect); + break; + } + case ServerConfigOption::RECONNECT: { + al::setNerve(this, &nrvStageSceneStateServerConfigRestartServer); + break; + } + case ServerConfigOption::SETIP: { + al::setNerve(this, &nrvStageSceneStateServerConfigOpenKeyboard); + break; + } + default: + kill(); + break; + } + } +} + +void StageSceneStateServerConfig::exeOpenKeyboard() { + if (al::isFirstStep(this)) { + + mCurrentList->deactivate(); + + Client::openKeyboardIP(); + // anything that happens after this will be ran after the keyboard closes + al::startHitReaction(mCurrentMenu, "リセット", 0); + al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu); + } +} + +void StageSceneStateServerConfig::exeRestartServer() { + if (al::isFirstStep(this)) { + mCurrentList->deactivate(); + Client::stopConnection(); + } + + if (Client::isSocketActive()) { + al::startHitReaction(mCurrentMenu, "リセット", 0); + al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu); + } +} + +void StageSceneStateServerConfig::exeGamemodeConfig() { + if (al::isFirstStep(this)) { + mCurrentList = mGameModeConfigList; + mCurrentMenu = mGamemodeConfig; + subMenuStart(); + } + + subMenuUpdate(); + + if (mIsDecideConfig && mCurrentList->isDecideEnd()) { + if (mGamemodeConfigMenu->updateMenu(mCurrentList->mCurSelected)) { + endSubMenu(); + } + } +} + +void StageSceneStateServerConfig::exeGamemodeSelect() { + if (al::isFirstStep(this)) { + + mCurrentList = mModeSelectList; + mCurrentMenu = mModeSelect; + + subMenuStart(); + + } + + subMenuUpdate(); + + if (mIsDecideConfig && mCurrentList->isDecideEnd()) { + Logger::log("Setting Server Mode to: %d\n", mCurrentList->mCurSelected); + Client::setServerMode(static_cast(mCurrentList->mCurSelected)); + endSubMenu(); + } +} + +void StageSceneStateServerConfig::endSubMenu() { + mCurrentList->deactivate(); + mCurrentMenu->kill(); + + mCurrentList = mMainOptionsList; + mCurrentMenu = mMainOptions; + + mCurrentMenu->startAppear("Appear"); + + al::startHitReaction(mCurrentMenu, "リセット", 0); + al::setNerve(this, &nrvStageSceneStateServerConfigMainMenu); +} + +void StageSceneStateServerConfig::subMenuStart() { + mCurrentList->deactivate(); + + mCurrentMenu->kill(); + + mInput->reset(); + + mCurrentList->activate(); + + mCurrentList->appearCursor(); + + mCurrentMenu->startAppear("Appear"); + + mIsDecideConfig = false; +} + +void StageSceneStateServerConfig::subMenuUpdate() { + mInput->update(); + + mCurrentList->update(); + + if (mInput->isTriggerUiUp()) { + mCurrentList->up(); + } + + if (mInput->isTriggerUiDown()) { + mCurrentList->down(); + } + + if (rs::isTriggerUiCancel(mHost)) { + endSubMenu(); + } + + if (rs::isTriggerUiDecide(mHost)) { + al::startHitReaction(mCurrentMenu, "決定", 0); + mCurrentList->endCursor(); + mCurrentList->decide(); + mIsDecideConfig = true; + } +} + +namespace { +NERVE_IMPL(StageSceneStateServerConfig, MainMenu) +NERVE_IMPL(StageSceneStateServerConfig, OpenKeyboard) +NERVE_IMPL(StageSceneStateServerConfig, RestartServer) +NERVE_IMPL(StageSceneStateServerConfig, GamemodeConfig) +NERVE_IMPL(StageSceneStateServerConfig, GamemodeSelect) +} \ No newline at end of file diff --git a/switch.specs b/switch.specs new file mode 100644 index 0000000..7950f31 --- /dev/null +++ b/switch.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T ../linkerscripts/symstemp.ld -T ../linkerscripts/application.ld --shared --build-id=sha1 --build-id=sha1 + +*startfile: +crti%O%s crtbegin%O%s \ No newline at end of file

Jl?;nis&bYgA{;$V>w_tiS4*?H> I{|5qp12c&;-2eap literal 0 HcmV?d00001 diff --git a/romfs/DebugData/Font/nvn_font_shader_jis1.bin b/romfs/DebugData/Font/nvn_font_shader_jis1.bin new file mode 100644 index 0000000000000000000000000000000000000000..a4bc7399ca2c5d820cfbc23a3529fe451a41426c GIT binary patch literal 9472 zcmeHMO>7&-6@JTIawwWMC8L1TA}BW}C9b<&krXXhH6mH07y`7U>dyj9Rh1k40gffm zvL#0_%!5(&phFJ<;!{H8;;Rn@V)POR5MUo%)VJnT7|J21!gT6@KDgR`Z+4aUiXTav5a4kzUWQ zcitHozq-_E*`dLq(d^)WIWRtTVd2t;7iTVCo?4z+UA|J7=?EF2G+*uI1rcAF|&-Ph;edcfNSwMA0 zL((a8vax=i67zBxS)EE*{k%GmPilc;wCW??Ss$dS)W8C~whFs=Re^6JAs4n>Rq+^> zQw<$VlAh=SJvEe&dekW`gXqy$VFREx37G69ArM0kc$A1_>-FgYT=Yh+9(oS?F?asPgRtb=6ZrSRnq5jJP>%jLYTDk>(c$bFbG9%&_tDIyhN{-1zO1grv2<`C<5saLoCfQQ z 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 QR*J2kM&7M_r(yU10FU;0w*UYD literal 0 HcmV?d00001 diff --git a/romfs/LayoutData/TitleLogoParts.szs b/romfs/LayoutData/TitleLogoParts.szs new file mode 100644 index 0000000000000000000000000000000000000000..3a0bd29b925f0c3af38425939f2b24e4803b8533 GIT binary patch literal 310356 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�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∋hj@};#og(CX&ay(e-o?P@E;mdl{Q$k9 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