From eefc67e50a3cfff0ef6575989235e37e11f2c4d1 Mon Sep 17 00:00:00 2001 From: nickpons666 Date: Mon, 30 Mar 2026 15:33:19 -0600 Subject: [PATCH] Plugins agregados --- OTRExporter/.gitignore | 355 ++ OTRExporter/CMake/Default.cmake | 65 + OTRExporter/CMake/DefaultCXX.cmake | 16 + OTRExporter/CMake/Utils.cmake | 233 + OTRExporter/CMakeLists.txt | 97 + OTRExporter/LICENSE | 9 + OTRExporter/OTRExporter/AnimationExporter.cpp | 92 + OTRExporter/OTRExporter/AnimationExporter.h | 13 + OTRExporter/OTRExporter/ArrayExporter.cpp | 116 + OTRExporter/OTRExporter/ArrayExporter.h | 12 + OTRExporter/OTRExporter/AudioExporter.cpp | 483 ++ OTRExporter/OTRExporter/AudioExporter.h | 33 + .../OTRExporter/BackgroundExporter.cpp | 14 + OTRExporter/OTRExporter/BackgroundExporter.h | 12 + OTRExporter/OTRExporter/BlobExporter.cpp | 21 + OTRExporter/OTRExporter/BlobExporter.h | 12 + OTRExporter/OTRExporter/CKeyFrameExporter.cpp | 105 + OTRExporter/OTRExporter/CKeyFrameExporter.h | 19 + OTRExporter/OTRExporter/CMakeLists.txt | 213 + OTRExporter/OTRExporter/CollisionExporter.cpp | 84 + OTRExporter/OTRExporter/CollisionExporter.h | 11 + OTRExporter/OTRExporter/CutsceneExporter.cpp | 606 +++ OTRExporter/OTRExporter/CutsceneExporter.h | 13 + .../OTRExporter/DisplayListExporter.cpp | 1089 ++++ OTRExporter/OTRExporter/DisplayListExporter.h | 17 + OTRExporter/OTRExporter/Exporter.cpp | 21 + OTRExporter/OTRExporter/Exporter.h | 17 + OTRExporter/OTRExporter/ExporterArchive.cpp | 11 + OTRExporter/OTRExporter/ExporterArchive.h | 26 + .../OTRExporter/ExporterArchiveO2R.cpp | 93 + OTRExporter/OTRExporter/ExporterArchiveO2R.h | 24 + .../OTRExporter/ExporterArchiveOTR.cpp | 147 + OTRExporter/OTRExporter/ExporterArchiveOTR.h | 29 + OTRExporter/OTRExporter/Main.cpp | 440 ++ OTRExporter/OTRExporter/Main.h | 9 + OTRExporter/OTRExporter/MtxExporter.cpp | 13 + OTRExporter/OTRExporter/MtxExporter.h | 10 + OTRExporter/OTRExporter/PathExporter.cpp | 27 + OTRExporter/OTRExporter/PathExporter.h | 12 + .../OTRExporter/PlayerAnimationExporter.cpp | 21 + .../OTRExporter/PlayerAnimationExporter.h | 13 + OTRExporter/OTRExporter/RoomExporter.cpp | 636 +++ OTRExporter/OTRExporter/RoomExporter.h | 15 + OTRExporter/OTRExporter/SkeletonExporter.cpp | 39 + OTRExporter/OTRExporter/SkeletonExporter.h | 14 + .../OTRExporter/SkeletonLimbExporter.cpp | 180 + .../OTRExporter/SkeletonLimbExporter.h | 15 + OTRExporter/OTRExporter/TextExporter.cpp | 19 + OTRExporter/OTRExporter/TextExporter.h | 12 + OTRExporter/OTRExporter/TextMMExporter.cpp | 25 + OTRExporter/OTRExporter/TextMMExporter.h | 13 + .../OTRExporter/TextureAnimationExporter.cpp | 126 + .../OTRExporter/TextureAnimationExporter.h | 15 + OTRExporter/OTRExporter/TextureExporter.cpp | 32 + OTRExporter/OTRExporter/TextureExporter.h | 12 + OTRExporter/OTRExporter/VersionInfo.cpp | 29 + OTRExporter/OTRExporter/VersionInfo.h | 8 + OTRExporter/OTRExporter/VtxExporter.cpp | 44 + OTRExporter/OTRExporter/VtxExporter.h | 13 + OTRExporter/OTRExporter/command_macros_base.h | 32 + OTRExporter/OTRExporter/z64cutscene.h | 290 + .../OTRExporter/z64cutscene_commands.h | 448 ++ .../accessibility/texts/filechoose_eng.json | 29 + .../accessibility/texts/filechoose_fra.json | 29 + .../accessibility/texts/filechoose_ger.json | 29 + .../accessibility/texts/kaleidoscope_eng.json | 231 + .../accessibility/texts/kaleidoscope_fra.json | 231 + .../accessibility/texts/kaleidoscope_ger.json | 231 + .../assets/accessibility/texts/misc_eng.json | 24 + .../assets/accessibility/texts/misc_fra.json | 24 + .../assets/accessibility/texts/misc_ger.json | 24 + .../accessibility/texts/scenes_eng.json | 112 + .../accessibility/texts/scenes_fra.json | 112 + .../accessibility/texts/scenes_ger.json | 112 + OTRExporter/assets/fonts/Fipps-Regular.otf | Bin 0 -> 34220 bytes .../assets/fonts/PressStart2P-Regular.ttf | Bin 0 -> 116008 bytes .../cosmetics/gEndGrayscaleAndEndDlistDL | 5 + ...stmasGreenTreasureChestFrontTex.rgb5a1.png | Bin 0 -> 2917 bytes ...GreenTreasureChestSideAndTopTex.rgb5a1.png | Bin 0 -> 1111 bytes ...ristmasRedTreasureChestFrontTex.rgb5a1.png | Bin 0 -> 3518 bytes ...asRedTreasureChestSideAndTopTex.rgb5a1.png | Bin 0 -> 1661 bytes .../gGoldTreasureChestFrontTex.rgb5a1.png | Bin 0 -> 4727 bytes ...gGoldTreasureChestSideAndTopTex.rgb5a1.png | Bin 0 -> 2338 bytes .../gKeyTreasureChestFrontTex.rgb5a1.png | Bin 0 -> 4233 bytes .../gKeyTreasureChestSideAndTopTex.rgb5a1.png | Bin 0 -> 2184 bytes .../gSkullTreasureChestFrontTex.rgb5a1.png | Bin 0 -> 4265 bytes ...SkullTreasureChestSideAndTopTex.rgb5a1.png | Bin 0 -> 1979 bytes .../gTitleBossRushSubtitleTex.rgba32.png | Bin 0 -> 6291 bytes .../gTitleRandomizerSubtitleTex.rgba32.png | Bin 0 -> 6971 bytes .../gTriforcePieceCompletedDL | 13 + .../gTriforcePieceCompletedDL_tri_0 | 58 + .../gTriforcePieceCompletedDL_tri_1 | 7 + .../gTriforcePieceCompletedDL_vtx_0 | 54 + .../gTriforcePieceCompletedDL_vtx_1 | 8 + ...rcePieceCompletedDL_f3dlite_triforce_edges | 21 + ...ePieceCompletedDL_f3dlite_triforce_surface | 21 + .../object_triforce_completed/noise_tex | Bin 0 -> 2140 bytes .../object_triforce_piece_0/gTriforcePiece0DL | 15 + .../gTriforcePiece0DL_tri_0 | 17 + .../gTriforcePiece0DL_tri_1 | 18 + .../gTriforcePiece0DL_tri_2 | 51 + .../gTriforcePiece0DL_vtx_0 | 18 + .../gTriforcePiece0DL_vtx_1 | 22 + .../gTriforcePiece0DL_vtx_2 | 49 + .../mat_gTriforcePiece0DL_f3dlite_shard_edge | 21 + ...t_gTriforcePiece0DL_f3dlite_triforce_edges | 21 + ...gTriforcePiece0DL_f3dlite_triforce_surface | 21 + .../objects/object_triforce_piece_0/noise_tex | Bin 0 -> 2140 bytes .../object_triforce_piece_1/gTriforcePiece1DL | 13 + .../gTriforcePiece1DL_tri_0 | 20 + .../gTriforcePiece1DL_tri_1 | 25 + .../gTriforcePiece1DL_vtx_0 | 23 + .../gTriforcePiece1DL_vtx_1 | 34 + .../mat_gTriforcePiece1DL_f3dlite_shard_edge | 21 + ...gTriforcePiece1DL_f3dlite_triforce_surface | 21 + .../objects/object_triforce_piece_1/noise_tex | Bin 0 -> 2140 bytes .../object_triforce_piece_2/gTriforcePiece2DL | 15 + .../gTriforcePiece2DL_tri_0 | 29 + .../gTriforcePiece2DL_tri_1 | 11 + .../gTriforcePiece2DL_tri_2 | 19 + .../gTriforcePiece2DL_vtx_0 | 36 + .../gTriforcePiece2DL_vtx_1 | 12 + .../gTriforcePiece2DL_vtx_2 | 18 + .../mat_gTriforcePiece2DL_f3dlite_shard_edge | 21 + ...t_gTriforcePiece2DL_f3dlite_triforce_edges | 21 + ...gTriforcePiece2DL_f3dlite_triforce_surface | 21 + .../objects/object_triforce_piece_2/noise_tex | Bin 0 -> 2140 bytes .../scenes/nonmq/syotes_scene/syotes_room_0 | Bin 0 -> 211 bytes OTRExporter/assets/textures/buttons/ABtn.png | Bin 0 -> 10967 bytes OTRExporter/assets/textures/buttons/BBtn.png | Bin 0 -> 1300 bytes OTRExporter/assets/textures/buttons/CDown.png | Bin 0 -> 11241 bytes OTRExporter/assets/textures/buttons/CLeft.png | Bin 0 -> 11232 bytes .../assets/textures/buttons/CRight.png | Bin 0 -> 11284 bytes OTRExporter/assets/textures/buttons/CUp.png | Bin 0 -> 11219 bytes OTRExporter/assets/textures/buttons/LBtn.png | Bin 0 -> 355 bytes OTRExporter/assets/textures/buttons/RBtn.png | Bin 0 -> 379 bytes .../assets/textures/buttons/StartBtn.png | Bin 0 -> 11868 bytes OTRExporter/assets/textures/buttons/ZBtn.png | Bin 0 -> 1241 bytes OTRExporter/assets/textures/icons/gIcon.png | Bin 0 -> 932 bytes .../textures/nintendo_rogo_static/SoHShiny | Bin 0 -> 1116 bytes .../textures/nintendo_rogo_static/gShipLogoDL | 21 + .../nintendo_rogo_static/gShipLogoDL_tri_0 | 241 + .../nintendo_rogo_static/gShipLogoDL_tri_1 | 80 + .../nintendo_rogo_static/gShipLogoDL_tri_2 | 259 + .../nintendo_rogo_static/gShipLogoDL_tri_3 | 118 + .../nintendo_rogo_static/gShipLogoDL_vtx_0 | 335 ++ .../nintendo_rogo_static/gShipLogoDL_vtx_1 | 98 + .../nintendo_rogo_static/gShipLogoDL_vtx_2 | 323 ++ .../nintendo_rogo_static/gShipLogoDL_vtx_3 | 128 + .../mat_gShipLogoDL_f3d_material | 20 + .../mat_gShipLogoDL_f3d_material_001 | 20 + .../mat_gShipLogoDL_f3d_material_002 | 20 + .../mat_gShipLogoDL_f3d_material_003 | 20 + .../mat_revert_gShipLogoDL_f3d_material | 8 + .../mat_revert_gShipLogoDL_f3d_material_001 | 8 + .../mat_revert_gShipLogoDL_f3d_material_002 | 8 + .../mat_revert_gShipLogoDL_f3d_material_003 | 8 + .../nintendo_rogo_static_Tex_LUS_000000 | Bin 0 -> 6291548 bytes .../parameter_static/gArrowDown.ia16.png | Bin 0 -> 17227 bytes .../parameter_static/gArrowUp.ia16.png | Bin 0 -> 17217 bytes .../textures/parameter_static/gDPad.ia16.png | Bin 0 -> 1017 bytes .../gTriforcePiece.rgba32.png | Bin 0 -> 1761 bytes .../gFileSelBossRushSettingsENGTex.ia8.png | Bin 0 -> 4004 bytes .../gFileSelBossRushSettingsFRATex.ia8.png | Bin 0 -> 3942 bytes .../gFileSelBossRushSettingsGERTex.ia8.png | Bin 0 -> 3884 bytes .../title_static/gFileSelMQButtonTex.ia16.png | Bin 0 -> 883 bytes .../gFileSelPleaseChooseAQuestENGTex.ia8.png | Bin 0 -> 3682 bytes .../gFileSelPleaseChooseAQuestFRATex.ia8.png | Bin 0 -> 3852 bytes .../gFileSelPleaseChooseAQuestGERTex.ia8.png | Bin 0 -> 3666 bytes .../gFileSelRANDButtonTex.ia16.png | Bin 0 -> 1045 bytes .../textures/virtual/gEmptyTexture.rgba32.png | Bin 0 -> 194 bytes OTRExporter/extract_assets.py | 93 + OTRExporter/extract_baserom.py | 53 + OTRExporter/offsets.json | 8 + OTRExporter/rom_chooser.py | 54 + OTRExporter/rom_info.py | 129 + ZAPDTR/.clang-format | 84 + ZAPDTR/.github/workflows/main.yml | 83 + ZAPDTR/.gitignore | 341 ++ ZAPDTR/ExporterTest/CollisionExporter.cpp | 76 + ZAPDTR/ExporterTest/CollisionExporter.h | 10 + ZAPDTR/ExporterTest/Main.cpp | 79 + ZAPDTR/ExporterTest/RoomExporter.cpp | 372 ++ ZAPDTR/ExporterTest/RoomExporter.h | 10 + ZAPDTR/ExporterTest/TextureExporter.cpp | 14 + ZAPDTR/ExporterTest/TextureExporter.h | 11 + ZAPDTR/LICENSE | 21 + ZAPDTR/README.md | 164 + ZAPDTR/ZAPD/CMakeLists.txt | 454 ++ ZAPDTR/ZAPD/CRC32.h | 23 + ZAPDTR/ZAPD/CrashHandler.cpp | 208 + ZAPDTR/ZAPD/CrashHandler.h | 6 + ZAPDTR/ZAPD/Declaration.cpp | 248 + ZAPDTR/ZAPD/Declaration.h | 188 + ZAPDTR/ZAPD/ExecutableMain.cpp | 9 + ZAPDTR/ZAPD/ExporterSet.h | 27 + ZAPDTR/ZAPD/FileWorker.cpp | 0 ZAPDTR/ZAPD/FileWorker.h | 16 + ZAPDTR/ZAPD/GameConfig.cpp | 301 ++ ZAPDTR/ZAPD/GameConfig.h | 97 + ZAPDTR/ZAPD/Globals.cpp | 363 ++ ZAPDTR/ZAPD/Globals.h | 103 + ZAPDTR/ZAPD/ImageBackend.cpp | 507 ++ ZAPDTR/ZAPD/ImageBackend.h | 72 + ZAPDTR/ZAPD/Main.cpp | 756 +++ ZAPDTR/ZAPD/NuGet/libpng.static.txt | 0 .../ZAPD/OtherStructs/CutsceneMM_Commands.cpp | 614 +++ .../ZAPD/OtherStructs/CutsceneMM_Commands.h | 481 ++ .../OtherStructs/CutsceneOoT_Commands.cpp | 458 ++ .../ZAPD/OtherStructs/CutsceneOoT_Commands.h | 314 ++ ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.cpp | 128 + ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.h | 72 + ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.cpp | 354 ++ ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.h | 111 + ZAPDTR/ZAPD/OutputFormatter.cpp | 123 + ZAPDTR/ZAPD/OutputFormatter.h | 42 + ZAPDTR/ZAPD/Overlays/ZOverlay.cpp | 354 ++ ZAPDTR/ZAPD/Overlays/ZOverlay.h | 78 + ZAPDTR/ZAPD/Utils/BinaryReader.cpp | 178 + ZAPDTR/ZAPD/Utils/BinaryReader.h | 41 + ZAPDTR/ZAPD/Utils/BinaryWriter.cpp | 148 + ZAPDTR/ZAPD/Utils/BinaryWriter.h | 41 + ZAPDTR/ZAPD/Utils/BitConverter.h | 209 + ZAPDTR/ZAPD/Utils/Directory.h | 60 + ZAPDTR/ZAPD/Utils/DiskFile.h | 91 + ZAPDTR/ZAPD/Utils/MemoryStream.cpp | 97 + ZAPDTR/ZAPD/Utils/MemoryStream.h | 33 + ZAPDTR/ZAPD/Utils/Path.h | 50 + ZAPDTR/ZAPD/Utils/Stream.h | 34 + ZAPDTR/ZAPD/Utils/StringHelper.cpp | 191 + ZAPDTR/ZAPD/Utils/StringHelper.h | 33 + ZAPDTR/ZAPD/Utils/vt.h | 45 + ZAPDTR/ZAPD/WarningHandler.cpp | 455 ++ ZAPDTR/ZAPD/WarningHandler.h | 146 + ZAPDTR/ZAPD/ZActorList.cpp | 194 + ZAPDTR/ZAPD/ZActorList.h | 52 + ZAPDTR/ZAPD/ZAnimation.cpp | 580 ++ ZAPDTR/ZAPD/ZAnimation.h | 179 + ZAPDTR/ZAPD/ZArray.cpp | 154 + ZAPDTR/ZAPD/ZArray.h | 31 + ZAPDTR/ZAPD/ZAudio.cpp | 416 ++ ZAPDTR/ZAPD/ZAudio.h | 130 + ZAPDTR/ZAPD/ZAudioDecode.cpp | 670 +++ ZAPDTR/ZAPD/ZBackground.cpp | 200 + ZAPDTR/ZAPD/ZBackground.h | 34 + ZAPDTR/ZAPD/ZBlob.cpp | 116 + ZAPDTR/ZAPD/ZBlob.h | 31 + ZAPDTR/ZAPD/ZCKeyFrame.cpp | 307 ++ ZAPDTR/ZAPD/ZCKeyFrame.h | 121 + ZAPDTR/ZAPD/ZCKeyFrameAnim.cpp | 221 + ZAPDTR/ZAPD/ZCkeyFrameAnim.h | 52 + ZAPDTR/ZAPD/ZCollision.cpp | 451 ++ ZAPDTR/ZAPD/ZCollision.h | 75 + ZAPDTR/ZAPD/ZCollisionPoly.cpp | 78 + ZAPDTR/ZAPD/ZCollisionPoly.h | 29 + ZAPDTR/ZAPD/ZCutscene.cpp | 375 ++ ZAPDTR/ZAPD/ZCutscene.h | 37 + ZAPDTR/ZAPD/ZDisplayList.cpp | 2324 ++++++++ ZAPDTR/ZAPD/ZDisplayList.h | 265 + ZAPDTR/ZAPD/ZFile.cpp | 1505 ++++++ ZAPDTR/ZAPD/ZFile.h | 150 + ZAPDTR/ZAPD/ZLimb.cpp | 420 ++ ZAPDTR/ZAPD/ZLimb.h | 74 + ZAPDTR/ZAPD/ZMtx.cpp | 59 + ZAPDTR/ZAPD/ZMtx.h | 24 + ZAPDTR/ZAPD/ZPath.cpp | 218 + ZAPDTR/ZAPD/ZPath.h | 51 + ZAPDTR/ZAPD/ZPlayerAnimationData.cpp | 108 + ZAPDTR/ZAPD/ZPlayerAnimationData.h | 28 + ZAPDTR/ZAPD/ZPointer.cpp | 57 + ZAPDTR/ZAPD/ZPointer.h | 22 + ZAPDTR/ZAPD/ZResource.cpp | 450 ++ ZAPDTR/ZAPD/ZResource.h | 268 + ZAPDTR/ZAPD/ZRom.cpp | 290 + ZAPDTR/ZAPD/ZRom.h | 28 + ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.cpp | 20 + ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.h | 13 + ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.cpp | 59 + ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.h | 24 + .../ZRoom/Commands/SetAlternateHeaders.cpp | 84 + .../ZAPD/ZRoom/Commands/SetAlternateHeaders.h | 23 + .../Commands/SetAnimatedMaterialList.cpp | 49 + .../ZRoom/Commands/SetAnimatedMaterialList.h | 20 + .../ZAPD/ZRoom/Commands/SetCameraSettings.cpp | 36 + .../ZAPD/ZRoom/Commands/SetCameraSettings.h | 21 + .../ZRoom/Commands/SetCollisionHeader.cpp | 45 + .../ZAPD/ZRoom/Commands/SetCollisionHeader.h | 20 + ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.cpp | 154 + ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.h | 40 + .../ZRoom/Commands/SetCutsceneEntryList.cpp | 103 + .../ZRoom/Commands/SetCutsceneEntryList.h | 42 + ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.cpp | 150 + ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.h | 33 + .../ZAPD/ZRoom/Commands/SetEchoSettings.cpp | 27 + ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.h | 18 + .../ZAPD/ZRoom/Commands/SetEntranceList.cpp | 99 + ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.h | 31 + ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.cpp | 76 + ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.h | 20 + ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.cpp | 100 + ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.h | 42 + .../ZRoom/Commands/SetLightingSettings.cpp | 116 + .../ZAPD/ZRoom/Commands/SetLightingSettings.h | 38 + ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.cpp | 617 +++ ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.h | 160 + .../ZAPD/ZRoom/Commands/SetMinimapChests.cpp | 83 + ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.h | 33 + ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.cpp | 97 + ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.h | 37 + ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.cpp | 67 + ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.h | 19 + ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.cpp | 65 + ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.h | 23 + .../ZAPD/ZRoom/Commands/SetRoomBehavior.cpp | 50 + ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.h | 29 + ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.cpp | 155 + ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.h | 53 + .../ZAPD/ZRoom/Commands/SetSkyboxModifier.cpp | 32 + .../ZAPD/ZRoom/Commands/SetSkyboxModifier.h | 19 + .../ZAPD/ZRoom/Commands/SetSkyboxSettings.cpp | 36 + .../ZAPD/ZRoom/Commands/SetSkyboxSettings.h | 21 + .../ZAPD/ZRoom/Commands/SetSoundSettings.cpp | 30 + ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.h | 20 + .../ZAPD/ZRoom/Commands/SetSpecialObjects.cpp | 40 + .../ZAPD/ZRoom/Commands/SetSpecialObjects.h | 19 + .../ZRoom/Commands/SetStartPositionList.cpp | 68 + .../ZRoom/Commands/SetStartPositionList.h | 20 + .../ZAPD/ZRoom/Commands/SetTimeSettings.cpp | 30 + ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.h | 20 + .../ZRoom/Commands/SetTransitionActorList.cpp | 93 + .../ZRoom/Commands/SetTransitionActorList.h | 36 + ZAPDTR/ZAPD/ZRoom/Commands/SetWind.cpp | 32 + ZAPDTR/ZAPD/ZRoom/Commands/SetWind.h | 21 + .../ZRoom/Commands/SetWorldMapVisited.cpp | 26 + .../ZAPD/ZRoom/Commands/SetWorldMapVisited.h | 14 + ZAPDTR/ZAPD/ZRoom/Commands/Unused09.cpp | 21 + ZAPDTR/ZAPD/ZRoom/Commands/Unused09.h | 14 + ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.cpp | 21 + ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.h | 14 + .../ZAPD/ZRoom/Commands/ZRoomCommandUnk.cpp | 20 + ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.h | 12 + ZAPDTR/ZAPD/ZRoom/ZNames.h | 68 + ZAPDTR/ZAPD/ZRoom/ZRoom.cpp | 409 ++ ZAPDTR/ZAPD/ZRoom/ZRoom.h | 49 + ZAPDTR/ZAPD/ZRoom/ZRoomCommand.cpp | 58 + ZAPDTR/ZAPD/ZRoom/ZRoomCommand.h | 84 + ZAPDTR/ZAPD/ZScalar.cpp | 280 + ZAPDTR/ZAPD/ZScalar.h | 68 + ZAPDTR/ZAPD/ZSkeleton.cpp | 478 ++ ZAPDTR/ZAPD/ZSkeleton.h | 85 + ZAPDTR/ZAPD/ZString.cpp | 69 + ZAPDTR/ZAPD/ZString.h | 24 + ZAPDTR/ZAPD/ZSurfaceType.cpp | 65 + ZAPDTR/ZAPD/ZSurfaceType.h | 27 + ZAPDTR/ZAPD/ZSymbol.cpp | 98 + ZAPDTR/ZAPD/ZSymbol.h | 27 + ZAPDTR/ZAPD/ZText.cpp | 193 + ZAPDTR/ZAPD/ZText.h | 32 + ZAPDTR/ZAPD/ZTextMM.cpp | 222 + ZAPDTR/ZAPD/ZTextMM.h | 36 + ZAPDTR/ZAPD/ZTexture.cpp | 1013 ++++ ZAPDTR/ZAPD/ZTexture.h | 119 + ZAPDTR/ZAPD/ZTextureAnimation.cpp | 679 +++ ZAPDTR/ZAPD/ZTextureAnimation.h | 151 + ZAPDTR/ZAPD/ZVector.cpp | 126 + ZAPDTR/ZAPD/ZVector.h | 31 + ZAPDTR/ZAPD/ZVtx.cpp | 86 + ZAPDTR/ZAPD/ZVtx.h | 33 + ZAPDTR/ZAPD/ZWaterbox.cpp | 74 + ZAPDTR/ZAPD/ZWaterbox.h | 30 + ZAPDTR/ZAPD/any/any/zlib.static.txt | 0 ZAPDTR/ZAPD/ctpl_stl.h | 251 + ZAPDTR/ZAPD/genbuildinfo.py | 24 + ZAPDTR/ZAPD/packages.config | 9 + ZAPDTR/ZAPD/pathconf.c | 6 + ZAPDTR/ZAPD/yaz0/readwrite.h | 29 + ZAPDTR/ZAPD/yaz0/yaz0.cpp | 256 + ZAPDTR/ZAPD/yaz0/yaz0.h | 7 + ZAPDTR/copycheck.py | 9 + ZAPDTR/docs/zapd_extraction_xml_reference.md | 680 +++ ZAPDTR/docs/zapd_xml_spec.md | 55 + ZAPDTR/lib/libgfxd/.gitrepo | 12 + ZAPDTR/lib/libgfxd/LICENSE | 21 + ZAPDTR/lib/libgfxd/README.md | 478 ++ ZAPDTR/lib/libgfxd/gbi.h | 3838 +++++++++++++ ZAPDTR/lib/libgfxd/gfxd.c | 863 +++ ZAPDTR/lib/libgfxd/gfxd.h | 387 ++ ZAPDTR/lib/libgfxd/priv.h | 125 + ZAPDTR/lib/libgfxd/uc.c | 54 + ZAPDTR/lib/libgfxd/uc_argfn.c | 1814 +++++++ ZAPDTR/lib/libgfxd/uc_argtbl.c | 485 ++ ZAPDTR/lib/libgfxd/uc_f3d.c | 4 + ZAPDTR/lib/libgfxd/uc_f3db.c | 5 + ZAPDTR/lib/libgfxd/uc_f3dex.c | 4 + ZAPDTR/lib/libgfxd/uc_f3dex2.c | 4 + ZAPDTR/lib/libgfxd/uc_f3dexb.c | 5 + ZAPDTR/lib/libgfxd/uc_macrofn.c | 2507 +++++++++ ZAPDTR/lib/libgfxd/uc_macrotbl.c | 1397 +++++ ZAPDTR/lib/tinyxml2/tinyxml2.cpp | 2837 ++++++++++ ZAPDTR/lib/tinyxml2/tinyxml2.h | 2309 ++++++++ libultraship/.clang-format | 23 + libultraship/.clang-tidy | 30 + libultraship/.github/workflows/apt-deps.txt | 1 + libultraship/.github/workflows/brew-deps.txt | 1 + .../workflows/tidy-format-validation.yml | 62 + .../.github/workflows/tidy-result-publish.yml | 74 + libultraship/.gitignore | 367 ++ libultraship/CMakeLists.txt | 67 + libultraship/CODE_OF_CONDUCT.md | 134 + libultraship/CONTRIBUTING.md | 28 + libultraship/LICENSE | 21 + libultraship/README.md | 56 + libultraship/cmake/Default.cmake | 65 + libultraship/cmake/DefaultCXX.cmake | 16 + libultraship/cmake/HandleCompilerRT.cmake | 123 + libultraship/cmake/Utils.cmake | 248 + libultraship/cmake/automate-vcpkg.cmake | 193 + libultraship/cmake/cvars.cmake | 47 + libultraship/cmake/dependencies/android.cmake | 73 + libultraship/cmake/dependencies/common.cmake | 113 + .../cmake/dependencies/git-patch.cmake | 61 + libultraship/cmake/dependencies/ios.cmake | 90 + libultraship/cmake/dependencies/linux.cmake | 10 + libultraship/cmake/dependencies/mac.cmake | 42 + libultraship/cmake/dependencies/openbsd.cmake | 20 + .../patches/imgui-fixes-and-config.patch | 112 + .../patches/stormlib-optimizations.patch | 123 + .../cmake/dependencies/windows-vcpkg.cmake | 17 + libultraship/cmake/dependencies/windows.cmake | 12 + .../cmake/ios-toolchain-populate.cmake | 15 + libultraship/include/fast/Fast3dWindow.h | 76 + .../include/fast/FastMouseStateManager.h | 15 + libultraship/include/fast/LICENSE.txt | 21 + libultraship/include/fast/README.md | 30 + .../include/fast/backends/gfx_direct3d11.h | 11 + .../fast/backends/gfx_direct3d_common.h | 185 + libultraship/include/fast/backends/gfx_dxgi.h | 120 + .../include/fast/backends/gfx_metal.h | 224 + .../include/fast/backends/gfx_metal_shader.h | 20 + .../include/fast/backends/gfx_opengl.h | 140 + .../include/fast/backends/gfx_rendering_api.h | 85 + .../include/fast/backends/gfx_screen_config.h | 4 + libultraship/include/fast/backends/gfx_sdl.h | 65 + .../fast/backends/gfx_window_manager_api.h | 52 + libultraship/include/fast/f3dex.h | 153 + libultraship/include/fast/f3dex2.h | 149 + libultraship/include/fast/interpreter.h | 531 ++ libultraship/include/fast/lus_gbi.h | 1367 +++++ .../include/fast/resource/ResourceType.h | 14 + .../resource/factory/DisplayListFactory.h | 24 + .../fast/resource/factory/LightFactory.h | 12 + .../fast/resource/factory/MatrixFactory.h | 12 + .../fast/resource/factory/TextureFactory.h | 18 + .../fast/resource/factory/VertexFactory.h | 19 + .../include/fast/resource/type/DisplayList.h | 23 + .../include/fast/resource/type/Light.h | 65 + .../include/fast/resource/type/Matrix.h | 18 + .../include/fast/resource/type/Texture.h | 41 + .../include/fast/resource/type/Vertex.h | 20 + libultraship/include/fast/types.h | 25 + libultraship/include/fast/ucodehandlers.h | 11 + libultraship/include/libultraship/bridge.h | 10 + .../include/libultraship/bridge/audiobridge.h | 23 + .../bridge/consolevariablebridge.h | 42 + .../libultraship/bridge/controllerbridge.h | 14 + .../libultraship/bridge/crashhandlerbridge.h | 15 + .../include/libultraship/bridge/gfxbridge.h | 16 + .../libultraship/bridge/gfxdebuggerbridge.h | 17 + .../libultraship/bridge/resourcebridge.h | 51 + .../libultraship/bridge/windowbridge.h | 19 + libultraship/include/libultraship/classes.h | 38 + libultraship/include/libultraship/color.h | 39 + .../controller/controldeck/ControlDeck.h | 30 + .../controldevice/controller/Controller.h | 27 + .../mapping/ControllerDefaultMappings.h | 39 + libultraship/include/libultraship/libultra.h | 27 + .../include/libultraship/libultra/abi.h | 471 ++ .../libultraship/libultra/controller.h | 162 + .../include/libultraship/libultra/convert.h | 15 + .../include/libultraship/libultra/eeprom.h | 18 + .../include/libultraship/libultra/exception.h | 42 + .../include/libultraship/libultra/gbi.h | 4223 +++++++++++++++ .../include/libultraship/libultra/gs2dex.h | 384 ++ .../include/libultraship/libultra/gu.h | 201 + .../include/libultraship/libultra/internal.h | 27 + .../include/libultraship/libultra/interrupt.h | 5 + .../include/libultraship/libultra/mbi.h | 42 + .../include/libultraship/libultra/message.h | 66 + .../include/libultraship/libultra/motor.h | 22 + .../include/libultraship/libultra/os.h | 152 + .../include/libultraship/libultra/pfs.h | 134 + .../include/libultraship/libultra/pi.h | 79 + .../include/libultraship/libultra/printf.h | 30 + .../include/libultraship/libultra/r4300.h | 364 ++ .../include/libultraship/libultra/rcp.h | 269 + .../include/libultraship/libultra/rdp.h | 48 + .../include/libultraship/libultra/sptask.h | 65 + .../include/libultraship/libultra/thread.h | 61 + .../include/libultraship/libultra/time.h | 14 + .../include/libultraship/libultra/types.h | 33 + .../include/libultraship/libultra/vi.h | 134 + .../include/libultraship/libultraship.h | 10 + libultraship/include/libultraship/luslog.h | 3 + .../window/gui/GfxDebuggerWindow.h | 33 + libultraship/include/ship/Context.h | 110 + libultraship/include/ship/audio/Audio.h | 37 + .../include/ship/audio/AudioChannelsSetting.h | 16 + libultraship/include/ship/audio/AudioPlayer.h | 84 + .../include/ship/audio/CoreAudioAudioPlayer.h | 37 + .../include/ship/audio/NullAudioPlayer.h | 18 + .../include/ship/audio/SDLAudioPlayer.h | 23 + .../include/ship/audio/SoundMatrixDecoder.h | 120 + .../include/ship/audio/WasapiAudioPlayer.h | 50 + libultraship/include/ship/config/Config.h | 103 + .../include/ship/config/ConsoleVariable.h | 69 + .../ship/controller/controldeck/ControlDeck.h | 50 + .../ship/controller/controldeck/ControlPort.h | 24 + .../controller/controldevice/ControlDevice.h | 14 + .../controldevice/controller/Controller.h | 65 + .../controller/ControllerButton.h | 49 + .../controldevice/controller/ControllerGyro.h | 30 + .../controldevice/controller/ControllerLED.h | 36 + .../controller/ControllerRumble.h | 38 + .../controller/ControllerStick.h | 83 + .../mapping/ControllerAxisDirectionMapping.h | 34 + .../mapping/ControllerButtonMapping.h | 33 + .../mapping/ControllerDefaultMappings.h | 81 + .../mapping/ControllerGyroMapping.h | 32 + .../mapping/ControllerInputMapping.h | 14 + .../controller/mapping/ControllerLEDMapping.h | 36 + .../controller/mapping/ControllerMapping.h | 23 + .../mapping/ControllerRumbleMapping.h | 40 + .../factories/AxisDirectionMappingFactory.h | 26 + .../mapping/factories/ButtonMappingFactory.h | 25 + .../mapping/factories/GyroMappingFactory.h | 13 + .../mapping/factories/LEDMappingFactory.h | 14 + .../mapping/factories/RumbleMappingFactory.h | 18 + .../keyboard/KeyboardKeyToAnyMapping.h | 19 + .../KeyboardKeyToAxisDirectionMapping.h | 17 + .../keyboard/KeyboardKeyToButtonMapping.h | 16 + .../mapping/keyboard/KeyboardScancodes.h | 148 + .../mapping/mouse/MouseButtonToAnyMapping.h | 19 + .../mouse/MouseButtonToAxisDirectionMapping.h | 19 + .../mouse/MouseButtonToButtonMapping.h | 19 + .../mapping/mouse/MouseWheelToAnyMapping.h | 17 + .../mouse/MouseWheelToAxisDirectionMapping.h | 20 + .../mapping/mouse/MouseWheelToButtonMapping.h | 19 + .../controller/mapping/mouse/WheelHandler.h | 36 + .../sdl/SDLAxisDirectionToAnyMapping.h | 20 + .../SDLAxisDirectionToAxisDirectionMapping.h | 18 + .../sdl/SDLAxisDirectionToButtonMapping.h | 17 + .../mapping/sdl/SDLButtonToAnyMapping.h | 20 + .../sdl/SDLButtonToAxisDirectionMapping.h | 17 + .../mapping/sdl/SDLButtonToButtonMapping.h | 16 + .../controller/mapping/sdl/SDLGyroMapping.h | 21 + .../controller/mapping/sdl/SDLLEDMapping.h | 17 + .../controller/mapping/sdl/SDLMapping.h | 10 + .../controller/mapping/sdl/SDLRumbleMapping.h | 25 + .../ConnectedPhysicalDeviceManager.h | 32 + .../physicaldevice/GlobalSDLDeviceSettings.h | 25 + .../physicaldevice/PhysicalDeviceType.h | 7 + .../SDLAddRemoveDeviceEventHandler.h | 17 + .../include/ship/port/mobile/MobileImpl.h | 14 + libultraship/include/ship/resource/File.h | 38 + libultraship/include/ship/resource/Resource.h | 36 + .../include/ship/resource/ResourceFactory.h | 17 + .../ship/resource/ResourceFactoryBinary.h | 11 + .../ship/resource/ResourceFactoryXML.h | 11 + .../include/ship/resource/ResourceLoader.h | 67 + .../include/ship/resource/ResourceManager.h | 146 + .../include/ship/resource/ResourceType.h | 12 + .../include/ship/resource/archive/Archive.h | 59 + .../ship/resource/archive/ArchiveManager.h | 60 + .../ship/resource/archive/FolderArchive.h | 34 + .../ship/resource/archive/O2rArchive.h | 33 + .../ship/resource/archive/OtrArchive.h | 41 + .../ship/resource/factory/BlobFactory.h | 12 + .../ship/resource/factory/JsonFactory.h | 12 + .../ship/resource/factory/ShaderFactory.h | 13 + .../include/ship/resource/type/Blob.h | 17 + .../include/ship/resource/type/Json.h | 20 + .../include/ship/resource/type/Shader.h | 17 + .../include/ship/utils/AppleFolderManager.h | 67 + libultraship/include/ship/utils/StrHash64.h | 93 + .../include/ship/utils/StringHelper.h | 32 + libultraship/include/ship/utils/Utils.h | 20 + .../ship/utils/binarytools/BinaryReader.h | 49 + .../ship/utils/binarytools/BinaryWriter.h | 45 + .../ship/utils/binarytools/BitConverter.h | 181 + .../ship/utils/binarytools/MemoryStream.h | 35 + .../include/ship/utils/binarytools/Stream.h | 33 + .../ship/utils/binarytools/endianness.h | 85 + libultraship/include/ship/utils/color.h | 19 + .../ship/utils/filesystemtools/Directory.h | 53 + .../ship/utils/filesystemtools/DiskFile.h | 80 + .../ship/utils/filesystemtools/FileHelper.h | 98 + .../include/ship/utils/filesystemtools/Path.h | 46 + .../ship/utils/filesystemtools/PathHelper.h | 49 + libultraship/include/ship/utils/glob.h | 10 + libultraship/include/ship/utils/macUtils.h | 11 + libultraship/include/ship/utils/stox.h | 10 + .../include/ship/window/FileDropMgr.h | 26 + .../include/ship/window/MouseStateManager.h | 35 + libultraship/include/ship/window/Window.h | 104 + .../include/ship/window/gui/ConsoleWindow.h | 92 + libultraship/include/ship/window/gui/Fonts.h | 2296 ++++++++ .../include/ship/window/gui/GameOverlay.h | 53 + libultraship/include/ship/window/gui/Gui.h | 147 + .../include/ship/window/gui/GuiElement.h | 34 + .../include/ship/window/gui/GuiMenuBar.h | 20 + .../include/ship/window/gui/GuiWindow.h | 36 + .../ship/window/gui/IconsFontAwesome4.h | 685 +++ .../ship/window/gui/InputEditorWindow.h | 78 + .../include/ship/window/gui/StatsWindow.h | 16 + .../include/ship/window/gui/resource/Font.h | 21 + .../ship/window/gui/resource/FontFactory.h | 12 + .../ship/window/gui/resource/GuiTexture.h | 29 + .../window/gui/resource/GuiTextureFactory.h | 12 + libultraship/src/CMakeLists.txt | 230 + libultraship/src/fast/CMakeLists.txt | 43 + libultraship/src/fast/Fast3dWindow.cpp | 390 ++ .../src/fast/FastMouseStateManager.cpp | 21 + libultraship/src/fast/LICENSE.txt | 21 + libultraship/src/fast/README.md | 30 + .../src/fast/backends/gfx_direct3d11.cpp | 1419 +++++ .../src/fast/backends/gfx_direct3d_common.cpp | 5 + libultraship/src/fast/backends/gfx_dxgi.cpp | 1096 ++++ libultraship/src/fast/backends/gfx_metal.cpp | 1184 ++++ .../src/fast/backends/gfx_metal_shader.cpp | 277 + libultraship/src/fast/backends/gfx_opengl.cpp | 1036 ++++ libultraship/src/fast/backends/gfx_sdl2.cpp | 742 +++ libultraship/src/fast/interpreter.cpp | 4759 +++++++++++++++++ .../resource/factory/DisplayListFactory.cpp | 1157 ++++ .../fast/resource/factory/LightFactory.cpp | 17 + .../fast/resource/factory/MatrixFactory.cpp | 28 + .../fast/resource/factory/TextureFactory.cpp | 51 + .../fast/resource/factory/VertexFactory.cpp | 75 + .../src/fast/resource/type/DisplayList.cpp | 21 + libultraship/src/fast/resource/type/Light.cpp | 11 + .../src/fast/resource/type/Matrix.cpp | 14 + .../src/fast/resource/type/Texture.cpp | 20 + .../src/fast/resource/type/Vertex.cpp | 15 + .../fast/shaders/directx/default.shader.hlsl | 332 ++ .../fast/shaders/metal/default.shader.metal | 290 + .../src/fast/shaders/opengl/default.shader.fs | 211 + .../src/fast/shaders/opengl/default.shader.vs | 79 + libultraship/src/libultraship/CMakeLists.txt | 48 + .../src/libultraship/bridge/audiobridge.cpp | 74 + .../bridge/consolevariablebridge.cpp | 88 + .../libultraship/bridge/controllerbridge.cpp | 14 + .../bridge/crashhandlerbridge.cpp | 7 + .../src/libultraship/bridge/gfxbridge.cpp | 31 + .../libultraship/bridge/gfxdebuggerbridge.cpp | 16 + .../libultraship/bridge/resourcebridge.cpp | 170 + .../src/libultraship/bridge/windowbridge.cpp | 34 + .../controller/controldeck/ControlDeck.cpp | 74 + .../controldevice/controller/Controller.cpp | 75 + .../mapping/ControllerDefaultMappings.cpp | 104 + libultraship/src/libultraship/libultra/os.cpp | 103 + .../src/libultraship/libultra/os_cache.cpp | 16 + .../src/libultraship/libultra/os_eeprom.cpp | 51 + .../src/libultraship/libultra/os_mesg.cpp | 59 + .../src/libultraship/libultra/os_pi.cpp | 21 + .../src/libultraship/libultra/os_time.cpp | 0 .../src/libultraship/libultra/os_vi.cpp | 52 + .../src/libultraship/libultra/os_vm.cpp | 11 + .../window/gui/GfxDebuggerWindow.cpp | 718 +++ libultraship/src/ship/CMakeLists.txt | 98 + libultraship/src/ship/Context.cpp | 527 ++ libultraship/src/ship/audio/Audio.cpp | 91 + libultraship/src/ship/audio/AudioPlayer.cpp | 108 + .../src/ship/audio/CoreAudioAudioPlayer.cpp | 212 + .../src/ship/audio/NullAudioPlayer.cpp | 24 + .../src/ship/audio/SDLAudioPlayer.cpp | 62 + .../src/ship/audio/SoundMatrixDecoder.cpp | 284 + .../src/ship/audio/WasapiAudioPlayer.cpp | 198 + libultraship/src/ship/config/Config.cpp | 367 ++ .../src/ship/config/ConsoleVariable.cpp | 383 ++ .../controller/controldeck/ControlDeck.cpp | 136 + .../controller/controldeck/ControlPort.cpp | 30 + .../controldevice/ControlDevice.cpp | 10 + .../controldevice/controller/Controller.cpp | 202 + .../controller/ControllerButton.cpp | 256 + .../controller/ControllerGyro.cpp | 96 + .../controller/ControllerLED.cpp | 141 + .../controller/ControllerRumble.cpp | 157 + .../controller/ControllerStick.cpp | 474 ++ .../ControllerAxisDirectionMapping.cpp | 27 + .../mapping/ControllerButtonMapping.cpp | 26 + .../mapping/ControllerDefaultMappings.cpp | 133 + .../mapping/ControllerGyroMapping.cpp | 38 + .../mapping/ControllerInputMapping.cpp | 14 + .../mapping/ControllerLEDMapping.cpp | 42 + .../controller/mapping/ControllerMapping.cpp | 17 + .../mapping/ControllerRumbleMapping.cpp | 50 + .../factories/AxisDirectionMappingFactory.cpp | 220 + .../factories/ButtonMappingFactory.cpp | 180 + .../mapping/factories/GyroMappingFactory.cpp | 82 + .../mapping/factories/LEDMappingFactory.cpp | 76 + .../factories/RumbleMappingFactory.cpp | 88 + .../keyboard/KeyboardKeyToAnyMapping.cpp | 46 + .../KeyboardKeyToAxisDirectionMapping.cpp | 66 + .../keyboard/KeyboardKeyToButtonMapping.cpp | 66 + .../mapping/mouse/MouseButtonToAnyMapping.cpp | 31 + .../MouseButtonToAxisDirectionMapping.cpp | 66 + .../mouse/MouseButtonToButtonMapping.cpp | 65 + .../mapping/mouse/MouseWheelToAnyMapping.cpp | 22 + .../MouseWheelToAxisDirectionMapping.cpp | 72 + .../mouse/MouseWheelToButtonMapping.cpp | 66 + .../controller/mapping/mouse/WheelHandler.cpp | 103 + .../sdl/SDLAxisDirectionToAnyMapping.cpp | 56 + ...SDLAxisDirectionToAxisDirectionMapping.cpp | 97 + .../sdl/SDLAxisDirectionToButtonMapping.cpp | 91 + .../mapping/sdl/SDLButtonToAnyMapping.cpp | 72 + .../sdl/SDLButtonToAxisDirectionMapping.cpp | 77 + .../mapping/sdl/SDLButtonToButtonMapping.cpp | 69 + .../controller/mapping/sdl/SDLGyroMapping.cpp | 113 + .../controller/mapping/sdl/SDLLEDMapping.cpp | 64 + .../mapping/sdl/SDLRumbleMapping.cpp | 76 + .../ConnectedPhysicalDeviceManager.cpp | 108 + .../GlobalSDLDeviceSettings.cpp | 59 + .../SDLAddRemoveDeviceEventHandler.cpp | 34 + libultraship/src/ship/install_config.h.in | 2 + .../src/ship/port/mobile/MobileImpl.cpp | 27 + libultraship/src/ship/resource/Resource.cpp | 23 + .../ship/resource/ResourceFactoryBinary.cpp | 20 + .../src/ship/resource/ResourceFactoryXML.cpp | 19 + .../src/ship/resource/ResourceLoader.cpp | 298 ++ .../src/ship/resource/ResourceManager.cpp | 507 ++ .../src/ship/resource/archive/Archive.cpp | 124 + .../ship/resource/archive/ArchiveManager.cpp | 299 ++ .../ship/resource/archive/FolderArchive.cpp | 75 + .../src/ship/resource/archive/O2rArchive.cpp | 151 + .../src/ship/resource/archive/OtrArchive.cpp | 115 + .../src/ship/resource/factory/BlobFactory.cpp | 26 + .../src/ship/resource/factory/JsonFactory.cpp | 20 + .../ship/resource/factory/ShaderFactory.cpp | 18 + libultraship/src/ship/resource/type/Blob.cpp | 14 + libultraship/src/ship/resource/type/Json.cpp | 14 + .../src/ship/resource/type/Shader.cpp | 14 + .../src/ship/utils/AppleFolderManager.mm | 50 + libultraship/src/ship/utils/StrHash64.cpp | 183 + libultraship/src/ship/utils/StringHelper.cpp | 160 + libultraship/src/ship/utils/Utils.cpp | 78 + .../ship/utils/binarytools/BinaryReader.cpp | 205 + .../ship/utils/binarytools/BinaryWriter.cpp | 150 + .../ship/utils/binarytools/MemoryStream.cpp | 89 + .../src/ship/utils/binarytools/Stream.cpp | 5 + libultraship/src/ship/utils/glob.c | 125 + libultraship/src/ship/utils/macUtils.mm | 28 + libultraship/src/ship/utils/stox.cpp | 62 + libultraship/src/ship/window/FileDropMgr.cpp | 118 + .../src/ship/window/MouseStateManager.cpp | 88 + libultraship/src/ship/window/Window.cpp | 135 + .../src/ship/window/gui/ConsoleWindow.cpp | 717 +++ .../src/ship/window/gui/GameOverlay.cpp | 270 + libultraship/src/ship/window/gui/Gui.cpp | 1067 ++++ .../src/ship/window/gui/GuiElement.cpp | 51 + .../src/ship/window/gui/GuiMenuBar.cpp | 54 + .../src/ship/window/gui/GuiWindow.cpp | 207 + .../src/ship/window/gui/InputEditorWindow.cpp | 1409 +++++ .../src/ship/window/gui/StatsWindow.cpp | 37 + .../src/ship/window/gui/resource/Font.cpp | 20 + .../ship/window/gui/resource/FontFactory.cpp | 22 + .../ship/window/gui/resource/GuiTexture.cpp | 20 + .../window/gui/resource/GuiTextureFactory.cpp | 30 + 766 files changed, 107667 insertions(+) create mode 100644 OTRExporter/.gitignore create mode 100644 OTRExporter/CMake/Default.cmake create mode 100644 OTRExporter/CMake/DefaultCXX.cmake create mode 100644 OTRExporter/CMake/Utils.cmake create mode 100644 OTRExporter/CMakeLists.txt create mode 100644 OTRExporter/LICENSE create mode 100644 OTRExporter/OTRExporter/AnimationExporter.cpp create mode 100644 OTRExporter/OTRExporter/AnimationExporter.h create mode 100644 OTRExporter/OTRExporter/ArrayExporter.cpp create mode 100644 OTRExporter/OTRExporter/ArrayExporter.h create mode 100644 OTRExporter/OTRExporter/AudioExporter.cpp create mode 100644 OTRExporter/OTRExporter/AudioExporter.h create mode 100644 OTRExporter/OTRExporter/BackgroundExporter.cpp create mode 100644 OTRExporter/OTRExporter/BackgroundExporter.h create mode 100644 OTRExporter/OTRExporter/BlobExporter.cpp create mode 100644 OTRExporter/OTRExporter/BlobExporter.h create mode 100644 OTRExporter/OTRExporter/CKeyFrameExporter.cpp create mode 100644 OTRExporter/OTRExporter/CKeyFrameExporter.h create mode 100644 OTRExporter/OTRExporter/CMakeLists.txt create mode 100644 OTRExporter/OTRExporter/CollisionExporter.cpp create mode 100644 OTRExporter/OTRExporter/CollisionExporter.h create mode 100644 OTRExporter/OTRExporter/CutsceneExporter.cpp create mode 100644 OTRExporter/OTRExporter/CutsceneExporter.h create mode 100644 OTRExporter/OTRExporter/DisplayListExporter.cpp create mode 100644 OTRExporter/OTRExporter/DisplayListExporter.h create mode 100644 OTRExporter/OTRExporter/Exporter.cpp create mode 100644 OTRExporter/OTRExporter/Exporter.h create mode 100644 OTRExporter/OTRExporter/ExporterArchive.cpp create mode 100644 OTRExporter/OTRExporter/ExporterArchive.h create mode 100644 OTRExporter/OTRExporter/ExporterArchiveO2R.cpp create mode 100644 OTRExporter/OTRExporter/ExporterArchiveO2R.h create mode 100644 OTRExporter/OTRExporter/ExporterArchiveOTR.cpp create mode 100644 OTRExporter/OTRExporter/ExporterArchiveOTR.h create mode 100644 OTRExporter/OTRExporter/Main.cpp create mode 100644 OTRExporter/OTRExporter/Main.h create mode 100644 OTRExporter/OTRExporter/MtxExporter.cpp create mode 100644 OTRExporter/OTRExporter/MtxExporter.h create mode 100644 OTRExporter/OTRExporter/PathExporter.cpp create mode 100644 OTRExporter/OTRExporter/PathExporter.h create mode 100644 OTRExporter/OTRExporter/PlayerAnimationExporter.cpp create mode 100644 OTRExporter/OTRExporter/PlayerAnimationExporter.h create mode 100644 OTRExporter/OTRExporter/RoomExporter.cpp create mode 100644 OTRExporter/OTRExporter/RoomExporter.h create mode 100644 OTRExporter/OTRExporter/SkeletonExporter.cpp create mode 100644 OTRExporter/OTRExporter/SkeletonExporter.h create mode 100644 OTRExporter/OTRExporter/SkeletonLimbExporter.cpp create mode 100644 OTRExporter/OTRExporter/SkeletonLimbExporter.h create mode 100644 OTRExporter/OTRExporter/TextExporter.cpp create mode 100644 OTRExporter/OTRExporter/TextExporter.h create mode 100644 OTRExporter/OTRExporter/TextMMExporter.cpp create mode 100644 OTRExporter/OTRExporter/TextMMExporter.h create mode 100644 OTRExporter/OTRExporter/TextureAnimationExporter.cpp create mode 100644 OTRExporter/OTRExporter/TextureAnimationExporter.h create mode 100644 OTRExporter/OTRExporter/TextureExporter.cpp create mode 100644 OTRExporter/OTRExporter/TextureExporter.h create mode 100644 OTRExporter/OTRExporter/VersionInfo.cpp create mode 100644 OTRExporter/OTRExporter/VersionInfo.h create mode 100644 OTRExporter/OTRExporter/VtxExporter.cpp create mode 100644 OTRExporter/OTRExporter/VtxExporter.h create mode 100644 OTRExporter/OTRExporter/command_macros_base.h create mode 100644 OTRExporter/OTRExporter/z64cutscene.h create mode 100644 OTRExporter/OTRExporter/z64cutscene_commands.h create mode 100644 OTRExporter/assets/accessibility/texts/filechoose_eng.json create mode 100644 OTRExporter/assets/accessibility/texts/filechoose_fra.json create mode 100644 OTRExporter/assets/accessibility/texts/filechoose_ger.json create mode 100644 OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json create mode 100644 OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json create mode 100644 OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json create mode 100644 OTRExporter/assets/accessibility/texts/misc_eng.json create mode 100644 OTRExporter/assets/accessibility/texts/misc_fra.json create mode 100644 OTRExporter/assets/accessibility/texts/misc_ger.json create mode 100644 OTRExporter/assets/accessibility/texts/scenes_eng.json create mode 100644 OTRExporter/assets/accessibility/texts/scenes_fra.json create mode 100644 OTRExporter/assets/accessibility/texts/scenes_ger.json create mode 100644 OTRExporter/assets/fonts/Fipps-Regular.otf create mode 100644 OTRExporter/assets/fonts/PressStart2P-Regular.ttf create mode 100644 OTRExporter/assets/helpers/cosmetics/gEndGrayscaleAndEndDlistDL create mode 100644 OTRExporter/assets/objects/object_box/gChristmasGreenTreasureChestFrontTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gChristmasGreenTreasureChestSideAndTopTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gChristmasRedTreasureChestFrontTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gChristmasRedTreasureChestSideAndTopTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gGoldTreasureChestFrontTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gGoldTreasureChestSideAndTopTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gKeyTreasureChestFrontTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gKeyTreasureChestSideAndTopTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gSkullTreasureChestFrontTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_box/gSkullTreasureChestSideAndTopTex.rgb5a1.png create mode 100644 OTRExporter/assets/objects/object_mag/gTitleBossRushSubtitleTex.rgba32.png create mode 100644 OTRExporter/assets/objects/object_mag/gTitleRandomizerSubtitleTex.rgba32.png create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges create mode 100644 OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_completed/noise_tex create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_piece_0/noise_tex create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_piece_1/noise_tex create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface create mode 100644 OTRExporter/assets/objects/object_triforce_piece_2/noise_tex create mode 100644 OTRExporter/assets/scenes/nonmq/syotes_scene/syotes_room_0 create mode 100644 OTRExporter/assets/textures/buttons/ABtn.png create mode 100644 OTRExporter/assets/textures/buttons/BBtn.png create mode 100644 OTRExporter/assets/textures/buttons/CDown.png create mode 100644 OTRExporter/assets/textures/buttons/CLeft.png create mode 100644 OTRExporter/assets/textures/buttons/CRight.png create mode 100644 OTRExporter/assets/textures/buttons/CUp.png create mode 100644 OTRExporter/assets/textures/buttons/LBtn.png create mode 100644 OTRExporter/assets/textures/buttons/RBtn.png create mode 100644 OTRExporter/assets/textures/buttons/StartBtn.png create mode 100644 OTRExporter/assets/textures/buttons/ZBtn.png create mode 100644 OTRExporter/assets/textures/icons/gIcon.png create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/SoHShiny create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_0 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_1 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_2 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_3 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_0 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_1 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_2 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_3 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_001 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_002 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_003 create mode 100644 OTRExporter/assets/textures/nintendo_rogo_static/nintendo_rogo_static_Tex_LUS_000000 create mode 100644 OTRExporter/assets/textures/parameter_static/gArrowDown.ia16.png create mode 100644 OTRExporter/assets/textures/parameter_static/gArrowUp.ia16.png create mode 100644 OTRExporter/assets/textures/parameter_static/gDPad.ia16.png create mode 100644 OTRExporter/assets/textures/parameter_static/gTriforcePiece.rgba32.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsENGTex.ia8.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsFRATex.ia8.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsGERTex.ia8.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelMQButtonTex.ia16.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelPleaseChooseAQuestENGTex.ia8.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelPleaseChooseAQuestFRATex.ia8.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelPleaseChooseAQuestGERTex.ia8.png create mode 100644 OTRExporter/assets/textures/title_static/gFileSelRANDButtonTex.ia16.png create mode 100644 OTRExporter/assets/textures/virtual/gEmptyTexture.rgba32.png create mode 100755 OTRExporter/extract_assets.py create mode 100644 OTRExporter/extract_baserom.py create mode 100644 OTRExporter/offsets.json create mode 100644 OTRExporter/rom_chooser.py create mode 100644 OTRExporter/rom_info.py create mode 100644 ZAPDTR/.clang-format create mode 100644 ZAPDTR/.github/workflows/main.yml create mode 100644 ZAPDTR/.gitignore create mode 100644 ZAPDTR/ExporterTest/CollisionExporter.cpp create mode 100644 ZAPDTR/ExporterTest/CollisionExporter.h create mode 100644 ZAPDTR/ExporterTest/Main.cpp create mode 100644 ZAPDTR/ExporterTest/RoomExporter.cpp create mode 100644 ZAPDTR/ExporterTest/RoomExporter.h create mode 100644 ZAPDTR/ExporterTest/TextureExporter.cpp create mode 100644 ZAPDTR/ExporterTest/TextureExporter.h create mode 100644 ZAPDTR/LICENSE create mode 100644 ZAPDTR/README.md create mode 100644 ZAPDTR/ZAPD/CMakeLists.txt create mode 100644 ZAPDTR/ZAPD/CRC32.h create mode 100644 ZAPDTR/ZAPD/CrashHandler.cpp create mode 100644 ZAPDTR/ZAPD/CrashHandler.h create mode 100644 ZAPDTR/ZAPD/Declaration.cpp create mode 100644 ZAPDTR/ZAPD/Declaration.h create mode 100644 ZAPDTR/ZAPD/ExecutableMain.cpp create mode 100644 ZAPDTR/ZAPD/ExporterSet.h create mode 100644 ZAPDTR/ZAPD/FileWorker.cpp create mode 100644 ZAPDTR/ZAPD/FileWorker.h create mode 100644 ZAPDTR/ZAPD/GameConfig.cpp create mode 100644 ZAPDTR/ZAPD/GameConfig.h create mode 100644 ZAPDTR/ZAPD/Globals.cpp create mode 100644 ZAPDTR/ZAPD/Globals.h create mode 100644 ZAPDTR/ZAPD/ImageBackend.cpp create mode 100644 ZAPDTR/ZAPD/ImageBackend.h create mode 100644 ZAPDTR/ZAPD/Main.cpp create mode 100644 ZAPDTR/ZAPD/NuGet/libpng.static.txt create mode 100644 ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.cpp create mode 100644 ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.h create mode 100644 ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.cpp create mode 100644 ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.h create mode 100644 ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.cpp create mode 100644 ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.h create mode 100644 ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.cpp create mode 100644 ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.h create mode 100644 ZAPDTR/ZAPD/OutputFormatter.cpp create mode 100644 ZAPDTR/ZAPD/OutputFormatter.h create mode 100644 ZAPDTR/ZAPD/Overlays/ZOverlay.cpp create mode 100644 ZAPDTR/ZAPD/Overlays/ZOverlay.h create mode 100644 ZAPDTR/ZAPD/Utils/BinaryReader.cpp create mode 100644 ZAPDTR/ZAPD/Utils/BinaryReader.h create mode 100644 ZAPDTR/ZAPD/Utils/BinaryWriter.cpp create mode 100644 ZAPDTR/ZAPD/Utils/BinaryWriter.h create mode 100644 ZAPDTR/ZAPD/Utils/BitConverter.h create mode 100644 ZAPDTR/ZAPD/Utils/Directory.h create mode 100644 ZAPDTR/ZAPD/Utils/DiskFile.h create mode 100644 ZAPDTR/ZAPD/Utils/MemoryStream.cpp create mode 100644 ZAPDTR/ZAPD/Utils/MemoryStream.h create mode 100644 ZAPDTR/ZAPD/Utils/Path.h create mode 100644 ZAPDTR/ZAPD/Utils/Stream.h create mode 100644 ZAPDTR/ZAPD/Utils/StringHelper.cpp create mode 100644 ZAPDTR/ZAPD/Utils/StringHelper.h create mode 100644 ZAPDTR/ZAPD/Utils/vt.h create mode 100644 ZAPDTR/ZAPD/WarningHandler.cpp create mode 100644 ZAPDTR/ZAPD/WarningHandler.h create mode 100644 ZAPDTR/ZAPD/ZActorList.cpp create mode 100644 ZAPDTR/ZAPD/ZActorList.h create mode 100644 ZAPDTR/ZAPD/ZAnimation.cpp create mode 100644 ZAPDTR/ZAPD/ZAnimation.h create mode 100644 ZAPDTR/ZAPD/ZArray.cpp create mode 100644 ZAPDTR/ZAPD/ZArray.h create mode 100644 ZAPDTR/ZAPD/ZAudio.cpp create mode 100644 ZAPDTR/ZAPD/ZAudio.h create mode 100644 ZAPDTR/ZAPD/ZAudioDecode.cpp create mode 100644 ZAPDTR/ZAPD/ZBackground.cpp create mode 100644 ZAPDTR/ZAPD/ZBackground.h create mode 100644 ZAPDTR/ZAPD/ZBlob.cpp create mode 100644 ZAPDTR/ZAPD/ZBlob.h create mode 100644 ZAPDTR/ZAPD/ZCKeyFrame.cpp create mode 100644 ZAPDTR/ZAPD/ZCKeyFrame.h create mode 100644 ZAPDTR/ZAPD/ZCKeyFrameAnim.cpp create mode 100644 ZAPDTR/ZAPD/ZCkeyFrameAnim.h create mode 100644 ZAPDTR/ZAPD/ZCollision.cpp create mode 100644 ZAPDTR/ZAPD/ZCollision.h create mode 100644 ZAPDTR/ZAPD/ZCollisionPoly.cpp create mode 100644 ZAPDTR/ZAPD/ZCollisionPoly.h create mode 100644 ZAPDTR/ZAPD/ZCutscene.cpp create mode 100644 ZAPDTR/ZAPD/ZCutscene.h create mode 100644 ZAPDTR/ZAPD/ZDisplayList.cpp create mode 100644 ZAPDTR/ZAPD/ZDisplayList.h create mode 100644 ZAPDTR/ZAPD/ZFile.cpp create mode 100644 ZAPDTR/ZAPD/ZFile.h create mode 100644 ZAPDTR/ZAPD/ZLimb.cpp create mode 100644 ZAPDTR/ZAPD/ZLimb.h create mode 100644 ZAPDTR/ZAPD/ZMtx.cpp create mode 100644 ZAPDTR/ZAPD/ZMtx.h create mode 100644 ZAPDTR/ZAPD/ZPath.cpp create mode 100644 ZAPDTR/ZAPD/ZPath.h create mode 100644 ZAPDTR/ZAPD/ZPlayerAnimationData.cpp create mode 100644 ZAPDTR/ZAPD/ZPlayerAnimationData.h create mode 100644 ZAPDTR/ZAPD/ZPointer.cpp create mode 100644 ZAPDTR/ZAPD/ZPointer.h create mode 100644 ZAPDTR/ZAPD/ZResource.cpp create mode 100644 ZAPDTR/ZAPD/ZResource.h create mode 100644 ZAPDTR/ZAPD/ZRom.cpp create mode 100644 ZAPDTR/ZAPD/ZRom.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetWind.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetWind.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/Unused09.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/Unused09.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.h create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.h create mode 100644 ZAPDTR/ZAPD/ZRoom/ZNames.h create mode 100644 ZAPDTR/ZAPD/ZRoom/ZRoom.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/ZRoom.h create mode 100644 ZAPDTR/ZAPD/ZRoom/ZRoomCommand.cpp create mode 100644 ZAPDTR/ZAPD/ZRoom/ZRoomCommand.h create mode 100644 ZAPDTR/ZAPD/ZScalar.cpp create mode 100644 ZAPDTR/ZAPD/ZScalar.h create mode 100644 ZAPDTR/ZAPD/ZSkeleton.cpp create mode 100644 ZAPDTR/ZAPD/ZSkeleton.h create mode 100644 ZAPDTR/ZAPD/ZString.cpp create mode 100644 ZAPDTR/ZAPD/ZString.h create mode 100644 ZAPDTR/ZAPD/ZSurfaceType.cpp create mode 100644 ZAPDTR/ZAPD/ZSurfaceType.h create mode 100644 ZAPDTR/ZAPD/ZSymbol.cpp create mode 100644 ZAPDTR/ZAPD/ZSymbol.h create mode 100644 ZAPDTR/ZAPD/ZText.cpp create mode 100644 ZAPDTR/ZAPD/ZText.h create mode 100644 ZAPDTR/ZAPD/ZTextMM.cpp create mode 100644 ZAPDTR/ZAPD/ZTextMM.h create mode 100644 ZAPDTR/ZAPD/ZTexture.cpp create mode 100644 ZAPDTR/ZAPD/ZTexture.h create mode 100644 ZAPDTR/ZAPD/ZTextureAnimation.cpp create mode 100644 ZAPDTR/ZAPD/ZTextureAnimation.h create mode 100644 ZAPDTR/ZAPD/ZVector.cpp create mode 100644 ZAPDTR/ZAPD/ZVector.h create mode 100644 ZAPDTR/ZAPD/ZVtx.cpp create mode 100644 ZAPDTR/ZAPD/ZVtx.h create mode 100644 ZAPDTR/ZAPD/ZWaterbox.cpp create mode 100644 ZAPDTR/ZAPD/ZWaterbox.h create mode 100644 ZAPDTR/ZAPD/any/any/zlib.static.txt create mode 100644 ZAPDTR/ZAPD/ctpl_stl.h create mode 100644 ZAPDTR/ZAPD/genbuildinfo.py create mode 100644 ZAPDTR/ZAPD/packages.config create mode 100644 ZAPDTR/ZAPD/pathconf.c create mode 100644 ZAPDTR/ZAPD/yaz0/readwrite.h create mode 100644 ZAPDTR/ZAPD/yaz0/yaz0.cpp create mode 100644 ZAPDTR/ZAPD/yaz0/yaz0.h create mode 100755 ZAPDTR/copycheck.py create mode 100644 ZAPDTR/docs/zapd_extraction_xml_reference.md create mode 100644 ZAPDTR/docs/zapd_xml_spec.md create mode 100644 ZAPDTR/lib/libgfxd/.gitrepo create mode 100644 ZAPDTR/lib/libgfxd/LICENSE create mode 100644 ZAPDTR/lib/libgfxd/README.md create mode 100644 ZAPDTR/lib/libgfxd/gbi.h create mode 100644 ZAPDTR/lib/libgfxd/gfxd.c create mode 100644 ZAPDTR/lib/libgfxd/gfxd.h create mode 100644 ZAPDTR/lib/libgfxd/priv.h create mode 100644 ZAPDTR/lib/libgfxd/uc.c create mode 100644 ZAPDTR/lib/libgfxd/uc_argfn.c create mode 100644 ZAPDTR/lib/libgfxd/uc_argtbl.c create mode 100644 ZAPDTR/lib/libgfxd/uc_f3d.c create mode 100644 ZAPDTR/lib/libgfxd/uc_f3db.c create mode 100644 ZAPDTR/lib/libgfxd/uc_f3dex.c create mode 100644 ZAPDTR/lib/libgfxd/uc_f3dex2.c create mode 100644 ZAPDTR/lib/libgfxd/uc_f3dexb.c create mode 100644 ZAPDTR/lib/libgfxd/uc_macrofn.c create mode 100644 ZAPDTR/lib/libgfxd/uc_macrotbl.c create mode 100644 ZAPDTR/lib/tinyxml2/tinyxml2.cpp create mode 100644 ZAPDTR/lib/tinyxml2/tinyxml2.h create mode 100644 libultraship/.clang-format create mode 100644 libultraship/.clang-tidy create mode 100644 libultraship/.github/workflows/apt-deps.txt create mode 100644 libultraship/.github/workflows/brew-deps.txt create mode 100644 libultraship/.github/workflows/tidy-format-validation.yml create mode 100644 libultraship/.github/workflows/tidy-result-publish.yml create mode 100644 libultraship/.gitignore create mode 100644 libultraship/CMakeLists.txt create mode 100644 libultraship/CODE_OF_CONDUCT.md create mode 100644 libultraship/CONTRIBUTING.md create mode 100644 libultraship/LICENSE create mode 100644 libultraship/README.md create mode 100644 libultraship/cmake/Default.cmake create mode 100644 libultraship/cmake/DefaultCXX.cmake create mode 100644 libultraship/cmake/HandleCompilerRT.cmake create mode 100644 libultraship/cmake/Utils.cmake create mode 100644 libultraship/cmake/automate-vcpkg.cmake create mode 100644 libultraship/cmake/cvars.cmake create mode 100644 libultraship/cmake/dependencies/android.cmake create mode 100644 libultraship/cmake/dependencies/common.cmake create mode 100644 libultraship/cmake/dependencies/git-patch.cmake create mode 100644 libultraship/cmake/dependencies/ios.cmake create mode 100644 libultraship/cmake/dependencies/linux.cmake create mode 100644 libultraship/cmake/dependencies/mac.cmake create mode 100644 libultraship/cmake/dependencies/openbsd.cmake create mode 100644 libultraship/cmake/dependencies/patches/imgui-fixes-and-config.patch create mode 100644 libultraship/cmake/dependencies/patches/stormlib-optimizations.patch create mode 100644 libultraship/cmake/dependencies/windows-vcpkg.cmake create mode 100644 libultraship/cmake/dependencies/windows.cmake create mode 100644 libultraship/cmake/ios-toolchain-populate.cmake create mode 100644 libultraship/include/fast/Fast3dWindow.h create mode 100644 libultraship/include/fast/FastMouseStateManager.h create mode 100644 libultraship/include/fast/LICENSE.txt create mode 100644 libultraship/include/fast/README.md create mode 100644 libultraship/include/fast/backends/gfx_direct3d11.h create mode 100644 libultraship/include/fast/backends/gfx_direct3d_common.h create mode 100644 libultraship/include/fast/backends/gfx_dxgi.h create mode 100644 libultraship/include/fast/backends/gfx_metal.h create mode 100644 libultraship/include/fast/backends/gfx_metal_shader.h create mode 100644 libultraship/include/fast/backends/gfx_opengl.h create mode 100644 libultraship/include/fast/backends/gfx_rendering_api.h create mode 100644 libultraship/include/fast/backends/gfx_screen_config.h create mode 100644 libultraship/include/fast/backends/gfx_sdl.h create mode 100644 libultraship/include/fast/backends/gfx_window_manager_api.h create mode 100644 libultraship/include/fast/f3dex.h create mode 100644 libultraship/include/fast/f3dex2.h create mode 100644 libultraship/include/fast/interpreter.h create mode 100644 libultraship/include/fast/lus_gbi.h create mode 100644 libultraship/include/fast/resource/ResourceType.h create mode 100644 libultraship/include/fast/resource/factory/DisplayListFactory.h create mode 100644 libultraship/include/fast/resource/factory/LightFactory.h create mode 100644 libultraship/include/fast/resource/factory/MatrixFactory.h create mode 100644 libultraship/include/fast/resource/factory/TextureFactory.h create mode 100644 libultraship/include/fast/resource/factory/VertexFactory.h create mode 100644 libultraship/include/fast/resource/type/DisplayList.h create mode 100644 libultraship/include/fast/resource/type/Light.h create mode 100644 libultraship/include/fast/resource/type/Matrix.h create mode 100644 libultraship/include/fast/resource/type/Texture.h create mode 100644 libultraship/include/fast/resource/type/Vertex.h create mode 100644 libultraship/include/fast/types.h create mode 100644 libultraship/include/fast/ucodehandlers.h create mode 100644 libultraship/include/libultraship/bridge.h create mode 100644 libultraship/include/libultraship/bridge/audiobridge.h create mode 100644 libultraship/include/libultraship/bridge/consolevariablebridge.h create mode 100644 libultraship/include/libultraship/bridge/controllerbridge.h create mode 100644 libultraship/include/libultraship/bridge/crashhandlerbridge.h create mode 100644 libultraship/include/libultraship/bridge/gfxbridge.h create mode 100644 libultraship/include/libultraship/bridge/gfxdebuggerbridge.h create mode 100644 libultraship/include/libultraship/bridge/resourcebridge.h create mode 100644 libultraship/include/libultraship/bridge/windowbridge.h create mode 100644 libultraship/include/libultraship/classes.h create mode 100644 libultraship/include/libultraship/color.h create mode 100644 libultraship/include/libultraship/controller/controldeck/ControlDeck.h create mode 100644 libultraship/include/libultraship/controller/controldevice/controller/Controller.h create mode 100644 libultraship/include/libultraship/controller/controldevice/controller/mapping/ControllerDefaultMappings.h create mode 100644 libultraship/include/libultraship/libultra.h create mode 100644 libultraship/include/libultraship/libultra/abi.h create mode 100644 libultraship/include/libultraship/libultra/controller.h create mode 100644 libultraship/include/libultraship/libultra/convert.h create mode 100644 libultraship/include/libultraship/libultra/eeprom.h create mode 100644 libultraship/include/libultraship/libultra/exception.h create mode 100644 libultraship/include/libultraship/libultra/gbi.h create mode 100644 libultraship/include/libultraship/libultra/gs2dex.h create mode 100644 libultraship/include/libultraship/libultra/gu.h create mode 100644 libultraship/include/libultraship/libultra/internal.h create mode 100644 libultraship/include/libultraship/libultra/interrupt.h create mode 100644 libultraship/include/libultraship/libultra/mbi.h create mode 100644 libultraship/include/libultraship/libultra/message.h create mode 100644 libultraship/include/libultraship/libultra/motor.h create mode 100644 libultraship/include/libultraship/libultra/os.h create mode 100644 libultraship/include/libultraship/libultra/pfs.h create mode 100644 libultraship/include/libultraship/libultra/pi.h create mode 100644 libultraship/include/libultraship/libultra/printf.h create mode 100644 libultraship/include/libultraship/libultra/r4300.h create mode 100644 libultraship/include/libultraship/libultra/rcp.h create mode 100644 libultraship/include/libultraship/libultra/rdp.h create mode 100644 libultraship/include/libultraship/libultra/sptask.h create mode 100644 libultraship/include/libultraship/libultra/thread.h create mode 100644 libultraship/include/libultraship/libultra/time.h create mode 100644 libultraship/include/libultraship/libultra/types.h create mode 100644 libultraship/include/libultraship/libultra/vi.h create mode 100644 libultraship/include/libultraship/libultraship.h create mode 100644 libultraship/include/libultraship/luslog.h create mode 100644 libultraship/include/libultraship/window/gui/GfxDebuggerWindow.h create mode 100644 libultraship/include/ship/Context.h create mode 100644 libultraship/include/ship/audio/Audio.h create mode 100644 libultraship/include/ship/audio/AudioChannelsSetting.h create mode 100644 libultraship/include/ship/audio/AudioPlayer.h create mode 100644 libultraship/include/ship/audio/CoreAudioAudioPlayer.h create mode 100644 libultraship/include/ship/audio/NullAudioPlayer.h create mode 100644 libultraship/include/ship/audio/SDLAudioPlayer.h create mode 100644 libultraship/include/ship/audio/SoundMatrixDecoder.h create mode 100644 libultraship/include/ship/audio/WasapiAudioPlayer.h create mode 100644 libultraship/include/ship/config/Config.h create mode 100644 libultraship/include/ship/config/ConsoleVariable.h create mode 100644 libultraship/include/ship/controller/controldeck/ControlDeck.h create mode 100644 libultraship/include/ship/controller/controldeck/ControlPort.h create mode 100644 libultraship/include/ship/controller/controldevice/ControlDevice.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/Controller.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/ControllerButton.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/ControllerGyro.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/ControllerLED.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/ControllerRumble.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/ControllerStick.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerButtonMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerDefaultMappings.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerGyroMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerInputMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerLEDMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/ControllerRumbleMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/factories/AxisDirectionMappingFactory.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/factories/ButtonMappingFactory.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/factories/GyroMappingFactory.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/factories/LEDMappingFactory.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/factories/RumbleMappingFactory.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/keyboard/KeyboardKeyToAnyMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/keyboard/KeyboardKeyToAxisDirectionMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/keyboard/KeyboardKeyToButtonMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/mouse/MouseButtonToAnyMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/mouse/MouseButtonToAxisDirectionMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/mouse/MouseButtonToButtonMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/mouse/MouseWheelToAnyMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/mouse/MouseWheelToAxisDirectionMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/mouse/MouseWheelToButtonMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/mouse/WheelHandler.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToAnyMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToAxisDirectionMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToButtonMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLButtonToAnyMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLButtonToAxisDirectionMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLButtonToButtonMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLGyroMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLLEDMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLMapping.h create mode 100644 libultraship/include/ship/controller/controldevice/controller/mapping/sdl/SDLRumbleMapping.h create mode 100644 libultraship/include/ship/controller/physicaldevice/ConnectedPhysicalDeviceManager.h create mode 100644 libultraship/include/ship/controller/physicaldevice/GlobalSDLDeviceSettings.h create mode 100644 libultraship/include/ship/controller/physicaldevice/PhysicalDeviceType.h create mode 100644 libultraship/include/ship/controller/physicaldevice/SDLAddRemoveDeviceEventHandler.h create mode 100644 libultraship/include/ship/port/mobile/MobileImpl.h create mode 100644 libultraship/include/ship/resource/File.h create mode 100644 libultraship/include/ship/resource/Resource.h create mode 100644 libultraship/include/ship/resource/ResourceFactory.h create mode 100644 libultraship/include/ship/resource/ResourceFactoryBinary.h create mode 100644 libultraship/include/ship/resource/ResourceFactoryXML.h create mode 100644 libultraship/include/ship/resource/ResourceLoader.h create mode 100644 libultraship/include/ship/resource/ResourceManager.h create mode 100644 libultraship/include/ship/resource/ResourceType.h create mode 100644 libultraship/include/ship/resource/archive/Archive.h create mode 100644 libultraship/include/ship/resource/archive/ArchiveManager.h create mode 100644 libultraship/include/ship/resource/archive/FolderArchive.h create mode 100644 libultraship/include/ship/resource/archive/O2rArchive.h create mode 100644 libultraship/include/ship/resource/archive/OtrArchive.h create mode 100644 libultraship/include/ship/resource/factory/BlobFactory.h create mode 100644 libultraship/include/ship/resource/factory/JsonFactory.h create mode 100644 libultraship/include/ship/resource/factory/ShaderFactory.h create mode 100644 libultraship/include/ship/resource/type/Blob.h create mode 100644 libultraship/include/ship/resource/type/Json.h create mode 100644 libultraship/include/ship/resource/type/Shader.h create mode 100644 libultraship/include/ship/utils/AppleFolderManager.h create mode 100644 libultraship/include/ship/utils/StrHash64.h create mode 100644 libultraship/include/ship/utils/StringHelper.h create mode 100644 libultraship/include/ship/utils/Utils.h create mode 100644 libultraship/include/ship/utils/binarytools/BinaryReader.h create mode 100644 libultraship/include/ship/utils/binarytools/BinaryWriter.h create mode 100644 libultraship/include/ship/utils/binarytools/BitConverter.h create mode 100644 libultraship/include/ship/utils/binarytools/MemoryStream.h create mode 100644 libultraship/include/ship/utils/binarytools/Stream.h create mode 100644 libultraship/include/ship/utils/binarytools/endianness.h create mode 100644 libultraship/include/ship/utils/color.h create mode 100644 libultraship/include/ship/utils/filesystemtools/Directory.h create mode 100644 libultraship/include/ship/utils/filesystemtools/DiskFile.h create mode 100644 libultraship/include/ship/utils/filesystemtools/FileHelper.h create mode 100644 libultraship/include/ship/utils/filesystemtools/Path.h create mode 100644 libultraship/include/ship/utils/filesystemtools/PathHelper.h create mode 100644 libultraship/include/ship/utils/glob.h create mode 100644 libultraship/include/ship/utils/macUtils.h create mode 100644 libultraship/include/ship/utils/stox.h create mode 100644 libultraship/include/ship/window/FileDropMgr.h create mode 100644 libultraship/include/ship/window/MouseStateManager.h create mode 100644 libultraship/include/ship/window/Window.h create mode 100644 libultraship/include/ship/window/gui/ConsoleWindow.h create mode 100644 libultraship/include/ship/window/gui/Fonts.h create mode 100644 libultraship/include/ship/window/gui/GameOverlay.h create mode 100644 libultraship/include/ship/window/gui/Gui.h create mode 100644 libultraship/include/ship/window/gui/GuiElement.h create mode 100644 libultraship/include/ship/window/gui/GuiMenuBar.h create mode 100644 libultraship/include/ship/window/gui/GuiWindow.h create mode 100644 libultraship/include/ship/window/gui/IconsFontAwesome4.h create mode 100644 libultraship/include/ship/window/gui/InputEditorWindow.h create mode 100644 libultraship/include/ship/window/gui/StatsWindow.h create mode 100644 libultraship/include/ship/window/gui/resource/Font.h create mode 100644 libultraship/include/ship/window/gui/resource/FontFactory.h create mode 100644 libultraship/include/ship/window/gui/resource/GuiTexture.h create mode 100644 libultraship/include/ship/window/gui/resource/GuiTextureFactory.h create mode 100644 libultraship/src/CMakeLists.txt create mode 100644 libultraship/src/fast/CMakeLists.txt create mode 100644 libultraship/src/fast/Fast3dWindow.cpp create mode 100644 libultraship/src/fast/FastMouseStateManager.cpp create mode 100644 libultraship/src/fast/LICENSE.txt create mode 100644 libultraship/src/fast/README.md create mode 100644 libultraship/src/fast/backends/gfx_direct3d11.cpp create mode 100644 libultraship/src/fast/backends/gfx_direct3d_common.cpp create mode 100644 libultraship/src/fast/backends/gfx_dxgi.cpp create mode 100644 libultraship/src/fast/backends/gfx_metal.cpp create mode 100644 libultraship/src/fast/backends/gfx_metal_shader.cpp create mode 100644 libultraship/src/fast/backends/gfx_opengl.cpp create mode 100644 libultraship/src/fast/backends/gfx_sdl2.cpp create mode 100644 libultraship/src/fast/interpreter.cpp create mode 100644 libultraship/src/fast/resource/factory/DisplayListFactory.cpp create mode 100644 libultraship/src/fast/resource/factory/LightFactory.cpp create mode 100644 libultraship/src/fast/resource/factory/MatrixFactory.cpp create mode 100644 libultraship/src/fast/resource/factory/TextureFactory.cpp create mode 100644 libultraship/src/fast/resource/factory/VertexFactory.cpp create mode 100644 libultraship/src/fast/resource/type/DisplayList.cpp create mode 100644 libultraship/src/fast/resource/type/Light.cpp create mode 100644 libultraship/src/fast/resource/type/Matrix.cpp create mode 100644 libultraship/src/fast/resource/type/Texture.cpp create mode 100644 libultraship/src/fast/resource/type/Vertex.cpp create mode 100644 libultraship/src/fast/shaders/directx/default.shader.hlsl create mode 100644 libultraship/src/fast/shaders/metal/default.shader.metal create mode 100644 libultraship/src/fast/shaders/opengl/default.shader.fs create mode 100644 libultraship/src/fast/shaders/opengl/default.shader.vs create mode 100644 libultraship/src/libultraship/CMakeLists.txt create mode 100644 libultraship/src/libultraship/bridge/audiobridge.cpp create mode 100644 libultraship/src/libultraship/bridge/consolevariablebridge.cpp create mode 100644 libultraship/src/libultraship/bridge/controllerbridge.cpp create mode 100644 libultraship/src/libultraship/bridge/crashhandlerbridge.cpp create mode 100644 libultraship/src/libultraship/bridge/gfxbridge.cpp create mode 100644 libultraship/src/libultraship/bridge/gfxdebuggerbridge.cpp create mode 100644 libultraship/src/libultraship/bridge/resourcebridge.cpp create mode 100644 libultraship/src/libultraship/bridge/windowbridge.cpp create mode 100644 libultraship/src/libultraship/controller/controldeck/ControlDeck.cpp create mode 100644 libultraship/src/libultraship/controller/controldevice/controller/Controller.cpp create mode 100644 libultraship/src/libultraship/controller/controldevice/controller/mapping/ControllerDefaultMappings.cpp create mode 100644 libultraship/src/libultraship/libultra/os.cpp create mode 100644 libultraship/src/libultraship/libultra/os_cache.cpp create mode 100644 libultraship/src/libultraship/libultra/os_eeprom.cpp create mode 100644 libultraship/src/libultraship/libultra/os_mesg.cpp create mode 100644 libultraship/src/libultraship/libultra/os_pi.cpp create mode 100644 libultraship/src/libultraship/libultra/os_time.cpp create mode 100644 libultraship/src/libultraship/libultra/os_vi.cpp create mode 100644 libultraship/src/libultraship/libultra/os_vm.cpp create mode 100644 libultraship/src/libultraship/window/gui/GfxDebuggerWindow.cpp create mode 100644 libultraship/src/ship/CMakeLists.txt create mode 100644 libultraship/src/ship/Context.cpp create mode 100644 libultraship/src/ship/audio/Audio.cpp create mode 100644 libultraship/src/ship/audio/AudioPlayer.cpp create mode 100644 libultraship/src/ship/audio/CoreAudioAudioPlayer.cpp create mode 100644 libultraship/src/ship/audio/NullAudioPlayer.cpp create mode 100644 libultraship/src/ship/audio/SDLAudioPlayer.cpp create mode 100644 libultraship/src/ship/audio/SoundMatrixDecoder.cpp create mode 100644 libultraship/src/ship/audio/WasapiAudioPlayer.cpp create mode 100644 libultraship/src/ship/config/Config.cpp create mode 100644 libultraship/src/ship/config/ConsoleVariable.cpp create mode 100644 libultraship/src/ship/controller/controldeck/ControlDeck.cpp create mode 100644 libultraship/src/ship/controller/controldeck/ControlPort.cpp create mode 100644 libultraship/src/ship/controller/controldevice/ControlDevice.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/Controller.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/ControllerButton.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/ControllerGyro.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/ControllerLED.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/ControllerRumble.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/ControllerStick.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerButtonMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerDefaultMappings.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerGyroMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerInputMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerLEDMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/ControllerRumbleMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/factories/AxisDirectionMappingFactory.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/factories/ButtonMappingFactory.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/factories/GyroMappingFactory.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/factories/LEDMappingFactory.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/factories/RumbleMappingFactory.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/keyboard/KeyboardKeyToAnyMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/keyboard/KeyboardKeyToAxisDirectionMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/keyboard/KeyboardKeyToButtonMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/mouse/MouseButtonToAnyMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/mouse/MouseButtonToAxisDirectionMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/mouse/MouseButtonToButtonMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/mouse/MouseWheelToAnyMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/mouse/MouseWheelToAxisDirectionMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/mouse/MouseWheelToButtonMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/mouse/WheelHandler.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToAnyMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToAxisDirectionMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToButtonMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLButtonToAnyMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLButtonToAxisDirectionMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLButtonToButtonMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLGyroMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLLEDMapping.cpp create mode 100644 libultraship/src/ship/controller/controldevice/controller/mapping/sdl/SDLRumbleMapping.cpp create mode 100644 libultraship/src/ship/controller/physicaldevice/ConnectedPhysicalDeviceManager.cpp create mode 100644 libultraship/src/ship/controller/physicaldevice/GlobalSDLDeviceSettings.cpp create mode 100644 libultraship/src/ship/controller/physicaldevice/SDLAddRemoveDeviceEventHandler.cpp create mode 100644 libultraship/src/ship/install_config.h.in create mode 100644 libultraship/src/ship/port/mobile/MobileImpl.cpp create mode 100644 libultraship/src/ship/resource/Resource.cpp create mode 100644 libultraship/src/ship/resource/ResourceFactoryBinary.cpp create mode 100644 libultraship/src/ship/resource/ResourceFactoryXML.cpp create mode 100644 libultraship/src/ship/resource/ResourceLoader.cpp create mode 100644 libultraship/src/ship/resource/ResourceManager.cpp create mode 100644 libultraship/src/ship/resource/archive/Archive.cpp create mode 100644 libultraship/src/ship/resource/archive/ArchiveManager.cpp create mode 100644 libultraship/src/ship/resource/archive/FolderArchive.cpp create mode 100644 libultraship/src/ship/resource/archive/O2rArchive.cpp create mode 100644 libultraship/src/ship/resource/archive/OtrArchive.cpp create mode 100644 libultraship/src/ship/resource/factory/BlobFactory.cpp create mode 100644 libultraship/src/ship/resource/factory/JsonFactory.cpp create mode 100644 libultraship/src/ship/resource/factory/ShaderFactory.cpp create mode 100644 libultraship/src/ship/resource/type/Blob.cpp create mode 100644 libultraship/src/ship/resource/type/Json.cpp create mode 100644 libultraship/src/ship/resource/type/Shader.cpp create mode 100644 libultraship/src/ship/utils/AppleFolderManager.mm create mode 100644 libultraship/src/ship/utils/StrHash64.cpp create mode 100644 libultraship/src/ship/utils/StringHelper.cpp create mode 100644 libultraship/src/ship/utils/Utils.cpp create mode 100644 libultraship/src/ship/utils/binarytools/BinaryReader.cpp create mode 100644 libultraship/src/ship/utils/binarytools/BinaryWriter.cpp create mode 100644 libultraship/src/ship/utils/binarytools/MemoryStream.cpp create mode 100644 libultraship/src/ship/utils/binarytools/Stream.cpp create mode 100644 libultraship/src/ship/utils/glob.c create mode 100644 libultraship/src/ship/utils/macUtils.mm create mode 100644 libultraship/src/ship/utils/stox.cpp create mode 100644 libultraship/src/ship/window/FileDropMgr.cpp create mode 100644 libultraship/src/ship/window/MouseStateManager.cpp create mode 100644 libultraship/src/ship/window/Window.cpp create mode 100644 libultraship/src/ship/window/gui/ConsoleWindow.cpp create mode 100644 libultraship/src/ship/window/gui/GameOverlay.cpp create mode 100644 libultraship/src/ship/window/gui/Gui.cpp create mode 100644 libultraship/src/ship/window/gui/GuiElement.cpp create mode 100644 libultraship/src/ship/window/gui/GuiMenuBar.cpp create mode 100644 libultraship/src/ship/window/gui/GuiWindow.cpp create mode 100644 libultraship/src/ship/window/gui/InputEditorWindow.cpp create mode 100644 libultraship/src/ship/window/gui/StatsWindow.cpp create mode 100644 libultraship/src/ship/window/gui/resource/Font.cpp create mode 100644 libultraship/src/ship/window/gui/resource/FontFactory.cpp create mode 100644 libultraship/src/ship/window/gui/resource/GuiTexture.cpp create mode 100644 libultraship/src/ship/window/gui/resource/GuiTextureFactory.cpp diff --git a/OTRExporter/.gitignore b/OTRExporter/.gitignore new file mode 100644 index 000000000..3f5299db8 --- /dev/null +++ b/OTRExporter/.gitignore @@ -0,0 +1,355 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +*.out +*.o +*.d +lib/libgfxd/libgfxd.a +ExporterTest/ExporterTest.a +ZAPDUtils/ZAPDUtils.a +.vscode/ +build/ +ZAPDUtils/build/ +ZAPD/BuildInfo.h +baserom/ +baserom_ntsc/ +*.vtx.inc +*.otr +*.o2r +*.swp +*.a +*.z64 +*.n64 +*.zip +Extract/ + +tmp.txt diff --git a/OTRExporter/CMake/Default.cmake b/OTRExporter/CMake/Default.cmake new file mode 100644 index 000000000..70bfa9038 --- /dev/null +++ b/OTRExporter/CMake/Default.cmake @@ -0,0 +1,65 @@ +################################################################################ +# Command for variable_watch. This command issues error message, if a variable +# is changed. If variable PROPERTY_READER_GUARD_DISABLED is TRUE nothing happens +# variable_watch( property_reader_guard) +################################################################################ +function(property_reader_guard VARIABLE ACCESS VALUE CURRENT_LIST_FILE STACK) + if("${PROPERTY_READER_GUARD_DISABLED}") + return() + endif() + + if("${ACCESS}" STREQUAL "MODIFIED_ACCESS") + message(FATAL_ERROR + " Variable ${VARIABLE} is not supposed to be changed.\n" + " It is used only for reading target property ${VARIABLE}.\n" + " Use\n" + " set_target_properties(\"\" PROPERTIES \"${VARIABLE}\" \"\")\n" + " or\n" + " set_target_properties(\"\" PROPERTIES \"${VARIABLE}_\" \"\")\n" + " instead.\n") + endif() +endfunction() + +################################################################################ +# Create variable with generator expression that expands to value of +# target property _. If property is empty or not set then property +# is used instead. Variable has watcher property_reader_guard that +# doesn't allow to edit it. +# create_property_reader() +# Input: +# name - Name of watched property and output variable +################################################################################ +function(create_property_reader NAME) + set(PROPERTY_READER_GUARD_DISABLED TRUE) + set(CONFIG_VALUE "$>>>") + set(IS_CONFIG_VALUE_EMPTY "$") + set(GENERAL_VALUE "$>") + set("${NAME}" "$" PARENT_SCOPE) + variable_watch("${NAME}" property_reader_guard) +endfunction() + +################################################################################ +# Set property $_${PROPS_CONFIG_U} of ${PROPS_TARGET} to +# set_config_specific_property( ) +# Input: +# name - Prefix of property name +# value - New value +################################################################################ +function(set_config_specific_property NAME VALUE) + set_target_properties("${PROPS_TARGET}" PROPERTIES "${NAME}_${PROPS_CONFIG_U}" "${VALUE}") +endfunction() + +################################################################################ + +create_property_reader("TARGET_NAME") +create_property_reader("OUTPUT_DIRECTORY") + +set_config_specific_property("TARGET_NAME" "${PROPS_TARGET}") +set_config_specific_property("OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("ARCHIVE_OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("LIBRARY_OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("RUNTIME_OUTPUT_NAME" "${TARGET_NAME}") + +set_config_specific_property("ARCHIVE_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") +set_config_specific_property("LIBRARY_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") +set_config_specific_property("RUNTIME_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") \ No newline at end of file diff --git a/OTRExporter/CMake/DefaultCXX.cmake b/OTRExporter/CMake/DefaultCXX.cmake new file mode 100644 index 000000000..810b1459e --- /dev/null +++ b/OTRExporter/CMake/DefaultCXX.cmake @@ -0,0 +1,16 @@ +include("${CMAKE_CURRENT_LIST_DIR}/Default.cmake") + +set_config_specific_property("OUTPUT_DIRECTORY" "${CMAKE_SOURCE_DIR}$<$>:/${CMAKE_VS_PLATFORM_NAME}>/${PROPS_CONFIG}") + +if(MSVC) + create_property_reader("DEFAULT_CXX_EXCEPTION_HANDLING") + create_property_reader("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT") + + set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + set_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc") + if (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache|sccache") + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Z7") + else() + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi") + endif() +endif() diff --git a/OTRExporter/CMake/Utils.cmake b/OTRExporter/CMake/Utils.cmake new file mode 100644 index 000000000..5bce7d488 --- /dev/null +++ b/OTRExporter/CMake/Utils.cmake @@ -0,0 +1,233 @@ +# utils file for projects came from visual studio solution with cmake-converter. + +################################################################################ +# Wrap each token of the command with condition +################################################################################ +cmake_policy(PUSH) +cmake_policy(SET CMP0054 NEW) +macro(prepare_commands) + unset(TOKEN_ROLE) + unset(COMMANDS) + foreach(TOKEN ${ARG_COMMANDS}) + if("${TOKEN}" STREQUAL "COMMAND") + set(TOKEN_ROLE "KEYWORD") + elseif("${TOKEN_ROLE}" STREQUAL "KEYWORD") + set(TOKEN_ROLE "CONDITION") + elseif("${TOKEN_ROLE}" STREQUAL "CONDITION") + set(TOKEN_ROLE "COMMAND") + elseif("${TOKEN_ROLE}" STREQUAL "COMMAND") + set(TOKEN_ROLE "ARG") + endif() + + if("${TOKEN_ROLE}" STREQUAL "KEYWORD") + list(APPEND COMMANDS "${TOKEN}") + elseif("${TOKEN_ROLE}" STREQUAL "CONDITION") + set(CONDITION ${TOKEN}) + elseif("${TOKEN_ROLE}" STREQUAL "COMMAND") + list(APPEND COMMANDS "$<$:${DUMMY}>$<${CONDITION}:${TOKEN}>") + elseif("${TOKEN_ROLE}" STREQUAL "ARG") + list(APPEND COMMANDS "$<${CONDITION}:${TOKEN}>") + endif() + endforeach() +endmacro() +cmake_policy(POP) + +################################################################################ +# Transform all the tokens to absolute paths +################################################################################ +macro(prepare_output) + unset(OUTPUT) + foreach(TOKEN ${ARG_OUTPUT}) + if(IS_ABSOLUTE ${TOKEN}) + list(APPEND OUTPUT "${TOKEN}") + else() + list(APPEND OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/${TOKEN}") + endif() + endforeach() +endmacro() + +################################################################################ +# Parse add_custom_command_if args. +# +# Input: +# PRE_BUILD - Pre build event option +# PRE_LINK - Pre link event option +# POST_BUILD - Post build event option +# TARGET - Target +# OUTPUT - List of output files +# DEPENDS - List of files on which the command depends +# COMMANDS - List of commands(COMMAND condition1 commannd1 args1 COMMAND +# condition2 commannd2 args2 ...) +# Output: +# OUTPUT - Output files +# DEPENDS - Files on which the command depends +# COMMENT - Comment +# PRE_BUILD - TRUE/FALSE +# PRE_LINK - TRUE/FALSE +# POST_BUILD - TRUE/FALSE +# TARGET - Target name +# COMMANDS - Prepared commands(every token is wrapped in CONDITION) +# NAME - Unique name for custom target +# STEP - PRE_BUILD/PRE_LINK/POST_BUILD +################################################################################ +function(add_custom_command_if_parse_arguments) + cmake_parse_arguments("ARG" "PRE_BUILD;PRE_LINK;POST_BUILD" "TARGET;COMMENT" "DEPENDS;OUTPUT;COMMANDS" ${ARGN}) + + if(WIN32) + set(DUMMY "cd.") + elseif(UNIX) + set(DUMMY "true") + endif() + + prepare_commands() + prepare_output() + + set(DEPENDS "${ARG_DEPENDS}") + set(COMMENT "${ARG_COMMENT}") + set(PRE_BUILD "${ARG_PRE_BUILD}") + set(PRE_LINK "${ARG_PRE_LINK}") + set(POST_BUILD "${ARG_POST_BUILD}") + set(TARGET "${ARG_TARGET}") + if(PRE_BUILD) + set(STEP "PRE_BUILD") + elseif(PRE_LINK) + set(STEP "PRE_LINK") + elseif(POST_BUILD) + set(STEP "POST_BUILD") + endif() + set(NAME "${TARGET}_${STEP}") + + set(OUTPUT "${OUTPUT}" PARENT_SCOPE) + set(DEPENDS "${DEPENDS}" PARENT_SCOPE) + set(COMMENT "${COMMENT}" PARENT_SCOPE) + set(PRE_BUILD "${PRE_BUILD}" PARENT_SCOPE) + set(PRE_LINK "${PRE_LINK}" PARENT_SCOPE) + set(POST_BUILD "${POST_BUILD}" PARENT_SCOPE) + set(TARGET "${TARGET}" PARENT_SCOPE) + set(COMMANDS "${COMMANDS}" PARENT_SCOPE) + set(STEP "${STEP}" PARENT_SCOPE) + set(NAME "${NAME}" PARENT_SCOPE) +endfunction() + +################################################################################ +# Add conditional custom command +# +# Generating Files +# The first signature is for adding a custom command to produce an output: +# add_custom_command_if( +# +# +# +# [COMMAND condition command2 [args2...]] +# [DEPENDS [depends...]] +# [COMMENT comment] +# +# Build Events +# add_custom_command_if( +# +# +# +# [COMMAND condition command2 [args2...]] +# [COMMENT comment] +# +# Input: +# output - Output files the command is expected to produce +# condition - Generator expression for wrapping the command +# command - Command-line(s) to execute at build time. +# args - Command`s args +# depends - Files on which the command depends +# comment - Display the given message before the commands are executed at +# build time. +# PRE_BUILD - Run before any other rules are executed within the target +# PRE_LINK - Run after sources have been compiled but before linking the +# binary +# POST_BUILD - Run after all other rules within the target have been +# executed +################################################################################ +function(add_custom_command_if) + add_custom_command_if_parse_arguments(${ARGN}) + + if(OUTPUT AND TARGET) + message(FATAL_ERROR "Wrong syntax. A TARGET and OUTPUT can not both be specified.") + endif() + + if(OUTPUT) + add_custom_command(OUTPUT ${OUTPUT} + ${COMMANDS} + DEPENDS ${DEPENDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + elseif(TARGET) + if(PRE_BUILD AND NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio") + add_custom_target( + ${NAME} + ${COMMANDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + add_dependencies(${TARGET} ${NAME}) + else() + add_custom_command( + TARGET ${TARGET} + ${STEP} + ${COMMANDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + endif() + else() + message(FATAL_ERROR "Wrong syntax. A TARGET or OUTPUT must be specified.") + endif() +endfunction() + +################################################################################ +# Use props file for a target and configs +# use_props( ) +# Inside there are following variables: +# PROPS_TARGET - +# PROPS_CONFIG - One of +# PROPS_CONFIG_U - Uppercase PROPS_CONFIG +# Input: +# target - Target to apply props file +# configs - Build configurations to apply props file +# props_file - CMake script +################################################################################ +macro(use_props TARGET CONFIGS PROPS_FILE) + set(PROPS_TARGET "${TARGET}") + foreach(PROPS_CONFIG ${CONFIGS}) + string(TOUPPER "${PROPS_CONFIG}" PROPS_CONFIG_U) + + get_filename_component(ABSOLUTE_PROPS_FILE "${PROPS_FILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") + if(EXISTS "${ABSOLUTE_PROPS_FILE}") + include("${ABSOLUTE_PROPS_FILE}") + else() + message(WARNING "Corresponding cmake file from props \"${ABSOLUTE_PROPS_FILE}\" doesn't exist") + endif() + endforeach() +endmacro() + +################################################################################ +# Add compile options to source file +# source_file_compile_options( [compile_options...]) +# Input: +# source_file - Source file +# compile_options - Options to add to COMPILE_FLAGS property +################################################################################ +function(source_file_compile_options SOURCE_FILE) + if("${ARGC}" LESS_EQUAL "1") + return() + endif() + + get_source_file_property(COMPILE_OPTIONS "${SOURCE_FILE}" COMPILE_OPTIONS) + + if(COMPILE_OPTIONS) + list(APPEND COMPILE_OPTIONS ${ARGN}) + else() + set(COMPILE_OPTIONS "${ARGN}") + endif() + + set_source_files_properties("${SOURCE_FILE}" PROPERTIES COMPILE_OPTIONS "${COMPILE_OPTIONS}") +endfunction() + +################################################################################ +# Default properties of visual studio projects +################################################################################ +set(DEFAULT_CXX_PROPS "${CMAKE_CURRENT_LIST_DIR}/DefaultCXX.cmake") diff --git a/OTRExporter/CMakeLists.txt b/OTRExporter/CMakeLists.txt new file mode 100644 index 000000000..bd4e368bc --- /dev/null +++ b/OTRExporter/CMakeLists.txt @@ -0,0 +1,97 @@ +cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR) + +set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE) +set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +set(CMAKE_C_STANDARD 11 CACHE STRING "The C standard to use") + +project(OTRExporter C CXX) + +################################################################################ +# Set target arch type if empty. Visual studio solution generator provides it. +################################################################################ +#if (CMAKE_SYSTEM_NAME STREQUAL "Windows") +# if(NOT CMAKE_VS_PLATFORM_NAME) +# set(CMAKE_VS_PLATFORM_NAME "x64") +# endif() +# message("${CMAKE_VS_PLATFORM_NAME} architecture in use") +# +# if(NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64" +# OR "${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")) +# message(FATAL_ERROR "${CMAKE_VS_PLATFORM_NAME} arch is not supported!") +# endif() +#endif() + +################################################################################ +# Global configuration types +################################################################################ +set(CMAKE_CONFIGURATION_TYPES + "Debug" + "Release" + CACHE STRING "" FORCE +) + +################################################################################ +# Global compiler options +################################################################################ +if(MSVC) + # remove default flags provided with CMake for MSVC + set(CMAKE_C_FLAGS "") + set(CMAKE_C_FLAGS_DEBUG "") + set(CMAKE_C_FLAGS_RELEASE "") + set(CMAKE_CXX_FLAGS "") + set(CMAKE_CXX_FLAGS_DEBUG "") + set(CMAKE_CXX_FLAGS_RELEASE "") +endif() + +################################################################################ +# Global linker options +################################################################################ +if(MSVC) + # remove default flags provided with CMake for MSVC + set(CMAKE_EXE_LINKER_FLAGS "") + set(CMAKE_MODULE_LINKER_FLAGS "") + set(CMAKE_SHARED_LINKER_FLAGS "") + set(CMAKE_STATIC_LINKER_FLAGS "") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS}") + set(CMAKE_STATIC_LINKER_FLAGS_DEBUG "${CMAKE_STATIC_LINKER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS}") + set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS}") +endif() + +################################################################################ +# Common utils +################################################################################ +include(CMake/Utils.cmake) + +################################################################################ +# Additional Global Settings(add specific info there) +################################################################################ +include(CMake/GlobalSettingsInclude.cmake OPTIONAL) + +################################################################################ +# Use solution folders feature +################################################################################ +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +################################################################################ +# Sub-projects +################################################################################ +if (NOT TARGET libultraship) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libultraship ${CMAKE_BINARY_DIR}/libultraship) +endif() + +if (NOT TARGET ZAPD) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPD ${CMAKE_BINARY_DIR}/ZAPD) +endif() + +add_subdirectory(OTRExporter) + +if (GAME_STR STREQUAL "MM") + target_compile_definitions(OTRExporter PRIVATE GAME_MM) +else() + target_compile_definitions(OTRExporter PRIVATE GAME_OOT) +endif() diff --git a/OTRExporter/LICENSE b/OTRExporter/LICENSE new file mode 100644 index 000000000..59ddd97bc --- /dev/null +++ b/OTRExporter/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2022 Harbour Masters + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/OTRExporter/OTRExporter/AnimationExporter.cpp b/OTRExporter/OTRExporter/AnimationExporter.cpp new file mode 100644 index 000000000..6a5bf7bd2 --- /dev/null +++ b/OTRExporter/OTRExporter/AnimationExporter.cpp @@ -0,0 +1,92 @@ +#include "AnimationExporter.h" +#ifdef GAME_MM +#include +#elif GAME_OOT +#include "../../soh/soh/resource/type/Animation.h" +#endif + +#include +#include "DisplayListExporter.h" +#undef FindResource + + +void OTRExporter_Animation::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZAnimation* anim = (ZAnimation*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_Animation)); + + ZNormalAnimation* normalAnim = dynamic_cast(anim); + ZCurveAnimation* curveAnim = dynamic_cast(anim); + ZLinkAnimation* linkAnim = dynamic_cast(anim); + if (linkAnim != nullptr) + { + writer->Write((uint32_t)SOH::AnimationType::Link); + writer->Write((uint16_t)linkAnim->frameCount); + std::string name; + bool found = Globals::Instance->GetSegmentedPtrName((linkAnim->segmentAddress), res->parent, "", name, res->parent->workerID); + if (found) + { + if (name.at(0) == '&') + name.erase(0, 1); + + writer->Write(StringHelper::Sprintf("__OTR__misc/link_animetion/%s", name.c_str())); + } + else + { + writer->Write(""); + } + //writer->Write((uint32_t)linkAnim->segmentAddress); + } + else if (curveAnim != nullptr) + { + writer->Write((uint32_t)SOH::AnimationType::Curve); + writer->Write((uint16_t)curveAnim->frameCount); + + writer->Write((uint32_t)curveAnim->refIndexArr.size()); + + for (auto val : curveAnim->refIndexArr) + writer->Write(val); + + writer->Write((uint32_t)curveAnim->transformDataArr.size()); + + for (auto val : curveAnim->transformDataArr) + { + writer->Write(val.unk_00); + writer->Write(val.unk_02); + writer->Write(val.unk_04); + writer->Write(val.unk_06); + writer->Write(val.unk_08); + } + + writer->Write((uint32_t)curveAnim->copyValuesArr.size()); + + for (auto val : curveAnim->copyValuesArr) + writer->Write(val); + } + else if (normalAnim != nullptr) + { + writer->Write((uint32_t)SOH::AnimationType::Normal); + writer->Write((uint16_t)normalAnim->frameCount); + + writer->Write((uint32_t)normalAnim->rotationValues.size()); + + for (size_t i = 0; i < normalAnim->rotationValues.size(); i++) + writer->Write(normalAnim->rotationValues[i]); + + writer->Write((uint32_t)normalAnim->rotationIndices.size()); + + for (size_t i = 0; i < normalAnim->rotationIndices.size(); i++) + { + writer->Write(normalAnim->rotationIndices[i].x); + writer->Write(normalAnim->rotationIndices[i].y); + writer->Write(normalAnim->rotationIndices[i].z); + } + + writer->Write(normalAnim->limit); + } + else + { + writer->Write((uint32_t)SOH::AnimationType::Legacy); + } +} diff --git a/OTRExporter/OTRExporter/AnimationExporter.h b/OTRExporter/OTRExporter/AnimationExporter.h new file mode 100644 index 000000000..58fddb2fd --- /dev/null +++ b/OTRExporter/OTRExporter/AnimationExporter.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ZResource.h" +#include "ZTexture.h" +#include "ZAnimation.h" +#include "Exporter.h" +#include + +class OTRExporter_Animation : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/ArrayExporter.cpp b/OTRExporter/OTRExporter/ArrayExporter.cpp new file mode 100644 index 000000000..bca44d594 --- /dev/null +++ b/OTRExporter/OTRExporter/ArrayExporter.cpp @@ -0,0 +1,116 @@ +#include "ArrayExporter.h" +#include "VtxExporter.h" +#include +void OTRExporter_Array::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZArray* arr = (ZArray*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_Array)); + + writer->Write((uint32_t)arr->resList[0]->GetResourceType()); + writer->Write((uint32_t)arr->arrayCnt); + + for (size_t i = 0; i < arr->arrayCnt; i++) + { + if (arr->resList[i]->GetResourceType() == ZResourceType::Vertex) + { + ZVtx* vtx = (ZVtx*)arr->resList[i]; + writer->Write(vtx->x); + writer->Write(vtx->y); + writer->Write(vtx->z); + writer->Write(vtx->flag); + writer->Write(vtx->s); + writer->Write(vtx->t); + writer->Write(vtx->r); + writer->Write(vtx->g); + writer->Write(vtx->b); + writer->Write(vtx->a); + } + else if (arr->resList[i]->GetResourceType() == ZResourceType::Vector) + { + ZVector* vec = (ZVector*)arr->resList[i]; + writer->Write((uint32_t)vec->scalarType); + writer->Write((uint32_t)vec->dimensions); + + for (size_t k = 0; k < vec->dimensions; k++) + { + // OTRTODO: Duplicate code here. Cleanup at a later date... + switch (vec->scalarType) + { + case ZScalarType::ZSCALAR_S8: + writer->Write(vec->scalars[k].scalarData.s8); + break; + case ZScalarType::ZSCALAR_U8: + case ZScalarType::ZSCALAR_X8: + writer->Write(vec->scalars[k].scalarData.u8); + break; + case ZScalarType::ZSCALAR_S16: + writer->Write(vec->scalars[k].scalarData.s16); + break; + case ZScalarType::ZSCALAR_U16: + case ZScalarType::ZSCALAR_X16: + writer->Write(vec->scalars[k].scalarData.u16); + break; + case ZScalarType::ZSCALAR_S32: + writer->Write(vec->scalars[k].scalarData.s32); + break; + case ZScalarType::ZSCALAR_U32: + case ZScalarType::ZSCALAR_X32: + writer->Write(vec->scalars[k].scalarData.u32); + break; + case ZScalarType::ZSCALAR_S64: + writer->Write(vec->scalars[k].scalarData.s64); + break; + case ZScalarType::ZSCALAR_U64: + case ZScalarType::ZSCALAR_X64: + writer->Write(vec->scalars[k].scalarData.u64); + break; + // OTRTODO: ADD OTHER TYPES + default: + break; + } + } + } + else + { + ZScalar* scal = (ZScalar*)arr->resList[i]; + + writer->Write((uint32_t)scal->scalarType); + + switch (scal->scalarType) + { + case ZScalarType::ZSCALAR_S8: + writer->Write(scal->scalarData.s8); + break; + case ZScalarType::ZSCALAR_U8: + case ZScalarType::ZSCALAR_X8: + writer->Write(scal->scalarData.u8); + break; + case ZScalarType::ZSCALAR_S16: + writer->Write(scal->scalarData.s16); + break; + case ZScalarType::ZSCALAR_U16: + case ZScalarType::ZSCALAR_X16: + writer->Write(scal->scalarData.u16); + break; + case ZScalarType::ZSCALAR_S32: + writer->Write(scal->scalarData.s32); + break; + case ZScalarType::ZSCALAR_U32: + case ZScalarType::ZSCALAR_X32: + writer->Write(scal->scalarData.u32); + break; + case ZScalarType::ZSCALAR_S64: + writer->Write(scal->scalarData.s64); + break; + case ZScalarType::ZSCALAR_U64: + case ZScalarType::ZSCALAR_X64: + writer->Write(scal->scalarData.u64); + break; + // OTRTODO: ADD OTHER TYPES + default: + break; + } + } + } +} diff --git a/OTRExporter/OTRExporter/ArrayExporter.h b/OTRExporter/OTRExporter/ArrayExporter.h new file mode 100644 index 000000000..a006490e6 --- /dev/null +++ b/OTRExporter/OTRExporter/ArrayExporter.h @@ -0,0 +1,12 @@ +#pragma once +#include "ZResource.h" +#include "ZArray.h" +#include "Exporter.h" +#include + +class OTRExporter_Array : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; + +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/AudioExporter.cpp b/OTRExporter/OTRExporter/AudioExporter.cpp new file mode 100644 index 000000000..d6639d804 --- /dev/null +++ b/OTRExporter/OTRExporter/AudioExporter.cpp @@ -0,0 +1,483 @@ +#include "AudioExporter.h" +#include "Main.h" +#include +#include +#include +#include "DisplayListExporter.h" + +const char* OTRExporter_Audio::GetMediumStr(uint8_t medium) { + switch (medium) { + case 0: + return "Ram"; + case 1: + return "Unk"; + case 2: + return "Cart"; + case 3: + return "Disk"; + case 5: + return "RamUnloaded"; + default: + return "ERROR"; + } +} + +const char* OTRExporter_Audio::GetCachePolicyStr(uint8_t policy) { + switch (policy) { + case 0: + return "Temporary"; + case 1: + return "Persistent"; + case 2: + return "Either"; + case 3: + return "Permanent"; + default: + return "ERROR"; + } +} + +const char* OTRExporter_Audio::GetCodecStr(uint8_t codec) { + switch (codec) { + case 0: + return "ADPCM"; + case 1: + return "S8"; + case 2: + return "S16MEM"; + case 3: + return "ADPCMSMALL"; + case 4: + return "REVERB"; + case 5: + return "S16"; + case 6: + return "UNK6"; + case 7: + return "UNK7"; + default: + return "ERROR"; + } +} + +std::string OTRExporter_Audio::GetSampleEntryReference(ZAudio* audio, SampleEntry* entry) +{ + if (entry != nullptr) + { + if (audio->sampleOffsets[entry->bankId].contains(entry->sampleDataOffset) && + audio->sampleOffsets[entry->bankId][entry->sampleDataOffset] != "") { + return (StringHelper::Sprintf("audio/samples/%s_META", + audio->sampleOffsets[entry->bankId][entry->sampleDataOffset].c_str())); + } + else + return StringHelper::Sprintf("audio/samples/sample_%d_%08X_META", entry->bankId, entry->sampleDataOffset); + } + else + return(""); +} + +void OTRExporter_Audio::WriteSampleEntry(SampleEntry* entry, BinaryWriter* writer) +{ + WriteHeader(nullptr, "", writer, static_cast(SOH::ResourceType::SOH_AudioSample), 2); + + writer->Write(entry->codec); + writer->Write(entry->medium); + writer->Write(entry->unk_bit26); + writer->Write(entry->unk_bit25); + + writer->Write((uint32_t)entry->data.size()); + writer->Write((char*)entry->data.data(), entry->data.size()); + + writer->Write((uint32_t)(entry->loop.start)); + writer->Write((uint32_t)(entry->loop.end)); + writer->Write((uint32_t)(entry->loop.count)); + writer->Write((uint32_t)entry->loop.states.size()); + + for (size_t i = 0; i < entry->loop.states.size(); i++) + writer->Write((entry->loop.states[i])); + + writer->Write((uint32_t)(entry->book.order)); + writer->Write((uint32_t)(entry->book.npredictors)); + writer->Write((uint32_t)entry->book.books.size()); + + for (size_t i = 0; i < entry->book.books.size(); i++) + writer->Write((entry->book.books[i])); +} + +void OTRExporter_Audio::WriteSampleEntry(SampleEntry* entry, tinyxml2::XMLElement* xmlDoc) { + tinyxml2::XMLElement* sEntry = xmlDoc; + + sEntry->SetAttribute("Codec", GetCodecStr(entry->codec)); + sEntry->SetAttribute("Medium", GetMediumStr(entry->medium)); + sEntry->SetAttribute("bit26", entry->unk_bit26); + sEntry->SetAttribute("Relocated", entry->unk_bit25); + + tinyxml2::XMLElement* loopRoot = sEntry->InsertNewChildElement("ADPCMLoop"); + loopRoot->SetAttribute("Start", entry->loop.start); + loopRoot->SetAttribute("End", entry->loop.end); + loopRoot->SetAttribute("Count", (int)entry->loop.count); // Cast to int to -1 shows as -1. + + for (size_t i = 0; i < entry->loop.states.size(); i++) { + tinyxml2::XMLElement* loop = loopRoot->InsertNewChildElement("Predictor"); + loop->SetAttribute("State", entry->loop.states[i]); + loopRoot->InsertEndChild(loop); + } + sEntry->InsertEndChild(loopRoot); + + tinyxml2::XMLElement* bookRoot = sEntry->InsertNewChildElement("ADPCMBook"); + bookRoot->SetAttribute("Order", entry->book.order); + bookRoot->SetAttribute("Npredictors", entry->book.npredictors); + + for (size_t i = 0; i < entry->book.books.size(); i++) { + tinyxml2::XMLElement* book = bookRoot->InsertNewChildElement("Book"); + book->SetAttribute("Page", entry->book.books[i]); + bookRoot->InsertEndChild(book); + } + sEntry->InsertEndChild(bookRoot); + +} + +void OTRExporter_Audio::WriteSoundFontEntry(ZAudio* audio, SoundFontEntry* entry, BinaryWriter* writer) +{ + writer->Write((uint8_t)(entry != nullptr ? 1 : 0)); + + if (entry != nullptr) + { + // This second byte isn't used but is needed to maintain compatibility with the V2 format. + writer->Write((uint8_t)(entry != nullptr ? 1 : 0)); + writer->Write(GetSampleEntryReference(audio, entry->sampleEntry)); + writer->Write(entry->tuning); + } +} + +void OTRExporter_Audio::WriteSoundFontEntry(ZAudio* audio, SoundFontEntry* entry, tinyxml2::XMLElement* xmlDoc, + const char* name) { + tinyxml2::XMLElement* sfEntry = xmlDoc->InsertNewChildElement(name); + + if (entry != nullptr) + { + sfEntry->SetAttribute("SampleRef", GetSampleEntryReference(audio, entry->sampleEntry).c_str()); + sfEntry->SetAttribute("Tuning", entry->tuning); + } + xmlDoc->InsertEndChild(sfEntry); +} + +void OTRExporter_Audio::WriteEnvData(std::vector envelopes, BinaryWriter* writer) +{ + writer->Write((uint32_t)envelopes.size()); + + for (auto env : envelopes) + { + writer->Write(env->delay); + writer->Write(env->arg); + } +} + +void OTRExporter_Audio::WriteEnvData(std::vector envelopes, tinyxml2::XMLElement* xmlDoc) { + tinyxml2::XMLElement* envs = xmlDoc->InsertNewChildElement("Envelopes"); + envs->SetAttribute("Count", (uint32_t)envelopes.size()); + + for (auto e : envelopes) { + tinyxml2::XMLElement* env = envs->InsertNewChildElement("Envelope"); + env->SetAttribute("Delay", e->delay); + env->SetAttribute("Arg", e->arg); + } + xmlDoc->InsertEndChild(envs); +} + + +void OTRExporter_Audio::WriteSoundFontTableXML(ZAudio* audio) { + for (size_t i = 0; i < audio->soundFontTable.size(); i++) { + tinyxml2::XMLDocument soundFont; + tinyxml2::XMLElement* root = soundFont.NewElement("SoundFont"); + root->SetAttribute("Version", 0); + root->SetAttribute("Num", (uint32_t)i); + root->SetAttribute("Medium", GetMediumStr(audio->soundFontTable[i].medium)); + root->SetAttribute("CachePolicy", GetCachePolicyStr(audio->soundFontTable[i].cachePolicy)); + root->SetAttribute("Data1", audio->soundFontTable[i].data1); + root->SetAttribute("Data2", audio->soundFontTable[i].data2); + root->SetAttribute("Data3", audio->soundFontTable[i].data3); + soundFont.InsertFirstChild(root); + + tinyxml2::XMLElement* drums = root->InsertNewChildElement("Drums"); + drums->SetAttribute("Count", (uint32_t)audio->soundFontTable[i].drums.size()); + + for (const auto& d : audio->soundFontTable[i].drums) { + tinyxml2::XMLElement* drum = drums->InsertNewChildElement("Drum"); + drum->SetAttribute("ReleaseRate", d.releaseRate); + drum->SetAttribute("Pan", d.pan); + drum->SetAttribute("Loaded", d.loaded); + drum->SetAttribute("SampleRef", GetSampleEntryReference(audio, d.sample).c_str()); + drum->SetAttribute("Tuning", d.tuning); + + WriteEnvData(d.env, drum); + drums->InsertEndChild(drum); + } + root->InsertEndChild(drums); + + tinyxml2::XMLElement* instruments = root->InsertNewChildElement("Instruments"); + instruments->SetAttribute("Count", (uint32_t)audio->soundFontTable[i].instruments.size()); + + //for (size_t k = 0; k < audio->soundFontTable[i].instruments.size(); k++) { + for (const auto i : audio->soundFontTable[i].instruments) { + tinyxml2::XMLElement* instrument = instruments->InsertNewChildElement("Instrument"); + + instrument->SetAttribute("IsValid", i.isValidInstrument); + instrument->SetAttribute("Loaded", i.loaded); + instrument->SetAttribute("NormalRangeLo", i.normalRangeLo); + instrument->SetAttribute("NormalRangeHi", i.normalRangeHi); + instrument->SetAttribute("ReleaseRate", i.releaseRate); + + WriteEnvData(i.env, instrument); + + WriteSoundFontEntry(audio, i.lowNotesSound, instrument, "LowNotesSound"); + WriteSoundFontEntry(audio, i.normalNotesSound, instrument, "NormalNotesSound"); + WriteSoundFontEntry(audio, i.highNotesSound, instrument, "HighNotesSound"); + } + root->InsertEndChild(instruments); + + tinyxml2::XMLElement* sfxTbl = root->InsertNewChildElement("SfxTable"); + sfxTbl->SetAttribute("Count", (uint32_t)audio->soundFontTable[i].soundEffects.size()); + + for (const auto s : audio->soundFontTable[i].soundEffects) { + WriteSoundFontEntry(audio, s, sfxTbl, "Sfx"); + } + root->InsertEndChild(sfxTbl); + soundFont.InsertEndChild(root); + + tinyxml2::XMLPrinter printer; + soundFont.Accept(&printer); + + std::string fName = OTRExporter_DisplayList::GetPathToRes( + (ZResource*)(audio), StringHelper::Sprintf("fonts/%s", audio->soundFontNames[i].c_str())); + std::vector xmlData((printer.CStr()), printer.CStr() + printer.CStrSize() - 1); + AddFile(fName, xmlData); + } +} + +void OTRExporter_Audio::WriteSoundFontTableBinary(ZAudio* audio) { + for (size_t i = 0; i < audio->soundFontTable.size(); i++) { + MemoryStream* fntStream = new MemoryStream(); + BinaryWriter fntWriter = BinaryWriter(fntStream); + + WriteHeader(nullptr, "", &fntWriter, static_cast(SOH::ResourceType::SOH_AudioSoundFont), 2); + + fntWriter.Write((uint32_t)i); + fntWriter.Write(audio->soundFontTable[i].medium); + fntWriter.Write(audio->soundFontTable[i].cachePolicy); + fntWriter.Write(audio->soundFontTable[i].data1); + fntWriter.Write(audio->soundFontTable[i].data2); + fntWriter.Write(audio->soundFontTable[i].data3); + + fntWriter.Write((uint32_t)audio->soundFontTable[i].drums.size()); + fntWriter.Write((uint32_t)audio->soundFontTable[i].instruments.size()); + fntWriter.Write((uint32_t)audio->soundFontTable[i].soundEffects.size()); + + for (size_t k = 0; k < audio->soundFontTable[i].drums.size(); k++) { + fntWriter.Write(audio->soundFontTable[i].drums[k].releaseRate); + fntWriter.Write(audio->soundFontTable[i].drums[k].pan); + fntWriter.Write(audio->soundFontTable[i].drums[k].loaded); + + WriteEnvData(audio->soundFontTable[i].drums[k].env, &fntWriter); + fntWriter.Write((uint8_t)(audio->soundFontTable[i].drums[k].sample != nullptr ? 1 : 0)); + + fntWriter.Write(GetSampleEntryReference(audio, audio->soundFontTable[i].drums[k].sample)); + fntWriter.Write(audio->soundFontTable[i].drums[k].tuning); + } + + for (size_t k = 0; k < audio->soundFontTable[i].instruments.size(); k++) { + fntWriter.Write((uint8_t)audio->soundFontTable[i].instruments[k].isValidInstrument); + + fntWriter.Write(audio->soundFontTable[i].instruments[k].loaded); + fntWriter.Write(audio->soundFontTable[i].instruments[k].normalRangeLo); + fntWriter.Write(audio->soundFontTable[i].instruments[k].normalRangeHi); + fntWriter.Write(audio->soundFontTable[i].instruments[k].releaseRate); + + WriteEnvData(audio->soundFontTable[i].instruments[k].env, &fntWriter); + + WriteSoundFontEntry(audio, audio->soundFontTable[i].instruments[k].lowNotesSound, &fntWriter); + WriteSoundFontEntry(audio, audio->soundFontTable[i].instruments[k].normalNotesSound, &fntWriter); + WriteSoundFontEntry(audio, audio->soundFontTable[i].instruments[k].highNotesSound, &fntWriter); + } + + for (size_t k = 0; k < audio->soundFontTable[i].soundEffects.size(); k++) { + WriteSoundFontEntry(audio, audio->soundFontTable[i].soundEffects[k], &fntWriter); + } + + std::string fName = OTRExporter_DisplayList::GetPathToRes( + (ZResource*)(audio), StringHelper::Sprintf("fonts/%s", audio->soundFontNames[i].c_str())); + AddFile(fName, fntStream->ToVector()); + } +} + +void OTRExporter_Audio::WriteSequenceXML(ZAudio* audio) { + for (size_t i = 0; i < audio->sequences.size(); i++) { + MemoryStream* seqStream = new MemoryStream(); + BinaryWriter seqWriter = BinaryWriter(seqStream); + auto& seq = audio->sequences[i]; + + tinyxml2::XMLDocument sequence; + tinyxml2::XMLElement* root = sequence.NewElement("Sequence"); + root->SetAttribute("Index", (uint32_t)i); + root->SetAttribute("Medium", GetMediumStr(audio->sequenceTable[i].medium)); + root->SetAttribute("CachePolicy", GetCachePolicyStr(audio->sequenceTable[i].cachePolicy)); + root->SetAttribute("Size", (uint32_t)seq.size()); + + std::string seqName = OTRExporter_DisplayList::GetPathToRes( + (ZResource*)(audio), StringHelper::Sprintf("sequencedata/%s_RAW", audio->seqNames[i].c_str())); + root->SetAttribute("Path", seqName.c_str()); + + + tinyxml2::XMLElement* fontIndicies = root->InsertNewChildElement("FontIndicies"); + for (size_t k = 0; k < audio->fontIndices[i].size(); k++) { + tinyxml2::XMLElement* fontIndex = fontIndicies->InsertNewChildElement("FontIndex"); + fontIndex->SetAttribute("FontIdx", audio->fontIndices[i][k]); + + } + root->InsertEndChild(fontIndicies); + root->InsertEndChild(root); + sequence.InsertEndChild(root); + seqWriter.Write(seq.data(), seq.size()); + AddFile(seqName, seqStream->ToVector()); + + tinyxml2::XMLPrinter printer; + sequence.Accept(&printer); + + + std::string seqMetaName = OTRExporter_DisplayList::GetPathToRes( + (ZResource*)(audio), StringHelper::Sprintf("sequences/%s_META", audio->seqNames[i].c_str())); + std::vector xmlData((printer.CStr()), printer.CStr() + printer.CStrSize() - 1); + AddFile(seqMetaName, xmlData); + } +} + +void OTRExporter_Audio::WriteSequenceBinary(ZAudio* audio) { + for (size_t i = 0; i < audio->sequences.size(); i++) { + auto& seq = audio->sequences[i]; + + MemoryStream* seqStream = new MemoryStream(); + BinaryWriter seqWriter = BinaryWriter(seqStream); + + WriteHeader(nullptr, "", &seqWriter, static_cast(SOH::ResourceType::SOH_AudioSequence), 2); + + seqWriter.Write((uint32_t)seq.size()); + seqWriter.Write(seq.data(), seq.size()); + seqWriter.Write((uint8_t)i); + seqWriter.Write((uint8_t)audio->sequenceTable[i].medium); + seqWriter.Write((uint8_t)audio->sequenceTable[i].cachePolicy); + seqWriter.Write((uint32_t)audio->fontIndices[i].size()); + + for (size_t k = 0; k < audio->fontIndices[i].size(); k++) + seqWriter.Write((uint8_t)audio->fontIndices[i][k]); + + std::string fName = OTRExporter_DisplayList::GetPathToRes( + (ZResource*)(audio), StringHelper::Sprintf("sequences/%s", audio->seqNames[i].c_str())); + AddFile(fName, seqStream->ToVector()); + } +} + +std::string OTRExporter_Audio::GetSampleEntryStr(ZAudio* audio, SampleEntry* entry) { + std::string basePath = ""; + + if (audio->sampleOffsets[entry->bankId].contains(entry->sampleDataOffset) && + audio->sampleOffsets[entry->bankId][entry->sampleDataOffset] != "") { + basePath = StringHelper::Sprintf( + "samples/%s", audio->sampleOffsets[entry->bankId][entry->sampleDataOffset].c_str()); + } else + basePath = StringHelper::Sprintf("samples/sample_%d_%08X", entry->bankId, entry->sampleDataOffset); + return basePath; +} + +std::string OTRExporter_Audio::GetSampleDataStr(ZAudio* audio, SampleEntry* entry) { + std::string basePath = ""; + + if (audio->sampleOffsets[entry->bankId].contains(entry->sampleDataOffset) && + audio->sampleOffsets[entry->bankId][entry->sampleDataOffset] != "") { + basePath = StringHelper::Sprintf( + "samples/%s_RAW", + audio->sampleOffsets[entry->bankId][entry->sampleDataOffset].c_str()); + } else + basePath = StringHelper::Sprintf("samples/sample_%d_%08X_RAW", entry->bankId, entry->sampleDataOffset); + return basePath; +} + +void OTRExporter_Audio::WriteSampleBinary(ZAudio* audio) { + ZResource* res = audio; + + for (const auto& pair : audio->samples) { + MemoryStream* sampleStream = new MemoryStream(); + BinaryWriter sampleWriter = BinaryWriter(sampleStream); + + WriteSampleEntry(pair.second, &sampleWriter); + + std::string basePath = GetSampleEntryStr(audio, pair.second); + + std::string fName = OTRExporter_DisplayList::GetPathToRes(res, basePath); + fName += "_META"; + AddFile(fName, sampleStream->ToVector()); + } +} + +void OTRExporter_Audio::WriteSampleXML(ZAudio* audio) { + ZResource* res = audio; + + for (const auto& pair : audio->samples) { + tinyxml2::XMLDocument sample; + tinyxml2::XMLElement* root = sample.NewElement("Sample"); + root->SetAttribute("Version", 0); + + WriteSampleEntry(pair.second, root); + + // There is no overload for size_t. MSVC and GCC are fine with `size` being cast + // to size_t and passed in, but apple clang is not. + root->SetAttribute("Size", (uint64_t)pair.second->data.size()); + sample.InsertEndChild(root); + + std::string sampleDataPath = GetSampleDataStr(audio, pair.second); + sampleDataPath = OTRExporter_DisplayList::GetPathToRes(res, sampleDataPath); + + root->SetAttribute("Path", sampleDataPath.c_str()); + + std::string basePath = GetSampleEntryStr(audio, pair.second); + std::string fName = OTRExporter_DisplayList::GetPathToRes(res, basePath); + + fName += "_META"; + + tinyxml2::XMLPrinter printer; + sample.Accept(&printer); + std::vector xmlData((printer.CStr()), printer.CStr() + printer.CStrSize() - 1); + AddFile(fName, xmlData); + + MemoryStream* stream = new MemoryStream(); + BinaryWriter sampleDataWriter = BinaryWriter(stream); + + sampleDataWriter.Write((char*)pair.second->data.data(), pair.second->data.size()); + AddFile(sampleDataPath, stream->ToVector()); + } +} + +void OTRExporter_Audio::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZAudio* audio = (ZAudio*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_Audio), 2); + + // Write Samples as individual files + if (Globals::Instance->xmlExtractModes & (1 << (int)XMLModeShift::Sample)) + WriteSampleXML(audio); + else + WriteSampleBinary(audio); + + + // Write the soundfont table + if (Globals::Instance->xmlExtractModes & (1 << (int)XMLModeShift::SoundFont)) + WriteSoundFontTableXML(audio); + else + WriteSoundFontTableBinary(audio); + + // Write Sequences + if (Globals::Instance->xmlExtractModes & (1 << (int)XMLModeShift::Sequence)) + WriteSequenceXML(audio); + else + WriteSequenceBinary(audio); +} diff --git a/OTRExporter/OTRExporter/AudioExporter.h b/OTRExporter/OTRExporter/AudioExporter.h new file mode 100644 index 000000000..97c6142ca --- /dev/null +++ b/OTRExporter/OTRExporter/AudioExporter.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ZResource.h" +#include "ZAudio.h" +#include "Exporter.h" +#include +#include + +class OTRExporter_Audio : public OTRExporter +{ +public: + void WriteSampleEntry(SampleEntry* entry, BinaryWriter* writer); + void WriteSampleEntry(SampleEntry* entry, tinyxml2::XMLElement* writer); + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; + +private: + void WriteSoundFontTableBinary(ZAudio* audio); + void WriteSoundFontTableXML(ZAudio* audio); + void WriteSequenceBinary(ZAudio* audio); + void WriteSequenceXML(ZAudio* audio); + void WriteSampleBinary(ZAudio* audio); + void WriteSampleXML(ZAudio* audio); + std::string GetSampleEntryReference(ZAudio* audio, SampleEntry* entry); + std::string GetSampleEntryStr(ZAudio* audio, SampleEntry* entry); + std::string GetSampleDataStr(ZAudio* audio, SampleEntry* entry); + void WriteEnvData(std::vector envelopes, BinaryWriter* writer); + void WriteEnvData(std::vector envelopes, tinyxml2::XMLElement* xmlDoc); + void WriteSoundFontEntry(ZAudio* audio, SoundFontEntry* entry, BinaryWriter* writer); + void WriteSoundFontEntry(ZAudio* audio, SoundFontEntry* entry, tinyxml2::XMLElement* xmlDoc, const char* name); + const char* GetMediumStr(uint8_t medium); + const char* GetCachePolicyStr(uint8_t policy); + const char* GetCodecStr(uint8_t codec); +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/BackgroundExporter.cpp b/OTRExporter/OTRExporter/BackgroundExporter.cpp new file mode 100644 index 000000000..0c04ec4bf --- /dev/null +++ b/OTRExporter/OTRExporter/BackgroundExporter.cpp @@ -0,0 +1,14 @@ +#include "BackgroundExporter.h" +#include "../ZAPD/ZFile.h" + +void OTRExporter_Background::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZBackground* bg = (ZBackground*)res; + + WriteHeader(bg, outPath, writer, static_cast(SOH::ResourceType::SOH_Background)); + + writer->Write((uint32_t)bg->GetRawDataSize()); + + auto data = bg->parent->GetRawData(); + writer->Write((char*)data.data() + bg->GetRawDataIndex(), bg->GetRawDataSize()); +} \ No newline at end of file diff --git a/OTRExporter/OTRExporter/BackgroundExporter.h b/OTRExporter/OTRExporter/BackgroundExporter.h new file mode 100644 index 000000000..30c1adbbc --- /dev/null +++ b/OTRExporter/OTRExporter/BackgroundExporter.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ZResource.h" +#include "ZBackground.h" +#include "Exporter.h" +#include + +class OTRExporter_Background : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/BlobExporter.cpp b/OTRExporter/OTRExporter/BlobExporter.cpp new file mode 100644 index 000000000..e69ce3199 --- /dev/null +++ b/OTRExporter/OTRExporter/BlobExporter.cpp @@ -0,0 +1,21 @@ +#include "BlobExporter.h" +#include "../ZAPD/ZFile.h" + +void OTRExporter_Blob::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZBlob* blob = (ZBlob*)res; + + WriteHeader(blob, outPath, writer, static_cast(Ship::ResourceType::Blob)); + + auto start = std::chrono::steady_clock::now(); + + writer->Write((uint32_t)blob->GetRawDataSize()); + + auto data = blob->parent->GetRawData(); + + for (size_t i = blob->GetRawDataIndex(); i < blob->GetRawDataIndex() + blob->GetRawDataSize(); i++) + writer->Write(data[i]); + + auto end = std::chrono::steady_clock::now(); + size_t diff = std::chrono::duration_cast(end - start).count(); +} \ No newline at end of file diff --git a/OTRExporter/OTRExporter/BlobExporter.h b/OTRExporter/OTRExporter/BlobExporter.h new file mode 100644 index 000000000..43a262d5e --- /dev/null +++ b/OTRExporter/OTRExporter/BlobExporter.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ZResource.h" +#include "ZBlob.h" +#include "Exporter.h" +#include + +class OTRExporter_Blob : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/CKeyFrameExporter.cpp b/OTRExporter/OTRExporter/CKeyFrameExporter.cpp new file mode 100644 index 000000000..f80a46af6 --- /dev/null +++ b/OTRExporter/OTRExporter/CKeyFrameExporter.cpp @@ -0,0 +1,105 @@ +#ifdef GAME_MM +#include "CKeyFrameExporter.h" +#include "DisplayListExporter.h" +#include "Globals.h" +#include "spdlog/spdlog.h" + +// The Win32 API defines this function for its own uses. +#ifdef FindResource +#undef FindResource +#endif + +void OTRExporter_CKeyFrameSkel::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) { + ZKeyFrameSkel* skel = (ZKeyFrameSkel*)res; + ZKeyFrameLimbList* list = skel->limbList.get(); + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::TSH_CKeyFrameSkel)); + + writer->Write(skel->limbCount); + writer->Write(skel->dListCount); + writer->Write((uint8_t)skel->limbType); + writer->Write(list->numLimbs); + + + for (size_t i = 0; i < list->limbs.size(); i++) { + ZKeyFrameLimb* limb = list->limbs[i]; + std::string name = ""; + if (GETSEGOFFSET(limb->dlist) != SEGMENTED_NULL) { + bool found = Globals::Instance->GetSegmentedPtrName(GETSEGOFFSET(limb->dlist), res->parent, "", name, + res->parent->workerID); + + if (!found) { + ZDisplayList* dl = (ZDisplayList*)res->parent->FindResource(limb->dlist & 0x00FFFFFF); + if (dl != nullptr) { + name = dl->GetName(); + found = true; + } + } + if (found) { + if (name.at(0) == '&') + name.erase(0, 1); + + name = OTRExporter_DisplayList::GetPathToRes(res, name); + } else { + spdlog::error("Texture not found: 0x{:X}", limb->dlist); + //writer->Write(""); + name = ""; + } + } + writer->Write(name); + writer->Write(limb->numChildren); + writer->Write(limb->flags); + + if (skel->limbType == ZKeyframeSkelType::Normal) { + ZKeyFrameStandardLimb* stdLimb = (ZKeyFrameStandardLimb*)limb; + + writer->Write(stdLimb->translation.x); + writer->Write(stdLimb->translation.y); + writer->Write(stdLimb->translation.z); + } else { + ZKeyFrameFlexLimb* flexLimb = (ZKeyFrameFlexLimb*)limb; + + writer->Write(flexLimb->callbackIndex); + } + } +} + +void OTRExporter_CKeyFrameAnim::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) { + ZKeyFrameAnim* anim = (ZKeyFrameAnim*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::TSH_CKeyFrameAnim)); + + writer->Write((uint8_t)anim->skel->limbType); + if (anim->skel->limbType == ZKeyframeSkelType::Normal) { + writer->Write((uint32_t)anim->bitFlags.size()); + for (const auto b : anim->bitFlags) { + writer->Write(b); + } + } else { + writer->Write((uint32_t)anim->bitFlagsFlex.size()); + for (const auto b : anim->bitFlagsFlex) { + writer->Write(b); + } + } + + writer->Write((uint32_t)anim->keyFrames.size()); + for (const auto k : anim->keyFrames) { + writer->Write(k.frame); + writer->Write(k.value); + writer->Write(k.velocity); + } + + writer->Write((uint32_t)anim->kfNums.size()); + for (const auto kf : anim->kfNums) { + writer->Write(kf); + } + + writer->Write((uint32_t)anim->presetValues.size()); + for (const auto pv : anim->presetValues) { + writer->Write(pv); + } + + writer->Write(anim->unk_10); + writer->Write(anim->duration); +} +#endif diff --git a/OTRExporter/OTRExporter/CKeyFrameExporter.h b/OTRExporter/OTRExporter/CKeyFrameExporter.h new file mode 100644 index 000000000..ec6e0bc7f --- /dev/null +++ b/OTRExporter/OTRExporter/CKeyFrameExporter.h @@ -0,0 +1,19 @@ +#pragma once +#ifdef GAME_MM +#include "ZResource.h" +#include "ZCKeyFrame.h" +#include "ZCkeyFrameAnim.h" +#include "Exporter.h" +#include + +class OTRExporter_CKeyFrameSkel : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; + +class OTRExporter_CKeyFrameAnim : public OTRExporter { + public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; +#endif diff --git a/OTRExporter/OTRExporter/CMakeLists.txt b/OTRExporter/OTRExporter/CMakeLists.txt new file mode 100644 index 000000000..fd9e9a742 --- /dev/null +++ b/OTRExporter/OTRExporter/CMakeLists.txt @@ -0,0 +1,213 @@ +set(PROJECT_NAME OTRExporter) + +################################################################################ +# Source groups +################################################################################ +set(Header_Files + "AnimationExporter.h" + "ArrayExporter.h" + "AudioExporter.h" + "BackgroundExporter.h" + "BlobExporter.h" + "CKeyFrameExporter.h" + "CollisionExporter.h" + "command_macros_base.h" + "CutsceneExporter.h" + "DisplayListExporter.h" + "Exporter.h" + "ExporterArchive.h" + "ExporterArchiveO2R.h" + "ExporterArchiveOTR.h" + "Main.h" + "MtxExporter.h" + "PathExporter.h" + "PlayerAnimationExporter.h" + "RoomExporter.h" + "SkeletonExporter.h" + "SkeletonLimbExporter.h" + "TextExporter.h" + "TextMMExporter.h" + "TextureExporter.h" + "TextureAnimationExporter.h" + "VersionInfo.h" + "VtxExporter.h" + "z64cutscene.h" + "z64cutscene_commands.h" +) +source_group("Header Files" FILES ${Header_Files}) + +set(Source_Files + "AnimationExporter.cpp" + "ArrayExporter.cpp" + "AudioExporter.cpp" + "BackgroundExporter.cpp" + "BlobExporter.cpp" + "CollisionExporter.cpp" + "CKeyFrameExporter.cpp" + "CutsceneExporter.cpp" + "DisplayListExporter.cpp" + "Exporter.cpp" + "ExporterArchive.cpp" + "ExporterArchiveO2R.cpp" + "ExporterArchiveOTR.cpp" + "Main.cpp" + "MtxExporter.cpp" + "PathExporter.cpp" + "PlayerAnimationExporter.cpp" + "RoomExporter.cpp" + "SkeletonExporter.cpp" + "SkeletonLimbExporter.cpp" + "TextExporter.cpp" + "TextMMExporter.cpp" + "TextureExporter.cpp" + "TextureAnimationExporter.cpp" + "VersionInfo.cpp" + "VtxExporter.cpp" +) +source_group("Source Files" FILES ${Source_Files}) + +set(ALL_FILES + ${Header_Files} + ${Source_Files} +) + +################################################################################ +# Target +################################################################################ +add_library(${PROJECT_NAME} STATIC ${ALL_FILES}) + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") +use_props(${PROJECT_NAME} "${CMAKE_CONFIGURATION_TYPES}" "${DEFAULT_CXX_PROPS}") +endif() + +set(ROOT_NAMESPACE OTRExporter) + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties(${PROJECT_NAME} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE "TRUE" + ) +endif() +################################################################################ +# MSVC runtime library +################################################################################ +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + get_property(MSVC_RUNTIME_LIBRARY_DEFAULT TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY) + string(CONCAT "MSVC_RUNTIME_LIBRARY_STR" + $<$: + MultiThreadedDebug + > + $<$: + MultiThreaded + > + $<$,$>>:${MSVC_RUNTIME_LIBRARY_DEFAULT}> + ) + set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY ${MSVC_RUNTIME_LIBRARY_STR}) +endif() +################################################################################ +# Compile definitions +################################################################################ +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + target_compile_definitions(${PROJECT_NAME} PRIVATE + "$<$:" + "_DEBUG" + ">" + "$<$:" + "NDEBUG" + ">" + "_CONSOLE;" + "_CRT_SECURE_NO_WARNINGS;" + STORMLIB_NO_AUTO_LINK + ) +endif() + +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") + target_compile_definitions(${PROJECT_NAME} PRIVATE + "$<$:" + "_DEBUG" + ">" + "$<$:" + "NDEBUG" + ">" + "_CONSOLE;" + "_CRT_SECURE_NO_WARNINGS;" + ) +endif() +################################################################################ +# Compile and link options +################################################################################ + +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../ZAPDTR/ZAPD/ + ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/include + # TODO: these should no longer be necessary if we were to link against LUS + ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship + ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/src + ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/extern + ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/src/resource + ${CMAKE_CURRENT_SOURCE_DIR}/../../mm/2s2h + . +) + +find_package(nlohmann_json REQUIRED) +target_link_libraries(${PROJECT_NAME} PUBLIC nlohmann_json::nlohmann_json) + +find_package(spdlog REQUIRED) +target_link_libraries(${PROJECT_NAME} PUBLIC spdlog::spdlog) + +FetchContent_Declare( + StormLib + GIT_REPOSITORY https://github.com/ladislav-zezula/StormLib.git + GIT_TAG v9.25 +) +FetchContent_MakeAvailable(StormLib) +target_include_directories(${PROJECT_NAME} PRIVATE ${stormlib_SOURCE_DIR}/src) + +if(MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE + $<$: + /Od; + /Oi- + > + $<$: + /Oi; + /Gy + /O2 + > + /permissive-; + /sdl; + /W3; + ${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT}; + ${DEFAULT_CXX_EXCEPTION_HANDLING} + ) + target_link_options(${PROJECT_NAME} PRIVATE + $<$: + /OPT:REF; + /OPT:ICF + /INCREMENTAL:NO + > + /SUBSYSTEM:CONSOLE + ) +endif() + +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") + target_compile_options(${PROJECT_NAME} PRIVATE + -Wall -Wextra -Wno-error + -Wno-unused-parameter + -Wno-unused-function + -Wno-unused-variable + -Wno-missing-field-initializers + -Wno-parentheses + -Wno-narrowing + $<$:-Wno-deprecated-enum-enum-conversion> + ) +endif() +################################################################################ +# Dependencies +################################################################################ +add_dependencies(${PROJECT_NAME} + libultraship +) + +# Link with other targets. +target_link_libraries(${PROJECT_NAME} PUBLIC "${ADDITIONAL_LIBRARY_DEPENDENCIES}") + diff --git a/OTRExporter/OTRExporter/CollisionExporter.cpp b/OTRExporter/OTRExporter/CollisionExporter.cpp new file mode 100644 index 000000000..9722cbac0 --- /dev/null +++ b/OTRExporter/OTRExporter/CollisionExporter.cpp @@ -0,0 +1,84 @@ +#include "CollisionExporter.h" +#include + +void OTRExporter_Collision::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZCollisionHeader* col = (ZCollisionHeader*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_CollisionHeader)); + + writer->Write(col->absMinX); + writer->Write(col->absMinY); + writer->Write(col->absMinZ); + + writer->Write(col->absMaxX); + writer->Write(col->absMaxY); + writer->Write(col->absMaxZ); + + writer->Write((uint32_t)col->vertices.size()); + + for (uint16_t i = 0; i < col->vertices.size(); i++) + { + writer->Write(col->vertices[i].scalars[0].scalarData.s16); + writer->Write(col->vertices[i].scalars[1].scalarData.s16); + writer->Write(col->vertices[i].scalars[2].scalarData.s16); + } + + writer->Write((uint32_t)col->polygons.size()); + + for (uint16_t i = 0; i < col->polygons.size(); i++) + { + writer->Write(col->polygons[i].type); + writer->Write(col->polygons[i].vtxA); + writer->Write(col->polygons[i].vtxB); + writer->Write(col->polygons[i].vtxC); + writer->Write(col->polygons[i].normX); + writer->Write(col->polygons[i].normY); + writer->Write(col->polygons[i].normZ); + writer->Write(col->polygons[i].dist); + } + + writer->Write((uint32_t)col->polygonTypes.size()); + + for (uint16_t i = 0; i < col->polygonTypes.size(); i++) { + writer->Write(col->polygonTypes[i].data[1]); + writer->Write(col->polygonTypes[i].data[0]); + } + + writer->Write((uint32_t)col->camData->entries.size()); + + for (auto entry : col->camData->entries) + { + auto camPosDecl = col->parent->GetDeclarationRanged(Seg2Filespace(entry.cameraPosDataSeg, col->parent->baseAddress)); + + int idx = 0; + + if (camPosDecl != nullptr) + idx = ((entry.cameraPosDataSeg & 0x00FFFFFF) - camPosDecl->address) / 6; + + writer->Write(entry.cameraSType); + writer->Write(entry.numData); + writer->Write((uint32_t)idx); + } + + writer->Write((uint32_t)col->camData->cameraPositionData.size()); + + for (auto entry : col->camData->cameraPositionData) + { + writer->Write(entry.x); + writer->Write(entry.y); + writer->Write(entry.z); + } + + writer->Write((uint32_t)col->waterBoxes.size()); + + for (auto waterBox : col->waterBoxes) + { + writer->Write(waterBox.xMin); + writer->Write(waterBox.ySurface); + writer->Write(waterBox.zMin); + writer->Write(waterBox.xLength); + writer->Write(waterBox.zLength); + writer->Write(waterBox.properties); + } +} diff --git a/OTRExporter/OTRExporter/CollisionExporter.h b/OTRExporter/OTRExporter/CollisionExporter.h new file mode 100644 index 000000000..536d65344 --- /dev/null +++ b/OTRExporter/OTRExporter/CollisionExporter.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ZResource.h" +#include "ZCollision.h" +#include "Exporter.h" + +class OTRExporter_Collision : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/CutsceneExporter.cpp b/OTRExporter/OTRExporter/CutsceneExporter.cpp new file mode 100644 index 000000000..06897885f --- /dev/null +++ b/OTRExporter/OTRExporter/CutsceneExporter.cpp @@ -0,0 +1,606 @@ +#include "CutsceneExporter.h" +#include "Globals.h" +#include + +void OTRExporter_Cutscene::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) { + ZCutscene* cs = (ZCutscene*)res; + + WriteHeader(cs, outPath, writer, static_cast(SOH::ResourceType::SOH_Cutscene)); + + writer->Write((uint32_t)0); + + const auto currentStream = writer->GetBaseAddress(); + + writer->Write(CS_BEGIN_CUTSCENE(cs->numCommands, cs->endFrame)); + + if (Globals::Instance->game == ZGame::MM_RETAIL) { + SaveMM(cs, writer); + } else { + SaveOot(cs, writer); + } + + + // CS_END + writer->Write(0xFFFFFFFF); + writer->Write((uint32_t)0); + + const auto endStream = writer->GetBaseAddress(); + writer->Seek((uint32_t)currentStream - 4, SeekOffsetType::Start); + writer->Write((uint32_t)((endStream - currentStream) / 4)); + writer->Seek((uint32_t)endStream, SeekOffsetType::Start); +} + +void OTRExporter_Cutscene::SaveOot(ZCutscene* cs, BinaryWriter* writer) { + + for (size_t i = 0; i < cs->commands.size(); i++) { + switch (cs->commands[i]->commandID) { + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_EYE_SPLINE: { + CutsceneOoTCommand_GenericCameraCmd* cmdCamPos = (CutsceneOoTCommand_GenericCameraCmd*)cs->commands[i]; + + writer->Write(CS_CMD_CAM_EYE); + writer->Write(CMD_HH(0x0001, ((CutsceneOoTCommand_GenericCameraCmd*)cs->commands[i])->startFrame)); + writer->Write(CMD_HH(cmdCamPos->endFrame, 0x0000)); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTCommand_CameraPoint* point = (CutsceneOoTCommand_CameraPoint*)e; + writer->Write(CMD_BBH(point->continueFlag, point->cameraRoll, point->nextPointFrame)); + writer->Write(point->viewAngle); + writer->Write(CMD_HH(point->posX, point->posY)); + writer->Write(CMD_HH(point->posZ, point->unused)); + } + } break; + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_AT_SPLINE: { + CutsceneOoTCommand_GenericCameraCmd* cmdCamPos = (CutsceneOoTCommand_GenericCameraCmd*)cs->commands[i]; + + writer->Write(CS_CMD_CAM_AT); + writer->Write(CMD_HH(0x0001, cmdCamPos->startFrame)); + writer->Write(CMD_HH(cmdCamPos->endFrame, 0x0000)); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTCommand_CameraPoint* point = (CutsceneOoTCommand_CameraPoint*)e; + writer->Write(CMD_BBH(point->continueFlag, point->cameraRoll, point->nextPointFrame)); + writer->Write(point->viewAngle); + writer->Write(CMD_HH(point->posX, point->posY)); + writer->Write(CMD_HH(point->posZ, point->unused)); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_MISC: { + writer->Write(CS_CMD_MISC); + writer->Write((uint32_t)CMD_W((cs->commands[i])->entries.size())); + for (const auto& e : cs->commands[i]->entries) // All in OOT seem to only have 1 entry + { + CutsceneOoTSubCommandEntry_GenericCmd* cmd = (CutsceneOoTSubCommandEntry_GenericCmd*)e; + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HH(cmd->endFrame, cmd->pad)); + writer->Write(CMD_W(cmd->unused1)); + writer->Write(CMD_W(cmd->unused2)); + writer->Write(CMD_W(cmd->unused3)); + writer->Write(CMD_W(cmd->unused4)); + writer->Write(CMD_W(cmd->unused5)); + writer->Write(CMD_W(cmd->unused6)); + writer->Write(CMD_W(cmd->unused7)); + writer->Write(CMD_W(cmd->unused8)); + writer->Write(CMD_W(cmd->unused9)); + writer->Write(CMD_W(cmd->unused10)); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_LIGHT_SETTING: { + writer->Write(CS_CMD_SET_LIGHTING); + writer->Write((uint32_t)CMD_W((cs->commands[i])->entries.size())); + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTSubCommandEntry_GenericCmd* cmd = (CutsceneOoTSubCommandEntry_GenericCmd*)e; + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HH(cmd->endFrame, cmd->pad)); + writer->Write(CMD_W(cmd->unused1)); + writer->Write(CMD_W(cmd->unused2)); + writer->Write(CMD_W(cmd->unused3)); + writer->Write(CMD_W(cmd->unused4)); + writer->Write(CMD_W(cmd->unused5)); + writer->Write(CMD_W(cmd->unused6)); + writer->Write(CMD_W(cmd->unused7)); + writer->Write((uint32_t)0x0); + writer->Write((uint32_t)0x0); + writer->Write((uint32_t)0x0); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_EYE_SPLINE_REL_TO_PLAYER: { + CutsceneOoTCommand_GenericCameraCmd* cmdCamPos = (CutsceneOoTCommand_GenericCameraCmd*)cs->commands[i]; + + writer->Write(CS_CMD_CAM_EYE_REL_TO_PLAYER); + writer->Write(CMD_HH(0x0001, cmdCamPos->startFrame)); + writer->Write(CMD_HH(cmdCamPos->endFrame, 0x0000)); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTCommand_CameraPoint* point = (CutsceneOoTCommand_CameraPoint*)e; + writer->Write(CMD_BBH(point->continueFlag, point->cameraRoll, point->nextPointFrame)); + writer->Write(point->viewAngle); + writer->Write(CMD_HH(point->posX, point->posY)); + writer->Write(CMD_HH(point->posZ, point->unused)); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_AT_SPLINE_REL_TO_PLAYER: { + CutsceneOoTCommand_GenericCameraCmd* cmdCamPos = (CutsceneOoTCommand_GenericCameraCmd*)cs->commands[i]; + + writer->Write(CS_CMD_CAM_AT_REL_TO_PLAYER); + writer->Write(CMD_HH(0x0001, cmdCamPos->startFrame)); + writer->Write(CMD_HH(cmdCamPos->endFrame, 0x0000)); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTCommand_CameraPoint* point = (CutsceneOoTCommand_CameraPoint*)e; + writer->Write(CMD_BBH(point->continueFlag, point->cameraRoll, point->nextPointFrame)); + writer->Write(point->viewAngle); + writer->Write(CMD_HH(point->posX, point->posY)); + writer->Write(CMD_HH(point->posZ, point->unused)); + } + break; + } + + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_EYE: // Not used in OOT + break; + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_AT: // Not used in OOT + break; + + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_RUMBLE_CONTROLLER: { + writer->Write(CS_CMD_09); + writer->Write((uint32_t)CMD_W(((CutsceneOoTCommand_Rumble*)cs->commands[i])->entries.size())); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTSubCommandEntry_Rumble* r = (CutsceneOoTSubCommandEntry_Rumble*)e; + writer->Write(CMD_HH(r->base, r->startFrame)); + writer->Write(CMD_HBB(e->endFrame, r->sourceStrength, r->duration)); + writer->Write(CMD_BBH(r->decreaseRate, r->unk_09, r->unk_0A)); + } + break; + } + // case 0x15://Both unused in OoT + // case 0x1A://(uint32_t)CutsceneOoT_CommandType::Unknown: + { +#if 0 + CutsceneCommandUnknown* cmdUnk = (CutsceneCommandUnknown*)cs->commands[i]; + writer->Write((uint32_t)cs->commands[i]->commandID); + writer->Write((uint32_t)cmdUnk->entries.size()); + + for (const auto e : cmdUnk->entries) + { + writer->Write(CMD_W(e->unused0)); + writer->Write(CMD_W(e->unused1)); + writer->Write(CMD_W(e->unused2)); + writer->Write(CMD_W(e->unused3)); + writer->Write(CMD_W(e->unused4)); + writer->Write(CMD_W(e->unused5)); + writer->Write(CMD_W(e->unused6)); + writer->Write(CMD_W(e->unused7)); + writer->Write(CMD_W(e->unused8)); + writer->Write(CMD_W(e->unused9)); + writer->Write(CMD_W(e->unused10)); + writer->Write(CMD_W(e->unused11)); + } +#endif + } + break; + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_TEXT: { + writer->Write(CS_CMD_TEXTBOX); + writer->Write((uint32_t)CMD_W((cs->commands[i])->entries.size())); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTSubCommandEntry_Text* textBox = (CutsceneOoTSubCommandEntry_Text*)e; + if (textBox->base == 0xFFFF) // CS_TEXT_NONE + { + writer->Write(CMD_HH(0xFFFF, textBox->startFrame)); + writer->Write(CMD_HH(textBox->endFrame, 0xFFFF)); + writer->Write(CMD_HH(0xFFFF, 0xFFFF)); + } else // CS_TEXT_DISPLAY_TEXTBOX + { + writer->Write(CMD_HH(textBox->base, textBox->startFrame)); + writer->Write(CMD_HH(textBox->endFrame, textBox->type)); + writer->Write(CMD_HH(textBox->textId1, textBox->textId2)); + } + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_PLAYER_CUE: // ActorAction0 + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_7: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_8: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_9: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_10: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_11: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_12: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_13: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_14: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_15: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_16: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_17: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_7: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_8: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_9: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_10: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_11: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_12: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_13: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_14: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_15: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_16: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_17: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_7: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_8: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_9: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_10: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_11: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_12: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_13: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_7: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_8: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_9: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_10: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_11: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_12: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_7: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_8: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_7: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_1: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_2: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_3: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_4: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_5: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_6: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_8_0: + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_9_0: { + writer->Write((uint32_t)cs->commands[i]->commandID); + writer->Write((uint32_t)CMD_W(cs->commands[i]->entries.size())); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTSubCommandEntry_ActorCue* actorAct = (CutsceneOoTSubCommandEntry_ActorCue*)e; + writer->Write(CMD_HH(actorAct->base, actorAct->startFrame)); + writer->Write(CMD_HH(actorAct->endFrame, actorAct->rotX)); + writer->Write(CMD_HH(actorAct->rotY, actorAct->rotZ)); + writer->Write(CMD_W(actorAct->startPosX)); + writer->Write(CMD_W(actorAct->startPosY)); + writer->Write(CMD_W(actorAct->startPosZ)); + writer->Write(CMD_W(actorAct->endPosX)); + writer->Write(CMD_W(actorAct->endPosY)); + writer->Write(CMD_W(actorAct->endPosZ)); + writer->Write(CMD_W(actorAct->normalX)); + writer->Write(CMD_W(actorAct->normalY)); + writer->Write(CMD_W(actorAct->normalZ)); + } + + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_TRANSITION: { + CutsceneOoTCommand_Transition* cmdTFX = (CutsceneOoTCommand_Transition*)cs->commands[i]; + + writer->Write(CS_CMD_SCENE_TRANS_FX); + writer->Write((uint32_t)1); + writer->Write(CMD_HH((cmdTFX->base), cmdTFX->startFrame)); + writer->Write(CMD_HH((cmdTFX->endFrame), cmdTFX->endFrame)); + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_START_SEQ: { + writer->Write(CS_CMD_PLAYBGM); + writer->Write((uint32_t)CMD_W(cs->commands[i]->entries.size())); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTSubCommandEntry_GenericCmd* cmd = (CutsceneOoTSubCommandEntry_GenericCmd*)e; + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HH(cmd->endFrame, cmd->pad)); + writer->Write(CMD_W(cmd->unused1)); + writer->Write(CMD_W(cmd->unused2)); + writer->Write(CMD_W(cmd->unused3)); + writer->Write(CMD_W(cmd->unused4)); + writer->Write(CMD_W(cmd->unused5)); + writer->Write(CMD_W(cmd->unused6)); + writer->Write(CMD_W(cmd->unused7)); + writer->Write((uint32_t)0); + writer->Write((uint32_t)0); + writer->Write((uint32_t)0); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_STOP_SEQ: { + writer->Write(CS_CMD_STOPBGM); + writer->Write((uint32_t)CMD_W(cs->commands[i]->entries.size())); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTSubCommandEntry_GenericCmd* cmd = (CutsceneOoTSubCommandEntry_GenericCmd*)e; + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HH(cmd->endFrame, cmd->pad)); + writer->Write(CMD_W(cmd->unused1)); + writer->Write(CMD_W(cmd->unused2)); + writer->Write(CMD_W(cmd->unused3)); + writer->Write(CMD_W(cmd->unused4)); + writer->Write(CMD_W(cmd->unused5)); + writer->Write(CMD_W(cmd->unused6)); + writer->Write(CMD_W(cmd->unused7)); + writer->Write((uint32_t)0); + writer->Write((uint32_t)0); + writer->Write((uint32_t)0); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_FADE_OUT_SEQ: { + writer->Write(CS_CMD_FADEBGM); + writer->Write((uint32_t)CMD_W(cs->commands[i]->entries.size())); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneOoTSubCommandEntry_GenericCmd* cmd = (CutsceneOoTSubCommandEntry_GenericCmd*)e; + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HH(cmd->endFrame, cmd->pad)); + writer->Write(CMD_W(cmd->unused1)); + writer->Write(CMD_W(cmd->unused2)); + writer->Write(CMD_W(cmd->unused3)); + writer->Write(CMD_W(cmd->unused4)); + writer->Write(CMD_W(cmd->unused5)); + writer->Write(CMD_W(cmd->unused6)); + writer->Write(CMD_W(cmd->unused7)); + writer->Write((uint32_t)0); + writer->Write((uint32_t)0); + writer->Write((uint32_t)0); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_TIME: { + writer->Write(CS_CMD_SETTIME); + writer->Write((uint32_t)CMD_W(cs->commands[i]->entries.size())); + + for (const auto& e : cs->commands[i]->entries) { + CutsceneSubCommandEntry_SetTime* t = (CutsceneSubCommandEntry_SetTime*)e; + writer->Write(CMD_HH(t->base, t->startFrame)); + writer->Write(CMD_HBB(t->endFrame, t->hour, t->minute)); + writer->Write((uint32_t)0); + // writer->Write((uint32_t)CMD_W(t->unk_08)); + } + break; + } + case (uint32_t)CutsceneOoT_CommandType::CS_CMD_DESTINATION: { + CutsceneOoTCommand_Destination* t = (CutsceneOoTCommand_Destination*)cs->commands[i]; + writer->Write(CS_CMD_TERMINATOR); + writer->Write((uint32_t)1); + writer->Write(CMD_HH(t->base, t->startFrame)); + writer->Write(CMD_HH(t->endFrame, t->endFrame)); + break; + } + default: { + // writer->Write((uint32_t)cs->commands[i]->commandID); + printf("Undefined CS Opcode: %04X\n", cs->commands[i]->commandID); + } break; + } + } +} +// MUST be uint16_t for -1 +typedef enum : uint16_t { + /* -1 */ CS_TEXT_TYPE_NONE = (uint16_t)-1, + /* 0 */ CS_TEXT_TYPE_DEFAULT = 0, + /* 1 */ CS_TEXT_TYPE_1, + /* 2 */ CS_TEXT_OCARINA_ACTION, + /* 3 */ CS_TEXT_TYPE_3, + /* 4 */ CS_TEXT_TYPE_BOSSES_REMAINS, // use `altText1` in the Giant Cutscene if all remains are already obtained + /* 5 */ CS_TEXT_TYPE_ALL_NORMAL_MASKS +} CutsceneTextType; + +typedef enum : uint8_t { + /* 0 */ Header, + /* 1 */ CamPoint, + /* 2 */ CamMisc, + /* 3 */ Footer, +} CutsceneSplineType; + +void OTRExporter_Cutscene::SaveMM(ZCutscene* cs, BinaryWriter* writer) { + for (size_t i = 0; i < cs->commands.size(); i++) { + writer->Write((uint32_t)cs->commands[i]->commandID); + + if (((cs->commands[i]->commandID >= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_100) && + (cs->commands[i]->commandID <= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_149)) || + (cs->commands[i]->commandID == (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_201) || + ((cs->commands[i]->commandID >= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_450) && + (cs->commands[i]->commandID <= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_599))) { + goto actorCue; + } + + switch ((CutsceneMM_CommandType)cs->commands[i]->commandID) { + case CutsceneMM_CommandType::CS_CMD_TEXT: { + writer->Write((uint32_t)cs->commands[i]->entries.size()); + for (const auto& e : cs->commands[i]->entries) { + auto* cmd = (CutsceneMMSubCommandEntry_Text*)e; + + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HH(cmd->endFrame, cmd->type)); + writer->Write(CMD_HH(cmd->textId1, cmd->textId2)); + } + break; + } + + // BENTODO: Can these stay consilidated? + case CutsceneMM_CommandType::CS_CMD_CAMERA_SPLINE: { + // This command uses 4 different macros that are all different sizes and uses the number of bytes of the + // whole command instead of entries like most other commands. numEntries isn't actually the number of entries in the command + // rather the number of bytes the command will take up in the rom. We also can not simply use GetCommandSize because it returns + // a larger size to help ZAPD know where to start reading for the next command. + writer->Write((uint32_t)cs->commands[i]->numEntries); + // monkaS + size_t j = 0; + for (;;) { + auto* header = dynamic_cast(cs->commands[i]->entries[j]); + + //Debugging check, can probably remove after testing + assert(header != nullptr); + uint32_t numEntries = header->numEntries; + writer->Write(CMD_HH(header->numEntries, header->unused0)); + writer->Write(CMD_HH(header->unused1, header->duration)); + + for (size_t k = 0; k < numEntries * 2; k++) { + auto* element = + dynamic_cast(cs->commands[i]->entries[++j]); + // Debugging check, can probably remove after testing + assert(element != nullptr); + writer->Write(CMD_BBH(element->interpType, element->weight, element->duration)); + writer->Write(CMD_HH(element->posX, element->posY)); + writer->Write(CMD_HH(element->posZ, element->relTo)); + } + for (size_t k = 0; k < numEntries; k++) { + auto* element = + dynamic_cast(cs->commands[i]->entries[++j]); + // Debugging check, can probably remove after testing + assert(element != nullptr); + writer->Write(CMD_HH(element->unused0, element->roll)); + writer->Write(CMD_HH(element->fov, element->unused1)); + } + + auto* header2 = dynamic_cast(cs->commands[i]->entries[++j]); + if (header2 != nullptr) { + if (header2->base == 0xFFFF) { + break; + } + } + } + + // The footer exists as an error checking type and a way to output an empty macro. It holds no actual data. + auto* footer = dynamic_cast(cs->commands[i]->entries[j]); + assert(footer != nullptr); + writer->Write((uint16_t)0xFFFF); + writer->Write((uint16_t)0x0004); + //i += j; + break; + } + case CutsceneMM_CommandType::CS_CMD_MISC: + case CutsceneMM_CommandType::CS_CMD_LIGHT_SETTING: + case CutsceneMM_CommandType::CS_CMD_TRANSITION: + case CutsceneMM_CommandType::CS_CMD_MOTION_BLUR: + case CutsceneMM_CommandType::CS_CMD_GIVE_TATL: + case CutsceneMM_CommandType::CS_CMD_STOP_SEQ: + case CutsceneMM_CommandType::CS_CMD_START_SEQ: + case CutsceneMM_CommandType::CS_CMD_SFX_REVERB_INDEX_2: + case CutsceneMM_CommandType::CS_CMD_SFX_REVERB_INDEX_1: + case CutsceneMM_CommandType::CS_CMD_MODIFY_SEQ: + case CutsceneMM_CommandType::CS_CMD_START_AMBIENCE: + case CutsceneMM_CommandType::CS_CMD_FADE_OUT_AMBIENCE: + case CutsceneMM_CommandType::CS_CMD_DESTINATION: + case CutsceneMM_CommandType::CS_CMD_CHOOSE_CREDITS_SCENES: + default: + { + writer->Write((uint32_t)cs->commands[i]->entries.size()); + for (const auto e : cs->commands[i]->entries) { + writer->Write(CMD_HH(e->base, e->startFrame)); + writer->Write(CMD_HH(e->endFrame, e->pad)); + } + break; + } + case CutsceneMM_CommandType::CS_CMD_TRANSITION_GENERAL: { + writer->Write((uint32_t)cs->commands[i]->entries.size()); + for (const auto e : cs->commands[i]->entries) { + auto* cmd = (CutsceneSubCommandEntry_TransitionGeneral*)e; + + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HBB(cmd->endFrame, cmd->unk_06, cmd->unk_07)); + writer->Write(CMD_BBBB(cmd->unk_08, 0, 0, 0)); + } + break; + } + case CutsceneMM_CommandType::CS_CMD_FADE_OUT_SEQ: { + writer->Write((uint32_t)cs->commands[i]->entries.size()); + for (const auto e : cs->commands[i]->entries) { + writer->Write(CMD_HH(e->base, e->startFrame)); + writer->Write(CMD_HH(e->endFrame, e->pad)); + writer->Write(CMD_W(0)); + } + break; + } + case CutsceneMM_CommandType::CS_CMD_TIME: { + writer->Write((uint32_t)cs->commands[i]->entries.size()); + for (const auto e : cs->commands[i]->entries) { + auto* cmd = (CutsceneSubCommandEntry_SetTime*)e; + + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HBB(cmd->endFrame, cmd->hour, cmd->minute)); + writer->Write(CMD_W(0)); + } + break; + } + case CutsceneMM_CommandType::CS_CMD_PLAYER_CUE: { + actorCue: + writer->Write((uint32_t)cs->commands[i]->entries.size()); + for (const auto e : cs->commands[i]->entries) { + auto* cmd = (CutsceneMMSubCommandEntry_ActorCue*)e; + + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HH(cmd->endFrame, cmd->rotX)); + writer->Write(CMD_HH(cmd->rotY, cmd->rotZ)); + writer->Write(CMD_W(cmd->startPosX)); + writer->Write(CMD_W(cmd->startPosY)); + writer->Write(CMD_W(cmd->startPosZ)); + writer->Write(CMD_W(cmd->endPosX)); + writer->Write(CMD_W(cmd->endPosY)); + writer->Write(CMD_W(cmd->endPosZ)); + writer->Write(CMD_F(cmd->normalX)); + writer->Write(CMD_F(cmd->normalY)); + writer->Write(CMD_F(cmd->normalZ)); + } + break; + } + case CutsceneMM_CommandType::CS_CMD_RUMBLE: { + writer->Write((uint32_t)cs->commands[i]->entries.size()); + for (const auto e : cs->commands[i]->entries) { + auto* cmd = (CutsceneMMSubCommandEntry_Rumble*)e; + + writer->Write(CMD_HH(cmd->base, cmd->startFrame)); + writer->Write(CMD_HBB(cmd->endFrame, cmd->intensity, cmd->decayTimer)); + writer->Write(CMD_BBBB(cmd->decayStep, 0, 0, 0)); + } + break; + } + } + } +} diff --git a/OTRExporter/OTRExporter/CutsceneExporter.h b/OTRExporter/OTRExporter/CutsceneExporter.h new file mode 100644 index 000000000..1afd985b5 --- /dev/null +++ b/OTRExporter/OTRExporter/CutsceneExporter.h @@ -0,0 +1,13 @@ +#pragma once +#include "ZResource.h" +#include "ZCutscene.h" +#include "z64cutscene_commands.h" +#include "Exporter.h" + +class OTRExporter_Cutscene : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; + void SaveOot(ZCutscene* res, BinaryWriter* writer); + void SaveMM(ZCutscene* res, BinaryWriter* writer); +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/DisplayListExporter.cpp b/OTRExporter/OTRExporter/DisplayListExporter.cpp new file mode 100644 index 000000000..f7bcf4109 --- /dev/null +++ b/OTRExporter/OTRExporter/DisplayListExporter.cpp @@ -0,0 +1,1089 @@ +#define F3DEX_GBI_2 +#include "DisplayListExporter.h" +#include "Main.h" +#include "../ZAPD/ZFile.h" +#include +#include +#include +#include "spdlog/spdlog.h" +#include +#include +#include +#include +#include +#include "MtxExporter.h" +#include +#include "VersionInfo.h" +#undef FindResource + +#define GFX_SIZE 8 + +#define gsDPSetCombineLERP_NoMacros(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, \ + a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ +{ \ + _SHIFTL(G_SETCOMBINE, 24, 8) | \ + _SHIFTL(GCCc0w0(a0, c0, \ + Aa0, Ac0) | \ + GCCc1w0(a1, c1), 0, 24), \ + (unsigned int)(GCCc0w1(b0, d0, \ + Ab0, Ad0) | \ + GCCc1w1(b1, Aa1, \ + Ac1, d1, \ + Ab1, Ad1)) \ +} + +#define gsSPBranchLessZraw2(dl, vtx, zval) \ +{ _SHIFTL(G_BRANCH_Z,24,8)|_SHIFTL((vtx)*5,12,12)|_SHIFTL((vtx)*2,0,12),\ + (unsigned int)(zval), } + +#define gsSPBranchLessZraw3(dl) \ +{ _SHIFTL(G_RDPHALF_1,24,8), \ + (unsigned int)(dl), } + +#define gsDPWordLo(wordlo) \ + gsImmp1(G_RDPHALF_2, (unsigned int)(wordlo)) + +#define gsSPTextureRectangle2(xl, yl, xh, yh, tile) \ +{ (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)),\ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)) } + +#define UCODE_F3DEX2 (int8_t) 4 + +void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZDisplayList* dList = (ZDisplayList*)res; + + //printf("Exporting DList %s\n", dList->GetName().c_str()); + + WriteHeader(res, outPath, writer, static_cast(Fast::ResourceType::DisplayList)); + writer->Write(UCODE_F3DEX2); + + while (writer->GetBaseAddress() % 8 != 0) + writer->Write((uint8_t)0xFF); + + // DEBUG: Write in a marker + Declaration* dbgDecl = dList->parent->GetDeclaration(dList->GetRawDataIndex()); + std::string dbgName = StringHelper::Sprintf("%s/%s", GetParentFolderName(res).c_str(), dbgDecl->declName.c_str()); + uint64_t hash = CRC64(dbgName.c_str()); + writer->Write((uint32_t)(G_MARKER << 24)); + writer->Write((uint32_t)0xBEEFBEEF); + writer->Write((uint32_t)(hash >> 32)); + writer->Write((uint32_t)(hash & 0xFFFFFFFF)); + + auto dlStart = std::chrono::steady_clock::now(); + uint8_t lastOpCode; + + //for (auto data : dList->instructions) + for (size_t dataIdx = 0; dataIdx < dList->instructions.size(); dataIdx++) + { + auto data = dList->instructions[dataIdx]; + uint32_t word0 = 0; + uint32_t word1 = 0; + uint8_t opcode = (uint8_t)(data >> 56); + F3DZEXOpcode opF3D = (F3DZEXOpcode)opcode; + + if ((int)opF3D == G_DL)// || (int)opF3D == G_BRANCH_Z) + opcode = (uint8_t)G_DL_OTR_HASH; + + if ((int)opF3D == G_MTX) + opcode = (uint8_t)G_MTX_OTR; + + if ((int)opF3D == G_BRANCH_Z) + opcode = (uint8_t)G_BRANCH_Z_OTR; + + if ((int)opF3D == G_VTX) + opcode = (uint8_t)G_VTX_OTR_HASH; + + if ((int)opF3D == G_SETTIMG) + opcode = (uint8_t)G_SETTIMG_OTR_HASH; + + word0 += (opcode << 24); + + switch ((int)opF3D) + { + case G_NOOP: + { + Gfx value = {gsDPNoOp()}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_ENDDL: + { + Gfx value = {gsSPEndDisplayList()}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_MODIFYVTX: + { + int32_t ww = (data & 0x00FF000000000000ULL) >> 48; + int32_t nnnn = (data & 0x0000FFFF00000000ULL) >> 32; + int32_t vvvvvvvv = (data & 0x00000000FFFFFFFFULL); + + Gfx value = {gsSPModifyVertex(nnnn / 2, ww, vvvvvvvv)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + default: + { + printf("Undefined opcode: %02X\n", opcode); + //word0 = _byteswap_ulong((uint32_t)(data >> 32)); + //word1 = _byteswap_ulong((uint32_t)(data & 0xFFFFFFFF)); + } + break; + case G_GEOMETRYMODE: + { + int32_t cccccc = (data & 0x00FFFFFF00000000) >> 32; + int32_t ssssssss = (data & 0xFFFFFFFF); + + Gfx value = {gsSPGeometryMode(~cccccc, ssssssss)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_RDPPIPESYNC: + { + Gfx value = {gsDPPipeSync()}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_RDPLOADSYNC: + { + Gfx value = {gsDPLoadSync()}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_RDPTILESYNC: + { + Gfx value = {gsDPTileSync()}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_RDPFULLSYNC: + { + Gfx value = {gsDPFullSync()}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_RDPSETOTHERMODE: + { + int32_t hhhhhh = (data & 0x00FFFFFF00000000) >> 32; + int32_t llllllll = (data & 0x00000000FFFFFFFF); + + Gfx value = {gsDPSetOtherMode(hhhhhh, llllllll)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_POPMTX: + { + Gfx value = {gsSPPopMatrix(data)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_SETENVCOLOR: + { + uint8_t r = (uint8_t)((data & 0xFF000000) >> 24); + uint8_t g = (uint8_t)((data & 0x00FF0000) >> 16); + uint8_t b = (uint8_t)((data & 0xFF00FF00) >> 8); + uint8_t a = (uint8_t)((data & 0x000000FF) >> 0); + + Gfx value = {gsDPSetEnvColor(r, g, b, a)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_MTX: + { + if ((!Globals::Instance->HasSegment(GETSEGNUM(data), res->parent->workerID)) || ((data & 0xFFFFFFFF) == 0x07000000)) // En_Zf and En_Ny place a DL in segment 7 + { + uint32_t pp = (data & 0x000000FF00000000) >> 32; + uint32_t mm = (data & 0x00000000FFFFFFFF); + + pp ^= G_MTX_PUSH; + + mm = (mm & 0x0FFFFFFF) + 1; + + Gfx value = {gsSPMatrix(mm, pp)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + else + { + uint32_t pp = (data & 0x000000FF00000000) >> 32; + uint32_t mm = (data & 0x00000000FFFFFFFF); + pp ^= G_MTX_PUSH; + + Gfx value = {gsSPMatrix(mm, pp)}; + word0 = value.words.w0; + word1 = value.words.w1; + + word0 = (word0 & 0x00FFFFFF) + (G_MTX_OTR << 24); + + Declaration* mtxDecl = dList->parent->GetDeclaration(GETSEGOFFSET(mm)); + + int bp = 0; + + writer->Write(word0); + writer->Write(word1); + + if (mtxDecl != nullptr) + { + std::string vName = StringHelper::Sprintf("%s/%s", (GetParentFolderName(res).c_str()), mtxDecl->declName.c_str()); + + uint64_t hash = CRC64(vName.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + + // Write the Matrix + for (ZMtx mtx : dList->mtxList) + { + if (mtx.GetRawDataIndex() == mtxDecl->address) + { + MemoryStream* mtxStream = new MemoryStream(); + BinaryWriter mtxWriter = BinaryWriter(mtxStream); + + OTRExporter_MtxExporter mtxExporter; + + //printf("Adding MTX %s\n", vName.c_str()); + + mtxExporter.Save(&mtx, "", &mtxWriter); + AddFile(vName, mtxStream->ToVector()); + break; + } + } + } + else + { + uint32_t seg = data & 0xFFFFFFFF; + std::string resourceName = ""; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(seg, dList->parent, "", resourceName, res->parent->workerID); + if (foundDecl) { + // Remove any leading '&' on the resource name when building our OTR path string + if (resourceName[0] == '&') { + resourceName = resourceName.substr(1, resourceName.length() - 1); + } + + ZFile *assocFile = Globals::Instance->GetSegment(GETSEGNUM(seg), res->parent->workerID); + std::string assocFileName = assocFile->GetName(); + std::string fName = GetPathToRes(assocFile->resources[0], resourceName.c_str()); + + uint64_t hash = CRC64(fName.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + } else { + word0 = 0; + word1 = 0; + spdlog::error(StringHelper::Sprintf("dListDecl == nullptr! Addr = {:08X}", GETSEGOFFSET(data))); + } + } + } + } + break; + case G_LOADBLOCK: + { + int32_t sss = (data & 0x00FFF00000000000) >> 48; + int32_t ttt = (data & 0x00000FFF00000000) >> 36; + int32_t i = (data & 0x000000000F000000) >> 24; + int32_t xxx = (data & 0x0000000000FFF000) >> 12; + int32_t ddd = (data & 0x0000000000000FFF); + + // gSunDL Textures are rendered as i8 instead of i4 + if (res->GetName() == "gSunDL" && ttt != G_TX_LOADTILE) + { + xxx = (xxx+1)/2-1; + } + + Gfx value = {gsDPLoadBlock(i, sss, ttt, xxx, ddd)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_CULLDL: + { + int32_t vvvv = (data & 0xFFFF00000000) >> 32; + int32_t wwww = (data & 0x0000FFFF); + + Gfx value = {gsSPCullDisplayList(vvvv / 2, wwww / 2)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_RDPHALF_1: + { + auto data2 = dList->instructions[dataIdx + 1]; + + if ((data2 >> 56) != G_BRANCH_Z) + { + uint32_t a = (data & 0x00FFF00000000000) >> 44; + uint32_t b = (data & 0x00000FFF00000000) >> 32; + uint32_t z = (data & 0x00000000FFFFFFFF) >> 0; + uint32_t h = (data & 0xFFFFFFFF); + + Gfx value = {gsSPBranchLessZraw3(h & 0x00FFFFFF)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + else + { + word0 = (G_NOOP << 24); + word1 = 0; + } + } + break; + case G_RDPHALF_2: + { + Gfx value = {gsDPWordLo(data & 0xFFFFFFFF)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_TEXRECT: + { + int32_t xxx = (data & 0x00FFF00000000000) >> 44; + int32_t yyy = (data & 0x00000FFF00000000) >> 32; + int32_t i = (data & 0x000000000F000000) >> 24; + int32_t XXX = (data & 0x0000000000FFF000) >> 12; + int32_t YYY = (data & 0x0000000000000FFF); + + Gfx value = {gsSPTextureRectangle2(XXX, YYY, xxx, yyy, i)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_BRANCH_Z: + { + uint32_t a = (data & 0x00FFF00000000000) >> 44; + uint32_t b = (data & 0x00000FFF00000000) >> 32; + uint32_t z = (data & 0x00000000FFFFFFFF) >> 0; + uint32_t h = (data & 0xFFFFFFFF); + + auto data2 = dList->instructions[dataIdx - 1]; + uint32_t dListPtr = GETSEGOFFSET(data2); + + Declaration* dListDecl = dList->parent->GetDeclaration(dListPtr); + + int bp = 0; + + Gfx value = {gsSPBranchLessZraw2(0xDEADABCD, (a / 5) | (b / 2), z)}; + word0 = (value.words.w0 & 0x00FFFFFF) + (G_BRANCH_Z_OTR << 24); + word1 = value.words.w1; + + writer->Write(word0); + writer->Write(word1); + + if (dListDecl != nullptr) + { + std::string vName = StringHelper::Sprintf("%s/%s", (GetParentFolderName(res).c_str()), dListDecl->declName.c_str()); + + uint64_t hash = CRC64(vName.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + } + else + { + // If we can't find the display list in this file, try looking in other files based on the segment number + uint32_t seg = data2 & 0xFFFFFFFF; + std::string resourceName = ""; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(seg, dList->parent, "", resourceName, res->parent->workerID); + if (foundDecl) { + ZFile* assocFile = Globals::Instance->GetSegment(GETSEGNUM(seg), res->parent->workerID); + std::string assocFileName = assocFile->GetName(); + std::string fName = GetPathToRes(assocFile->resources[0], resourceName.c_str()); + + uint64_t hash = CRC64(fName.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + } else { + word0 = 0; + word1 = 0; + spdlog::error(StringHelper::Sprintf("dListDecl == nullptr! Addr = {:08X}", GETSEGOFFSET(data2))); + } + } + + for (size_t i = 0; i < dList->otherDLists.size(); i++) + { + Declaration* dListDecl2 = dList->parent->GetDeclaration(GETSEGOFFSET(dList->otherDLists[i]->GetRawDataIndex())); + + if (dListDecl2 != nullptr) + { + //std::string fName = StringHelper::Sprintf("%s\\%s", GetParentFolderName(res).c_str(), dListDecl2->varName.c_str()); + std::string fName = OTRExporter_DisplayList::GetPathToRes(res, dListDecl2->declName.c_str()); + + if (files.find(fName) == files.end() && !DiskFile::Exists("Extract/" + fName)) + { + MemoryStream* dlStream = new MemoryStream(); + BinaryWriter dlWriter = BinaryWriter(dlStream); + + Save(dList->otherDLists[i], outPath, &dlWriter); + AddFile(fName, dlStream->ToVector()); + } + } + else + { + spdlog::error(StringHelper::Sprintf("dListDecl2 == nullptr! Addr = {:08X}", GETSEGOFFSET(data))); + } + } + + //Gfx value = gsSPBranchLessZraw2(h & 0x00FFFFFF, (a / 5) | (b / 2), z); + //word0 = value.words.w0; + //word1 = value.words.w1; + } + break; + //case G_BRANCH_Z: + case G_DL: + { + if ((!Globals::Instance->HasSegment(GETSEGNUM(data), res->parent->workerID) && (int)opF3D != G_BRANCH_Z) + || ((data & 0xFFFFFFFF) == 0x07000000)) // En_Zf and En_Ny place a DL in segment 7 + { + int32_t pp = (data & 0x00FF000000000000) >> 56; + + Gfx value; + + u32 segNum = GETSEGNUM(data); + u32 segOffset = GETSEGOFFSET(data); + + // Use regular DL opcode for 0 offsets + if (segOffset == 0) { + u32 dListVal = (data & 0x0FFFFFFF) + 1; + + if (pp != 0) + value = {gsSPBranchList(dListVal)}; + else + value = {gsSPDisplayList(dListVal)}; + } else { + // Convert the offset value to an index value by diving by the original size for Gfx on hardware + // Adding 1 for seg addr flow will be handled on the renderer side + u32 dListVal = (segNum << 24) | (segOffset / (sizeof(u32) * 2)); + + if (pp != 0) + value = {gsSPBranchListIndex(dListVal)}; + else + value = {gsSPDisplayListIndex(dListVal)}; + } + + + word0 = value.words.w0; + word1 = value.words.w1; + } + else + { + uint32_t dListPtr = GETSEGOFFSET(data); + + if ((int)opF3D == G_BRANCH_Z) + { + auto data2 = dList->instructions[dataIdx - 1]; + dListPtr = GETSEGOFFSET(data2); + } + else + { + int bp = 0; + } + + Declaration* dListDecl = dList->parent->GetDeclaration(dListPtr); + + int bp = 0; + + writer->Write(word0); + writer->Write(word1); + + if (dListDecl != nullptr) + { + std::string vName = StringHelper::Sprintf("%s/%s", (GetParentFolderName(res).c_str()), dListDecl->declName.c_str()); + + uint64_t hash = CRC64(vName.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + } + else + { + // If we can't find the display list in this file, try looking in other files based on the segment number + uint32_t seg = data & 0xFFFFFFFF; + std::string resourceName = ""; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(seg, dList->parent, "", resourceName, res->parent->workerID); + if (foundDecl) { + ZFile* assocFile = Globals::Instance->GetSegment(GETSEGNUM(seg), res->parent->workerID); + std::string assocFileName = assocFile->GetName(); + std::string fName = GetPathToRes(assocFile->resources[0], resourceName.c_str()); + + uint64_t hash = CRC64(fName.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + } else { + word0 = 0; + word1 = 0; + spdlog::error(StringHelper::Sprintf("dListDecl == nullptr! Addr = {:08X}", GETSEGOFFSET(data))); + } + } + + for (size_t i = 0; i < dList->otherDLists.size(); i++) + { + Declaration* dListDecl2 = dList->parent->GetDeclaration(GETSEGOFFSET(dList->otherDLists[i]->GetRawDataIndex())); + + if (dListDecl2 != nullptr) + { + //std::string fName = StringHelper::Sprintf("%s\\%s", GetParentFolderName(res).c_str(), dListDecl2->varName.c_str()); + std::string fName = OTRExporter_DisplayList::GetPathToRes(res, dListDecl2->declName.c_str()); + + if (files.find(fName) == files.end() && !DiskFile::Exists("Extract/" + fName)) + { + MemoryStream* dlStream = new MemoryStream(); + BinaryWriter dlWriter = BinaryWriter(dlStream); + + Save(dList->otherDLists[i], outPath, &dlWriter); + + AddFile(fName, dlStream->ToVector()); + } + } + else + { + spdlog::error(StringHelper::Sprintf("dListDecl2 == nullptr! Addr = {:08X}", GETSEGOFFSET(data))); + } + } + } + } + break; + case G_TEXTURE: + { + int32_t ____ = (data & 0x0000FFFF00000000) >> 32; + int32_t ssss = (data & 0x00000000FFFF0000) >> 16; + int32_t tttt = (data & 0x000000000000FFFF); + int32_t lll = (____ & 0x3800) >> 11; + int32_t ddd = (____ & 0x700) >> 8; + int32_t nnnnnnn = (____ & 0xFE) >> 1; + + Gfx value = {gsSPTexture(ssss, tttt, lll, ddd, nnnnnnn)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_TRI1: + { + int32_t aa = ((data & 0x00FF000000000000ULL) >> 48) / 2; + int32_t bb = ((data & 0x0000FF0000000000ULL) >> 40) / 2; + int32_t cc = ((data & 0x000000FF00000000ULL) >> 32) / 2; + + Gfx test = {gsSP1Triangle(aa, bb, cc, 0)}; + word0 = test.words.w0; + word1 = test.words.w1; + } + break; + case G_TRI2: + { + int32_t aa = ((data & 0x00FF000000000000ULL) >> 48) / 2; + int32_t bb = ((data & 0x0000FF0000000000ULL) >> 40) / 2; + int32_t cc = ((data & 0x000000FF00000000ULL) >> 32) / 2; + int32_t dd = ((data & 0x00000000FF0000ULL) >> 16) / 2; + int32_t ee = ((data & 0x0000000000FF00ULL) >> 8) / 2; + int32_t ff = ((data & 0x000000000000FFULL) >> 0) / 2; + + Gfx test = {gsSP2Triangles(aa, bb, cc, 0, dd, ee, ff, 0)}; + word0 = test.words.w0; + word1 = test.words.w1; + } + break; + case G_QUAD: + { + int32_t aa = ((data & 0x00FF000000000000ULL) >> 48) / 2; + int32_t bb = ((data & 0x0000FF0000000000ULL) >> 40) / 2; + int32_t cc = ((data & 0x000000FF00000000ULL) >> 32) / 2; + int32_t dd = ((data & 0x000000000000FFULL)) / 2; + + Gfx test = {gsSP1Quadrangle(aa, bb, cc, dd, 0)}; + word0 = test.words.w0; + word1 = test.words.w1; + } + break; + case G_SETPRIMCOLOR: + { + int32_t mm = (data & 0x0000FF0000000000) >> 40; + int32_t ff = (data & 0x000000FF00000000) >> 32; + int32_t rr = (data & 0x00000000FF000000) >> 24; + int32_t gg = (data & 0x0000000000FF0000) >> 16; + int32_t bb = (data & 0x000000000000FF00) >> 8; + int32_t aa = (data & 0x00000000000000FF) >> 0; + + Gfx value = {gsDPSetPrimColor(mm, ff, rr, gg, bb, aa)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_SETOTHERMODE_L: + { + int32_t ss = (data & 0x0000FF0000000000) >> 40; + int32_t len = ((data & 0x000000FF00000000) >> 32) + 1; + int32_t sft = 32 - (len)-ss; + int32_t dd = (data & 0xFFFFFFFF); + + // TODO: Output the correct render modes in data + + Gfx value = {gsSPSetOtherMode(0xE2, sft, len, dd)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_SETOTHERMODE_H: + { + int32_t ss = (data & 0x0000FF0000000000) >> 40; + int32_t nn = (data & 0x000000FF00000000) >> 32; + int32_t dd = (data & 0xFFFFFFFF); + + int32_t sft = 32 - (nn + 1) - ss; + + Gfx value; + + if (sft == 14) // G_MDSFT_TEXTLUT + { + const char* types[] = { "G_TT_NONE", "G_TT_NONE", "G_TT_RGBA16", "G_TT_IA16" }; + value = {gsDPSetTextureLUT(dd >> 14)}; + } + else + { + value = {gsSPSetOtherMode(0xE3, sft, nn + 1, dd)}; + } + + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_SETTILE: + { + int32_t fff = (data & 0b0000000011100000000000000000000000000000000000000000000000000000) >> 53; + int32_t ii = (data & 0b0000000000011000000000000000000000000000000000000000000000000000) >> 51; + int32_t nnnnnnnnn = + (data & 0b0000000000000011111111100000000000000000000000000000000000000000) >> 41; + int32_t mmmmmmmmm = + (data & 0b0000000000000000000000011111111100000000000000000000000000000000) >> 32; + int32_t ttt = (data & 0b0000000000000000000000000000000000000111000000000000000000000000) >> 24; + int32_t pppp = + (data & 0b0000000000000000000000000000000000000000111100000000000000000000) >> 20; + int32_t cc = (data & 0b0000000000000000000000000000000000000000000011000000000000000000) >> 18; + int32_t aaaa = + (data & 0b0000000000000000000000000000000000000000000000111100000000000000) >> 14; + int32_t ssss = + (data & 0b0000000000000000000000000000000000000000000000000011110000000000) >> 10; + int32_t dd = (data & 0b0000000000000000000000000000000000000000000000000000001100000000) >> 8; + int32_t bbbb = (data & 0b0000000000000000000000000000000000000000000000000000000011110000) >> 4; + int32_t uuuu = (data & 0b0000000000000000000000000000000000000000000000000000000000001111); + + // gSunDL Textures are rendered as i8 instead of i4 + if (res->GetName() == "gSunDL" && ttt != G_TX_LOADTILE) + { + ii = G_IM_SIZ_4b; + } + + Gfx value = {gsDPSetTile(fff, ii, nnnnnnnnn, mmmmmmmmm, ttt, pppp, cc, aaaa, ssss, dd, bbbb, uuuu)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_SETCOMBINE: + { + int32_t a0 = (data & 0b000000011110000000000000000000000000000000000000000000000000000) >> 52; + int32_t c0 = (data & 0b000000000001111100000000000000000000000000000000000000000000000) >> 47; + int32_t aa0 = (data & 0b00000000000000011100000000000000000000000000000000000000000000) >> 44; + int32_t ac0 = (data & 0b00000000000000000011100000000000000000000000000000000000000000) >> 41; + int32_t a1 = (data & 0b000000000000000000000011110000000000000000000000000000000000000) >> 37; + int32_t c1 = (data & 0b000000000000000000000000001111100000000000000000000000000000000) >> 32; + int32_t b0 = (data & 0b000000000000000000000000000000011110000000000000000000000000000) >> 28; + int32_t b1 = (data & 0b000000000000000000000000000000000001111000000000000000000000000) >> 24; + int32_t aa1 = (data & 0b00000000000000000000000000000000000000111000000000000000000000) >> 21; + int32_t ac1 = (data & 0b00000000000000000000000000000000000000000111000000000000000000) >> 18; + int32_t d0 = (data & 0b000000000000000000000000000000000000000000000111000000000000000) >> 15; + int32_t ab0 = (data & 0b00000000000000000000000000000000000000000000000111000000000000) >> 12; + int32_t ad0 = (data & 0b00000000000000000000000000000000000000000000000000111000000000) >> 9; + int32_t d1 = (data & 0b000000000000000000000000000000000000000000000000000000111000000) >> 6; + int32_t ab1 = (data & 0b00000000000000000000000000000000000000000000000000000000111000) >> 3; + int32_t ad1 = (data & 0b00000000000000000000000000000000000000000000000000000000000111) >> 0; + + Gfx value = { gsDPSetCombineLERP_NoMacros(a0, b0, c0, d0, aa0, ab0, ac0, ad0, a1, b1, c1, d1, aa1, ab1, ac1, ad1)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_SETTILESIZE: + { + int32_t sss = (data & 0x00FFF00000000000) >> 44; + int32_t ttt = (data & 0x00000FFF00000000) >> 32; + int32_t uuu = (data & 0x0000000000FFF000) >> 12; + int32_t vvv = (data & 0x0000000000000FFF); + int32_t i = (data & 0x000000000F000000) >> 24; + + Gfx value = {gsDPSetTileSize(i, sss, ttt, uuu, vvv)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_LOADTLUT: + { + int32_t t = (data & 0x0000000007000000) >> 24; + int32_t ccc = (data & 0x00000000003FF000) >> 14; + + Gfx value = {gsDPLoadTLUTCmd(t, ccc)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_LOADTILE: + { + int sss = (data & 0x00FFF00000000000) >> 44; + int ttt = (data & 0x00000FFF00000000) >> 32; + int i = (data & 0x000000000F000000) >> 24; + int uuu = (data & 0x0000000000FFF000) >> 12; + int vvv= (data & 0x0000000000000FFF); + + Gfx value = {gsDPLoadTile(i, sss, ttt, uuu, vvv)}; + word0 = value.words.w0; + word1 = value.words.w1; + } + break; + case G_SETTIMG: + { + if (res->GetName() == "gGiSeedDL") { + int bp = 5; + } + uint32_t seg = data & 0xFFFFFFFF; + int32_t texAddress = Seg2Filespace(data, dList->parent->baseAddress); + + if (!Globals::Instance->HasSegment(GETSEGNUM(seg), res->parent->workerID) || (res->GetName() == "sShadowMaterialDL")) + { + if (res->GetName() == "sShadowMaterialDL") { + // sShadowMaterialDL (In ovl_En_Jsjutan) has a texture in bss. This is a hack to override the reference to one + // to segment C. The actor has been modified to load the texture into segment C. + seg = 0x0C000000; + } + int32_t __ = (data & 0x00FF000000000000) >> 48; + int32_t www = (data & 0x00000FFF00000000) >> 32; + + uint32_t fmt = (__ & 0xE0) >> 5; + uint32_t siz = (__ & 0x18) >> 3; + + Gfx value = {gsDPSetTextureImage(fmt, siz, www + 1, (seg & 0x0FFFFFFF) + 1)}; + word0 = value.words.w0; + word1 = value.words.w1; + + writer->Write(word0); + writer->Write(word1); + } + else + { + int32_t __ = (data & 0x00FF000000000000) >> 48; + int32_t www = (data & 0x00000FFF00000000) >> 32; + + uint32_t fmt = (__ & 0xE0) >> 5; + uint32_t siz = (__ & 0x18) >> 3; + + Gfx value = {gsDPSetTextureImage(fmt, siz, www + 1, __)}; + word0 = value.words.w0 & 0x00FFFFFF; + word0 += (G_SETTIMG_OTR_HASH << 24); + //word1 = value.words.w1; + word1 = 0; + + writer->Write(word0); + writer->Write(word1); + + bool foundDecl = false; + std::string resourcePath = ""; + + // First check current file + if (res->parent->segment == GETSEGNUM(seg)) { + uint32_t segmentOffset = GETSEGOFFSET(seg); + Declaration* resourceDecl = dList->parent->GetDeclaration(segmentOffset); + + if (resourceDecl != nullptr) + { + foundDecl = true; + resourcePath = OTRExporter_DisplayList::GetPathToRes(res, resourceDecl->declName); + } + } + + // Then check in global resources + if (!foundDecl) { + foundDecl = Globals::Instance->GetSegmentedPtrName(seg, dList->parent, "", resourcePath, res->parent->workerID); + + if (foundDecl) { + ZFile* assocFile = Globals::Instance->GetSegment(GETSEGNUM(seg), res->parent->workerID); + std::string assocFileName = assocFile->GetName(); + resourcePath = GetPathToRes(assocFile->resources[0], resourcePath); + } + } + + if (foundDecl) + { + uint64_t hash = CRC64(resourcePath.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + } + else + { + word0 = 0; + word1 = 0; + spdlog::error("texDecl == nullptr! PTR = 0x{:08X}", texAddress); + } + } + } + break; + case G_VTX: + { + if (!Globals::Instance->HasSegment(GETSEGNUM(data), res->parent->workerID)) + { + int32_t aa = (data & 0x000000FF00000000ULL) >> 32; + int32_t nn = (data & 0x000FF00000000000ULL) >> 44; + + Gfx value = {gsSPVertex((data & 0xFFFFFFFF) + 1, nn, ((aa >> 1) - nn))}; + + word0 = value.words.w0; + word1 = value.words.w1; + } + else + { + // Write CRC64 of vtx file name + uint32_t addr = data & 0xFFFFFFFF; + + if (GETSEGNUM(data) == 0x80) + addr -= dList->parent->baseAddress; + + auto segOffset = GETSEGOFFSET(addr); + Declaration* vtxDecl = dList->parent->GetDeclarationRanged(segOffset); + + int32_t aa = (data & 0x000000FF00000000ULL) >> 32; + int32_t nn = (data & 0x000FF00000000000ULL) >> 44; + bool isSegmentedPtr = false; + std::string fName = ""; + + // If we can't find the display list in this file, try looking in other files based on the segment number + if (vtxDecl == nullptr) { + uint32_t seg = data & 0xFFFFFFFF; + std::string resourceName = ""; + isSegmentedPtr = Globals::Instance->GetSegmentedPtrName(seg, dList->parent, "", resourceName, res->parent->workerID); + + if (isSegmentedPtr) { + ZFile* assocFile = Globals::Instance->GetSegment(GETSEGNUM(seg), res->parent->workerID); + std::string assocFileName = assocFile->GetName(); + fName = GetPathToRes(assocFile->resources[0], resourceName.c_str()); + vtxDecl = assocFile->GetDeclarationRanged(segOffset); + } + } + + if (vtxDecl != nullptr && vtxDecl->declName != "Gfx") + { + uint32_t diff = segOffset - vtxDecl->address; + + Gfx value = {gsSPVertex(diff, nn, ((aa >> 1) - nn))}; + + word0 = value.words.w0; + word0 &= 0x00FFFFFF; + word0 += (G_VTX_OTR_HASH << 24); + word1 = value.words.w1; + + writer->Write(word0); + writer->Write(word1); + + if (!isSegmentedPtr) { + fName = OTRExporter_DisplayList::GetPathToRes(res, vtxDecl->declName); + } + + uint64_t hash = CRC64(fName.c_str()); + + word0 = hash >> 32; + word1 = hash & 0xFFFFFFFF; + + if (files.find(fName) == files.end() && !DiskFile::Exists("Extract/" + fName)) + { + // Write vertices to file + MemoryStream* vtxStream = new MemoryStream(); + BinaryWriter vtxWriter = BinaryWriter(vtxStream); + + int arrCnt = 0; + + auto split = StringHelper::Split(vtxDecl->declBody, "\n"); + + for (size_t i = 0; i < split.size(); i++) + { + std::string line = split[i]; + + if (StringHelper::Contains(line, "VTX(")) + arrCnt++; + } + + // OTRTODO: Once we aren't relying on text representations, we should call ArrayExporter... + OTRExporter::WriteHeader(nullptr, "", &vtxWriter, static_cast(SOH::ResourceType::SOH_Array)); + + vtxWriter.Write((uint32_t)ZResourceType::Vertex); + vtxWriter.Write((uint32_t)arrCnt); + + auto start = std::chrono::steady_clock::now(); + + // God dammit this is so dumb + for (size_t i = 0; i < split.size(); i++) + { + std::string line = split[i]; + + if (StringHelper::Contains(line, "VTX(")) + { + auto split2 = StringHelper::Split(StringHelper::Split(StringHelper::Split(line, "VTX(")[1], ")")[0], ","); + + vtxWriter.Write((int16_t)std::stoi(split2[0], nullptr, 10)); // v.x + vtxWriter.Write((int16_t)std::stoi(split2[1], nullptr, 10)); // v.y + vtxWriter.Write((int16_t)std::stoi(split2[2], nullptr, 10)); // v.z + + vtxWriter.Write((int16_t)0); // v.flag + + vtxWriter.Write((int16_t)std::stoi(split2[3], nullptr, 10)); // v.s + vtxWriter.Write((int16_t)std::stoi(split2[4], nullptr, 10)); // v.t + + vtxWriter.Write((uint8_t)std::stoi(split2[5], nullptr, 10)); // v.r + vtxWriter.Write((uint8_t)std::stoi(split2[6], nullptr, 10)); // v.g + vtxWriter.Write((uint8_t)std::stoi(split2[7], nullptr, 10)); // v.b + vtxWriter.Write((uint8_t)std::stoi(split2[8], nullptr, 10)); // v.a + } + } + + AddFile(fName, vtxStream->ToVector()); + + auto end = std::chrono::steady_clock::now(); + size_t diff = std::chrono::duration_cast(end - start).count(); + } + } + else + { + spdlog::error("vtxDecl == nullptr!"); + } + } + } + break; + } + + writer->Write(word0); + writer->Write(word1); + lastOpCode = opcode; + } + + if (lastOpCode != G_ENDDL) { + Gfx value = { gsSPEndDisplayList() }; + writer->Write((uint32_t)value.words.w0); + writer->Write((uint32_t)value.words.w1); + } + + auto dlEnd = std::chrono::steady_clock::now(); + size_t dlDiff = std::chrono::duration_cast(dlEnd - dlStart).count(); + + //printf("Display List Gen in %zums\n", dlDiff); +} + +std::string OTRExporter_DisplayList::GetPathToRes(ZResource* res, std::string varName) +{ + std::string prefix = GetPrefix(res); + std::string fName = StringHelper::Sprintf("%s/%s", GetParentFolderName(res).c_str(), varName.c_str()); + + return fName; +} + +std::string OTRExporter_DisplayList::GetParentFolderName(ZResource* res) +{ + std::string prefix = GetPrefix(res); + std::string oName = res->parent->GetOutName(); + + if (StringHelper::Contains(oName, "_scene")) + { + auto split = StringHelper::Split(oName, "_"); + oName = ""; + for (size_t i = 0; i < split.size() - 1; i++) + oName += split[i] + "_"; + + oName += "scene"; + } + else if (StringHelper::Contains(oName, "_room")) + { + if (Globals::Instance->game != ZGame::MM_RETAIL) + oName = StringHelper::Split(oName, "_room")[0] + "_scene"; + else + oName = StringHelper::Split(oName, "_room")[0]; + } + + if (prefix != "") + oName = prefix + "/" + oName; + + return oName; +} +#ifdef GAME_MM +std::string OTRExporter_DisplayList::GetPrefix(ZResource* res) +{ + std::string oName = res->parent->GetOutName(); + std::string prefix = ""; + std::string xmlPath = StringHelper::Replace(res->parent->GetXmlFilePath().string(), "\\", "/"); + // BENTODO bring in the existing OTRExporter changes from SoHs + if (StringHelper::Contains(oName, "_scene") || StringHelper::Contains(oName, "_room") || (StringHelper::Contains(res->parent->GetXmlFilePath().string(), "/scenes/") || StringHelper::Contains(res->parent->GetXmlFilePath().string(), "\\scenes\\"))) { + prefix = "scenes"; + if (Globals::Instance->rom->IsMQ()) { + prefix += "/mq"; + } else { + prefix += "/nonmq"; + } + } + else if (StringHelper::Contains(xmlPath, "objects/")) + prefix = "objects"; + else if (StringHelper::Contains(xmlPath, "textures/")) + prefix = "textures"; + else if (StringHelper::Contains(xmlPath, "overlays/")) + prefix = "overlays"; + else if (StringHelper::Contains(xmlPath, "misc/")) + prefix = "misc"; + else if (StringHelper::Contains(xmlPath, "text/")) + prefix = "text"; + else if (StringHelper::Contains(xmlPath, "code/")) + prefix = "code"; + + return prefix; +} +#elif GAME_OOT +std::string OTRExporter_DisplayList::GetPrefix(ZResource* res) +{ + std::string oName = res->parent->GetOutName(); + std::string prefix = ""; + std::string xmlPath = StringHelper::Replace(res->parent->GetXmlFilePath().string(), "\\", "/"); + + if (StringHelper::Contains(oName, "_scene") || StringHelper::Contains(oName, "_room")) { + prefix = "scenes/shared"; + + // Regex for xml paths that are dungeons with unique MQ variants (only the main dungeon, not boss rooms) + std::regex dungeonsWithMQ(R"(((ydan)|(ddan)|(bdan)|(Bmori1)|(HIDAN)|(MIZUsin)|(jyasinzou)|(HAKAdan)|(HAKAdanCH)|(ice_doukutu)|(men)|(ganontika))\.xml)"); + + if (StringHelper::Contains(xmlPath, "dungeons/") && std::regex_search(xmlPath, dungeonsWithMQ)) { + if (Globals::Instance->rom->IsMQ()) { + prefix = "scenes/mq"; + } else { + prefix = "scenes/nonmq"; + } + } + } + else if (StringHelper::Contains(xmlPath, "objects/")) + prefix = "objects"; + else if (StringHelper::Contains(xmlPath, "textures/")) + prefix = "textures"; + else if (StringHelper::Contains(xmlPath, "overlays/")) + prefix = "overlays"; + else if (StringHelper::Contains(xmlPath, "misc/")) + prefix = "misc"; + else if (StringHelper::Contains(xmlPath, "text/")) + prefix = "text"; + else if (StringHelper::Contains(xmlPath, "code/")) + prefix = "code"; + + return prefix; +} +#endif diff --git a/OTRExporter/OTRExporter/DisplayListExporter.h b/OTRExporter/OTRExporter/DisplayListExporter.h new file mode 100644 index 000000000..d8d96971c --- /dev/null +++ b/OTRExporter/OTRExporter/DisplayListExporter.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ZResource.h" +#include "ZTexture.h" +#include "ZDisplayList.h" +#include "Exporter.h" +#include + +class OTRExporter_DisplayList : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; + static std::string GetParentFolderName(ZResource* res); + static std::string GetPathToRes(ZResource* res, std::string varName); + static std::string GetPrefix(ZResource* res); + +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/Exporter.cpp b/OTRExporter/OTRExporter/Exporter.cpp new file mode 100644 index 000000000..c2a582a62 --- /dev/null +++ b/OTRExporter/OTRExporter/Exporter.cpp @@ -0,0 +1,21 @@ +#include "Exporter.h" +#include "VersionInfo.h" + +void OTRExporter::WriteHeader(ZResource* res, const fs::path& outPath, BinaryWriter* writer, uint32_t resType, int32_t resVersion) +{ + writer->Write((uint8_t)Endianness::Little); // 0x00 + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + + writer->Write((uint32_t)resType); // 0x04 + //writer->Write((uint32_t)MAJOR_VERSION); // 0x08 + writer->Write((uint32_t)resVersion); // 0x08 + writer->Write((uint64_t)0xDEADBEEFDEADBEEF); // id, 0x0C + writer->Write((uint32_t)resourceVersions[resType]); // 0x10 + writer->Write((uint64_t)0); // ROM CRC, 0x14 + writer->Write((uint32_t)0); // ROM Enum, 0x1C + + while (writer->GetBaseAddress() < 0x40) + writer->Write((uint32_t)0); // To be used at a later date! +} diff --git a/OTRExporter/OTRExporter/Exporter.h b/OTRExporter/OTRExporter/Exporter.h new file mode 100644 index 000000000..a6a4f088e --- /dev/null +++ b/OTRExporter/OTRExporter/Exporter.h @@ -0,0 +1,17 @@ +#pragma once +#include "ZResource.h" +#include "ZArray.h" +#include "stdint.h" +#include +#include +#include "VersionInfo.h" +#ifdef GAME_MM +#include "../../mm/2s2h/resource/type/2shResourceType.h" +#elif GAME_OOT +#include "../../soh/soh/resource/type/SohResourceType.h" +#endif +class OTRExporter : public ZResourceExporter +{ +protected: + static void WriteHeader(ZResource* res, const fs::path& outPath, BinaryWriter* writer, uint32_t resType, int32_t resVersion = 0); +}; diff --git a/OTRExporter/OTRExporter/ExporterArchive.cpp b/OTRExporter/OTRExporter/ExporterArchive.cpp new file mode 100644 index 000000000..50f399728 --- /dev/null +++ b/OTRExporter/OTRExporter/ExporterArchive.cpp @@ -0,0 +1,11 @@ +#include "ExporterArchive.h" +#include "Utils/StringHelper.h" +#include +#include + +ExporterArchive::ExporterArchive(const std::string& path, bool enableWriting) : mPath(path) { +} + +ExporterArchive::~ExporterArchive() { +} + diff --git a/OTRExporter/OTRExporter/ExporterArchive.h b/OTRExporter/OTRExporter/ExporterArchive.h new file mode 100644 index 000000000..3070718d7 --- /dev/null +++ b/OTRExporter/OTRExporter/ExporterArchive.h @@ -0,0 +1,26 @@ +#pragma once + +#undef _DLL + +#include +#include +#include +#include +#include +#include + +class ExporterArchive : public std::enable_shared_from_this { + public: + ExporterArchive() {} + ExporterArchive(const std::string& path, bool enableWriting); + ~ExporterArchive(); + + virtual int CreateArchive(size_t fileCapacity) = 0; + virtual bool AddFile(const std::string& filePath, void* fileData, size_t fileSize) = 0; + + std::string mPath; + std::mutex mMutex; + + virtual bool Load(bool enableWriting) = 0; + virtual bool Unload() = 0; +}; diff --git a/OTRExporter/OTRExporter/ExporterArchiveO2R.cpp b/OTRExporter/OTRExporter/ExporterArchiveO2R.cpp new file mode 100644 index 000000000..aca45e460 --- /dev/null +++ b/OTRExporter/OTRExporter/ExporterArchiveO2R.cpp @@ -0,0 +1,93 @@ +#include "ExporterArchiveO2R.h" +#include "Utils/StringHelper.h" +#include +#include +#include + +ExporterArchiveO2R::ExporterArchiveO2R(const std::string& path, bool enableWriting) { + mPath = path; + mZip = nullptr; +} + +ExporterArchiveO2R::~ExporterArchiveO2R() { + Unload(); +} + +bool ExporterArchiveO2R::Load(bool enableWriting) { + int openErr; + zip_t* archive = zip_open(mPath.c_str(), ZIP_CHECKCONS, &openErr); + + if (archive == nullptr) { + zip_error_t error; + zip_error_init_with_code(&error, openErr); + SPDLOG_ERROR("Failed to open ZIP (O2R) file. Error: {}", zip_error_strerror(&error)); + zip_error_fini(&error); + return false; + } + + SPDLOG_INFO("Loaded ZIP (O2R) archive: {}", mPath.c_str()); + mZip = archive; + + return true; +} + +bool ExporterArchiveO2R::Unload() { + printf("Unload\n"); + int err; + { + const std::lock_guard lock(mMutex); + err = zip_close(mZip); + } + if (err < 0) { + zip_error_t* zipError = zip_get_error(mZip); + SPDLOG_ERROR("Failed to close ZIP (O2R) file. Error: {}", zip_error_strerror(zipError)); + printf("fail\n"); + zip_error_fini(zipError); + return false; + } + + return true; +} + +int ExporterArchiveO2R::CreateArchive([[maybe_unused]] size_t fileCapacity) { + int openErr; + zip_t* zip; + + { + const std::lock_guard lock(mMutex); + zip = zip_open(mPath.c_str(), ZIP_CREATE, &openErr); + } + + if (zip == nullptr) { + zip_error_t error; + zip_error_init_with_code(&error, openErr); + SPDLOG_ERROR("Failed to create ZIP (O2R) file. Error: {}", zip_error_strerror(&error)); + zip_error_fini(&error); + return -1; + } + mZip = zip; + SPDLOG_INFO("Loaded ZIP (O2R) archive: {}", mPath.c_str()); + return 0; +} + +bool ExporterArchiveO2R::AddFile(const std::string& filePath, void* fileData, size_t fileSize) { + zip_source_t* source = zip_source_buffer(mZip, fileData, fileSize, 0); + + if (source == nullptr) { + zip_error_t* zipError = zip_get_error(mZip); + SPDLOG_ERROR("Failed to create ZIP source. Error: {}", zip_error_strerror(zipError)); + zip_source_free(source); + zip_error_fini(zipError); + return false; + } + + if (zip_file_add(mZip, filePath.c_str(), source, ZIP_FL_OVERWRITE | ZIP_FL_ENC_UTF_8) < 0) { + zip_error_t* zipError = zip_get_error(mZip); + SPDLOG_ERROR("Failed to add file to ZIP. Error: {}", zip_error_strerror(zipError)); + zip_source_free(source); + zip_error_fini(zipError); + return false; + } + + return true; +} diff --git a/OTRExporter/OTRExporter/ExporterArchiveO2R.h b/OTRExporter/OTRExporter/ExporterArchiveO2R.h new file mode 100644 index 000000000..2ef57cc1b --- /dev/null +++ b/OTRExporter/OTRExporter/ExporterArchiveO2R.h @@ -0,0 +1,24 @@ +#pragma once + +#undef _DLL + +#include +#include +#include +#include +#include +#include +#include "ExporterArchive.h" + +class ExporterArchiveO2R : public ExporterArchive { + public: + ExporterArchiveO2R(const std::string& path, bool enableWriting); + ~ExporterArchiveO2R(); + + int CreateArchive(size_t fileCapacity) override; + bool AddFile(const std::string& filePath, void* fileData, size_t fileSize) override; + + zip_t* mZip; + bool Load(bool enableWriting) override; + bool Unload() override; +}; diff --git a/OTRExporter/OTRExporter/ExporterArchiveOTR.cpp b/OTRExporter/OTRExporter/ExporterArchiveOTR.cpp new file mode 100644 index 000000000..1aff41ab1 --- /dev/null +++ b/OTRExporter/OTRExporter/ExporterArchiveOTR.cpp @@ -0,0 +1,147 @@ +#ifdef INCLUDE_MPQ_SUPPORT +#include "ExporterArchiveOTR.h" +#include "Utils/StringHelper.h" +#include +#include + +ExporterArchiveOtr::ExporterArchiveOtr(const std::string& path, bool enableWriting) { + mPath = path; + mMpq = nullptr; +} + +ExporterArchiveOtr::~ExporterArchiveOtr() { + Unload(); +} + +bool ExporterArchiveOtr::Load(bool enableWriting) { + HANDLE mpqHandle = NULL; + + bool baseLoaded = false; + std::string fullPath = std::filesystem::absolute(mPath).string(); + + bool openArchiveSuccess; + { + const std::lock_guard lock(mMutex); + openArchiveSuccess = SFileOpenArchive(fullPath.c_str(), 0, enableWriting ? 0 : MPQ_OPEN_READ_ONLY, &mpqHandle); + } + if (openArchiveSuccess) { + printf("Opened mpq file %s\n", fullPath.c_str()); + mMpq = mpqHandle; + mPath = fullPath; + baseLoaded = true; + } + + if (!baseLoaded) { + printf("No valid OTR file was provided."); + return false; + } + + return true; +} + +bool ExporterArchiveOtr::Unload() { + bool success = true; + + bool closeArchiveSuccess; + { + const std::lock_guard lock(mMutex); + closeArchiveSuccess = SFileCloseArchive(mMpq); + } + if (!closeArchiveSuccess) { + printf("Failed to close mpq\n"); + success = false; + } + + mMpq = nullptr; + + return success; +} + +int ExporterArchiveOtr::CreateArchive(size_t fileCapacity) { + bool success; + { + const std::lock_guard lock(mMutex); + success = SFileCreateArchive(mPath.c_str(), MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES | MPQ_CREATE_ARCHIVE_V2, + static_cast(fileCapacity), &mMpq); + } + int32_t error = GetLastError(); + + if (success) { + return 0; + } else { + printf("We tried to create an archive, but it has fallen and cannot get up.\n"); + return -1; + } +} + +bool ExporterArchiveOtr::AddFile(const std::string& filePath, void* fileData, size_t fileSize) { + HANDLE hFile; +#ifdef _WIN32 + SYSTEMTIME sysTime; + GetSystemTime(&sysTime); + FILETIME t; + SystemTimeToFileTime(&sysTime, &t); + ULONGLONG theTime = static_cast(t.dwHighDateTime) << (sizeof(t.dwHighDateTime) * 8) | t.dwLowDateTime; +#else + time_t theTime; + time(&theTime); +#endif + + std::string updatedPath = filePath; + + StringHelper::ReplaceOriginal(updatedPath, "\\", "/"); + + bool createFileSuccess; + { + const std::lock_guard lock(mMutex); + createFileSuccess = + SFileCreateFile(mMpq, updatedPath.c_str(), theTime, static_cast(fileSize), 0, MPQ_FILE_COMPRESS, &hFile); + } + if (!createFileSuccess) { + printf("Failed to create file.\n"); + return false; + } + + bool writeFileSuccess; + { + const std::lock_guard lock(mMutex); + writeFileSuccess = SFileWriteFile(hFile, fileData, static_cast(fileSize), MPQ_COMPRESSION_ZLIB); + } + if (!writeFileSuccess) { + printf("Failed to write.\n"); + bool closeFileSuccess; + { + const std::lock_guard lock(mMutex); + closeFileSuccess = SFileCloseFile(hFile); + } + if (!closeFileSuccess) { + printf("Failed to close.\n"); + } + return false; + } + + bool finishFileSuccess; + { + const std::lock_guard lock(mMutex); + finishFileSuccess = SFileFinishFile(hFile); + } + if (!finishFileSuccess) { + printf("Failed to finish file.\n"); + bool closeFileSuccess; + { + const std::lock_guard lock(mMutex); + closeFileSuccess = SFileCloseFile(hFile); + } + if (!closeFileSuccess) { + printf("Failed to close after finish failure.\n"); + } + return false; + } + // SFileFinishFile already frees the handle, so no need to close it again. + + mAddedFiles.push_back(updatedPath); + mHashes[CRC64(updatedPath.c_str())] = updatedPath; + + return true; +} +#endif \ No newline at end of file diff --git a/OTRExporter/OTRExporter/ExporterArchiveOTR.h b/OTRExporter/OTRExporter/ExporterArchiveOTR.h new file mode 100644 index 000000000..f6f225209 --- /dev/null +++ b/OTRExporter/OTRExporter/ExporterArchiveOTR.h @@ -0,0 +1,29 @@ +#pragma once +#ifdef INCLUDE_MPQ_SUPPORT +#undef _DLL + +#include +#include +#include +#include +#include +#include +#include "ExporterArchive.h" + +class ExporterArchiveOtr : public ExporterArchive { + public: + ExporterArchiveOtr(const std::string& path, bool enableWriting); + ~ExporterArchiveOtr(); + + int CreateArchive(size_t fileCapacity) override; + bool AddFile(const std::string& filePath, void* fileData, size_t fileSize) override; + + bool Load(bool enableWriting) override; + bool Unload() override; + + private: + std::unordered_map mHashes; + std::vector mAddedFiles; + HANDLE mMpq; +}; +#endif \ No newline at end of file diff --git a/OTRExporter/OTRExporter/Main.cpp b/OTRExporter/OTRExporter/Main.cpp new file mode 100644 index 000000000..aff12cb64 --- /dev/null +++ b/OTRExporter/OTRExporter/Main.cpp @@ -0,0 +1,440 @@ +#include "Main.h" +#include "Exporter.h" +#include "BackgroundExporter.h" +#include "TextureExporter.h" +#include "RoomExporter.h" +#include "CollisionExporter.h" +#include "DisplayListExporter.h" +#include "PlayerAnimationExporter.h" +#include "SkeletonExporter.h" +#include "SkeletonLimbExporter.h" +#include "ArrayExporter.h" +#include "VtxExporter.h" +#include "AnimationExporter.h" +#include "CutsceneExporter.h" +#include "PathExporter.h" +#include "TextExporter.h" +#include "TextMMExporter.h" +#include "BlobExporter.h" +#include "MtxExporter.h" +#include "AudioExporter.h" +#include "TextureAnimationExporter.h" +#include "CKeyFrameExporter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ExporterArchiveOTR.h" +#ifdef GAME_MM +std::string archiveFileName = "mm.o2r"; +#elif GAME_OOT +std::string archiveFileName = "oot.o2r"; +#endif +std::string customArchiveFileName = ""; +std::string customAssetsPath = ""; +std::string portVersionString = "0.0.0"; + +std::shared_ptr archive; +BinaryWriter* fileWriter; +std::chrono::steady_clock::time_point fileStart, resStart; +std::map> files; +std::mutex fileMutex; + +void InitVersionInfo(); + +enum class ExporterFileMode +{ + BuildOTR = (int)ZFileMode::Custom + 1, +}; + +static void ExporterParseFileMode(const std::string& buildMode, ZFileMode& fileMode) +{ + if (buildMode == "botr") + { + fileMode = (ZFileMode)ExporterFileMode::BuildOTR; + + printf("BOTR: Generating OTR Archive...\n"); + + archive = std::make_shared(archiveFileName, true); + + if (DiskFile::Exists(archiveFileName)) + archive->Load(true); + else + archive->CreateArchive(40000); + + auto lst = Directory::ListFiles("Extract"); + + for (auto item : lst) + { + auto fileData = DiskFile::ReadAllBytes(item); + archive->AddFile(StringHelper::Split(item, "Extract/")[1], fileData.data(), fileData.size()); + } + } +} + +typedef struct Data { + std::vector fileData; + std::string filePath; + size_t size; +} Data; + +typedef struct DataU { + std::vector fileData; + std::string filePath; + size_t size; +} DataU; + +static void ExporterProgramEnd() +{ + uint32_t crc = 0xFFFFFFFF; + const uint8_t endianness = (uint8_t)Endianness::Big; + + std::vector portVersion = {}; + std::vector versionParts = StringHelper::Split(portVersionString, "."); + + // If a major.minor.patch string was not passed in, fallback to 0 0 0 + if (versionParts.size() != 3) { + portVersion = { 0, 0, 0 }; + } else { + // Parse version values to number + for (const auto& val : versionParts) { + uint16_t num = 0; + try { + num = (uint16_t)std::stoi(val, nullptr); + } catch (std::invalid_argument &e) { + num = 0; + } catch (std::out_of_range &e) { + num = 0; + } + + portVersion.push_back(num); + } + } + + MemoryStream *portVersionStream = new MemoryStream(); + BinaryWriter portVerWriter(portVersionStream); + portVerWriter.SetEndianness(Endianness::Big); + portVerWriter.Write(endianness); + portVerWriter.Write(portVersion[0]); // Major + portVerWriter.Write(portVersion[1]); // Minor + portVerWriter.Write(portVersion[2]); // Patch + portVerWriter.Close(); + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + { + std::string romPath = Globals::Instance->baseRomPath.string(); + std::vector romData = DiskFile::ReadAllBytes(romPath); + + BitConverter::RomToBigEndian(romData.data(), romData.size()); + + crc = BitConverter::ToUInt32BE(romData, 0x10); + printf("Creating version file...\n"); + + // Get crc from rom + + MemoryStream *versionStream = new MemoryStream(); + BinaryWriter writer(versionStream); + writer.SetEndianness(Endianness::Big); + writer.Write(endianness); + writer.Write(crc); + writer.Close(); + + printf("Created version file.\n"); + + printf("Generating OTR Archive...\n"); + archive = std::make_shared(archiveFileName, true); + archive->CreateArchive(40000); + + printf("Adding game version file.\n"); + auto versionStreamBuffer = versionStream->ToVector(); + archive->AddFile("version", (void*)versionStreamBuffer.data(), versionStream->GetLength()); + + printf("Adding portVersion file.\n"); + auto portVersionStreamBuffer = portVersionStream->ToVector(); + archive->AddFile("portVersion", (void*)portVersionStreamBuffer.data(), portVersionStream->GetLength()); + + for (const auto& item : files) + { + std::string fName = item.first; + if (fName.find("gTitleZeldaShieldLogoMQTex") != std::string::npos && !ZRom(romPath).IsMQ()) + { + size_t pos = 0; + if ((pos = fName.find("gTitleZeldaShieldLogoMQTex", 0)) != std::string::npos) + { + fName.replace(pos, 27, "gTitleZeldaShieldLogoTex"); + } + } + const auto& fileData = item.second; + archive->AddFile(fName, (void*)fileData.data(), fileData.size()); + } + + archive = nullptr; + } + + delete fileWriter; + files.clear(); + + // Generate custom otr file for extra assets + if (customAssetsPath == "" || customArchiveFileName == "" || DiskFile::Exists(customArchiveFileName)) { + printf("No Custom Assets path or otr file name provided, otr file already exists. Nothing to do.\n"); + return; + } + + if (!customAssetsPath.ends_with("/")) { + customAssetsPath += "/"; + } + + const auto& lst = Directory::ListFiles(customAssetsPath); + + printf("Generating Custom OTR Archive...\n"); + auto customOtr = std::make_unique(customArchiveFileName, true); + customOtr->CreateArchive(40000); + + printf("Adding portVersion file.\n"); + auto portVersionStreamBuffer = portVersionStream->ToVector(); + customOtr->AddFile("portVersion", (void*)portVersionStreamBuffer.data(), portVersionStream->GetLength()); + + std::vector dataVec; + std::vector dataVec2; + + + for (const auto& item : lst) + { + size_t filenameSepAt = item.find_last_of("/\\"); + const std::string filename = item.substr(filenameSepAt + 1); + + if (std::count(filename.begin(), filename.end(), '.') >= 2) + { + size_t extensionSepAt = filename.find_last_of("."); + size_t formatSepAt = filename.find_last_of(".", extensionSepAt - 1); + + const std::string extension = filename.substr(extensionSepAt + 1); + const std::string format = filename.substr(formatSepAt + 1, extensionSepAt - formatSepAt - 1); + std::string afterPath = item.substr(0, filenameSepAt + formatSepAt + 1); + + if (extension == "png" && (format == "rgba32" || format == "rgb5a1" || format == "i4" || format == "i8" || format == "ia4" || format == "ia8" || format == "ia16" || format == "ci4" || format == "ci8")) + { + ZTexture tex(nullptr); + Globals::Instance->buildRawTexture = true; + tex.FromPNG(item, ZTexture::GetTextureTypeFromString(format)); + printf("customOtr->AddFile(%s)\n", StringHelper::Split(afterPath, customAssetsPath)[1].c_str()); + + OTRExporter_Texture exporter; + + MemoryStream* stream = new MemoryStream(); + BinaryWriter writer(stream); + + exporter.Save(&tex, "", &writer); + + std::string src = tex.GetBodySourceCode(); + writer.Write((char *)src.c_str(), src.size()); + + std::vector fileData = stream->ToVector(); + dataVec.push_back({ fileData, StringHelper::Split(afterPath, customAssetsPath)[1], fileData.size() }); + continue; + } + } + + if (item.find("accessibility") != std::string::npos) + { + std::string extension = filename.substr(filename.find_last_of(".") + 1); + if (extension == "json") + { + const auto &fileData = DiskFile::ReadAllBytes(item); + printf("Adding accessibility texts %s\n", StringHelper::Split(item, customAssetsPath)[1].c_str()); + dataVec2.push_back({fileData, + StringHelper::Split(item, customAssetsPath)[1], fileData.size() }); + } + continue; + } + + const auto& fileData = DiskFile::ReadAllBytes(item); + printf("customOtr->AddFile(%s)\n", StringHelper::Split(item, customAssetsPath)[1].c_str()); + dataVec2.push_back({ fileData, StringHelper::Split(item, customAssetsPath)[1], fileData.size() }); + } + for (auto& d : dataVec) { + customOtr->AddFile(d.filePath, d.fileData.data(), d.size); + } + + for (auto& d : dataVec2) { + customOtr->AddFile(d.filePath, d.fileData.data(), d.size); + } + + printf("Done\n"); + // For O2Rs the zip file MUST be closed while the vectors are still valid so we need to close the file in this function. + customOtr = nullptr; +} + +static void ExporterParseArgs(int argc, char* argv[], int& i) +{ + std::string arg = argv[i]; + + if (arg == "--otrfile") { + archiveFileName = argv[i + 1]; + i++; + } else if (arg == "--customOtrFile") { + customArchiveFileName = argv[i + 1]; + i++; + } else if (arg == "--customAssetsPath") { + customAssetsPath = argv[i + 1]; + i++; + } else if (arg == "--portVer") { + portVersionString = argv[i + 1]; + i++; + } +} + +static bool ExporterProcessFileMode(ZFileMode fileMode) +{ + // Do whatever work is associated with these custom file modes... + // Return true to indicate one of our own file modes is being processed + if (fileMode == (ZFileMode)ExporterFileMode::BuildOTR) + return true; + + return false; +} + +static void ExporterFileBegin(ZFile* file) +{ + fileStart = std::chrono::steady_clock::now(); + + MemoryStream* stream = new MemoryStream(); + fileWriter = new BinaryWriter(stream); +} + +static void ExporterFileEnd(ZFile* file) +{ + // delete fileWriter; +} + +static void ExporterResourceEnd(ZResource* res, BinaryWriter& writer) +{ + auto streamShared = writer.GetStream(); + MemoryStream* strem = (MemoryStream*)streamShared.get(); + + auto start = std::chrono::steady_clock::now(); + + if (res->GetName() != "") + { + std::string oName = res->parent->GetOutName(); + std::string rName = res->GetName(); + std::string prefix = OTRExporter_DisplayList::GetPrefix(res); + + //auto xmlFilePath = res->parent->GetXmlFilePath(); + //prefix = StringHelper::Split(StringHelper::Split(xmlFilePath.string(), "xml\\")[1], ".xml")[0]; + + if (StringHelper::Contains(oName, "_scene")) + { + auto split = StringHelper::Split(oName, "_"); + oName = ""; + for (size_t i = 0; i < split.size() - 1; i++) + oName += split[i] + "_"; + + oName += "scene"; + } + else if (StringHelper::Contains(oName, "_room")) + { + if (Globals::Instance->game != ZGame::MM_RETAIL) + oName = StringHelper::Split(oName, "_room")[0] + "_scene"; + else + oName = StringHelper::Split(oName, "_room")[0]; + } + + std::string fName = ""; + + if (prefix != "") + fName = StringHelper::Sprintf("%s/%s/%s", prefix.c_str(), oName.c_str(), rName.c_str()); + else + fName = StringHelper::Sprintf("%s/%s", oName.c_str(), rName.c_str()); + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + { + std::unique_lock Lock(fileMutex); + files[fName] = strem->ToVector(); + } + else + DiskFile::WriteAllBytes("Extract/" + fName, strem->ToVector()); + } + + auto end = std::chrono::steady_clock::now(); + size_t diff = std::chrono::duration_cast(end - start).count(); + + //if (diff > 10) + //printf("Exported Resource End %s in %zums\n", res->GetName().c_str(), diff); +} + +static void ExporterProcessCompilable(tinyxml2::XMLElement* reader) +{ + std::string nodeName = reader->Name(); +} + +static void ExporterXMLBegin() +{ +} + +static void ExporterXMLEnd() +{ +} + +void AddFile(std::string fName, std::vector data) +{ + if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) + DiskFile::WriteAllBytes("Extract/" + fName, data); + else + { + std::unique_lock Lock(fileMutex); + files[fName] = data; + } +} + +void ImportExporters() +{ + // In this example we set up a new exporter called "EXAMPLE". + // By running ZAPD with the argument -se EXAMPLE, we tell it that we want to use this exporter for our resources. + ExporterSet* exporterSet = new ExporterSet(); + exporterSet->processFileModeFunc = ExporterProcessFileMode; + exporterSet->parseFileModeFunc = ExporterParseFileMode; + exporterSet->processCompilableFunc = ExporterProcessCompilable; + exporterSet->parseArgsFunc = ExporterParseArgs; + exporterSet->beginFileFunc = ExporterFileBegin; + exporterSet->endFileFunc = ExporterFileEnd; + exporterSet->beginXMLFunc = ExporterXMLBegin; + exporterSet->endXMLFunc = ExporterXMLEnd; + exporterSet->resSaveFunc = ExporterResourceEnd; + exporterSet->endProgramFunc = ExporterProgramEnd; + + exporterSet->exporters[ZResourceType::Background] = new OTRExporter_Background(); + exporterSet->exporters[ZResourceType::Texture] = new OTRExporter_Texture(); + exporterSet->exporters[ZResourceType::Room] = new OTRExporter_Room(); + exporterSet->exporters[ZResourceType::AltHeader] = new OTRExporter_Room(); + exporterSet->exporters[ZResourceType::Scene] = new OTRExporter_Room(); + exporterSet->exporters[ZResourceType::CollisionHeader] = new OTRExporter_Collision(); + exporterSet->exporters[ZResourceType::DisplayList] = new OTRExporter_DisplayList(); + exporterSet->exporters[ZResourceType::PlayerAnimationData] = new OTRExporter_PlayerAnimationExporter(); + exporterSet->exporters[ZResourceType::Skeleton] = new OTRExporter_Skeleton(); + exporterSet->exporters[ZResourceType::Limb] = new OTRExporter_SkeletonLimb(); + exporterSet->exporters[ZResourceType::Animation] = new OTRExporter_Animation(); + exporterSet->exporters[ZResourceType::Cutscene] = new OTRExporter_Cutscene(); + exporterSet->exporters[ZResourceType::Vertex] = new OTRExporter_Vtx(); + exporterSet->exporters[ZResourceType::Array] = new OTRExporter_Array(); + exporterSet->exporters[ZResourceType::Path] = new OTRExporter_Path(); + exporterSet->exporters[ZResourceType::Text] = new OTRExporter_Text(); +#ifdef GAME_MM + exporterSet->exporters[ZResourceType::TextMM] = new OTRExporter_TextMM(); + exporterSet->exporters[ZResourceType::KeyFrameSkel] = new OTRExporter_CKeyFrameSkel(); + exporterSet->exporters[ZResourceType::KeyFrameAnimation] = new OTRExporter_CKeyFrameAnim(); + exporterSet->exporters[ZResourceType::TextureAnimation] = new OTRExporter_TextureAnimation(); +#endif + exporterSet->exporters[ZResourceType::Blob] = new OTRExporter_Blob(); + exporterSet->exporters[ZResourceType::Mtx] = new OTRExporter_MtxExporter(); + exporterSet->exporters[ZResourceType::Audio] = new OTRExporter_Audio(); + Globals::AddExporter("OTR", exporterSet); + + InitVersionInfo(); +} diff --git a/OTRExporter/OTRExporter/Main.h b/OTRExporter/OTRExporter/Main.h new file mode 100644 index 000000000..8c7118ec6 --- /dev/null +++ b/OTRExporter/OTRExporter/Main.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include "ExporterArchive.h" + +extern std::shared_ptr archive; +extern std::map> files; + +void AddFile(std::string fName, std::vector data); diff --git a/OTRExporter/OTRExporter/MtxExporter.cpp b/OTRExporter/OTRExporter/MtxExporter.cpp new file mode 100644 index 000000000..a9320e778 --- /dev/null +++ b/OTRExporter/OTRExporter/MtxExporter.cpp @@ -0,0 +1,13 @@ +#include "MtxExporter.h" + +void OTRExporter_MtxExporter::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZMtx* mtx = (ZMtx*)res; + + WriteHeader(res, outPath, writer, static_cast(Fast::ResourceType::Matrix)); + + for (size_t i = 0; i < 4; i++) + for (size_t j = 0; j < 4; j++) + //TODO possibly utilize the array class better + writer->Write(mtx->mtx[i][j]); +} \ No newline at end of file diff --git a/OTRExporter/OTRExporter/MtxExporter.h b/OTRExporter/OTRExporter/MtxExporter.h new file mode 100644 index 000000000..895f2a40e --- /dev/null +++ b/OTRExporter/OTRExporter/MtxExporter.h @@ -0,0 +1,10 @@ +#include "ZResource.h" +#include "ZMtx.h" +#include "Exporter.h" +#include + +class OTRExporter_MtxExporter : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; diff --git a/OTRExporter/OTRExporter/PathExporter.cpp b/OTRExporter/OTRExporter/PathExporter.cpp new file mode 100644 index 000000000..1574cb785 --- /dev/null +++ b/OTRExporter/OTRExporter/PathExporter.cpp @@ -0,0 +1,27 @@ +#include "PathExporter.h" +#include "../ZAPD/ZFile.h" +#include "Globals.h" + +void OTRExporter_Path::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZPath* path = (ZPath*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_Path)); + + writer->Write((uint32_t)path->pathways.size()); + + for (size_t k = 0; k < path->pathways.size(); k++) + { + writer->Write((uint32_t)path->pathways[k].points.size()); + if (Globals::Instance->game == ZGame::MM_RETAIL) { + writer->Write(path->pathways[k].unk1); + writer->Write(path->pathways[k].unk2); + } + for (size_t i = 0; i < path->pathways[k].points.size(); i++) + { + writer->Write(path->pathways[k].points[i].scalars[0].scalarData.s16); + writer->Write(path->pathways[k].points[i].scalars[1].scalarData.s16); + writer->Write(path->pathways[k].points[i].scalars[2].scalarData.s16); + } + } +} \ No newline at end of file diff --git a/OTRExporter/OTRExporter/PathExporter.h b/OTRExporter/OTRExporter/PathExporter.h new file mode 100644 index 000000000..9614b84df --- /dev/null +++ b/OTRExporter/OTRExporter/PathExporter.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ZResource.h" +#include "ZPath.h" +#include "Exporter.h" +#include + +class OTRExporter_Path : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/PlayerAnimationExporter.cpp b/OTRExporter/OTRExporter/PlayerAnimationExporter.cpp new file mode 100644 index 000000000..4c25b1aa9 --- /dev/null +++ b/OTRExporter/OTRExporter/PlayerAnimationExporter.cpp @@ -0,0 +1,21 @@ +#include "PlayerAnimationExporter.h" +#include + +void OTRExporter_PlayerAnimationExporter::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZPlayerAnimationData* anim = (ZPlayerAnimationData*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_PlayerAnimation)); + + auto start = std::chrono::steady_clock::now(); + + writer->Write((uint32_t)anim->limbRotData.size()); + + for (size_t i = 0; i < anim->limbRotData.size(); i++) + writer->Write(anim->limbRotData[i]); + + auto end = std::chrono::steady_clock::now(); + size_t diff = std::chrono::duration_cast(end - start).count(); + + //printf("Exported Player Anim %s in %zums\n", anim->GetName().c_str(), diff); +} diff --git a/OTRExporter/OTRExporter/PlayerAnimationExporter.h b/OTRExporter/OTRExporter/PlayerAnimationExporter.h new file mode 100644 index 000000000..49e5468a2 --- /dev/null +++ b/OTRExporter/OTRExporter/PlayerAnimationExporter.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ZResource.h" +#include "ZTexture.h" +#include "ZPlayerAnimationData.h" +#include "Exporter.h" +#include + +class OTRExporter_PlayerAnimationExporter : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/RoomExporter.cpp b/OTRExporter/OTRExporter/RoomExporter.cpp new file mode 100644 index 000000000..2db8554df --- /dev/null +++ b/OTRExporter/OTRExporter/RoomExporter.cpp @@ -0,0 +1,636 @@ +#define NO_GDI +#define WIN32_LEAN_AND_MEAN +#include "RoomExporter.h" +#include "Utils/BinaryWriter.h" +#include "Utils/MemoryStream.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CollisionExporter.h" +#include "DisplayListExporter.h" +#include +#include +#include +#include +#include "TextureExporter.h" +#include "Main.h" +#include +#include "CutsceneExporter.h" +#include +#include +#include +#include +#include +#include "TextureAnimationExporter.h" +#include "PathExporter.h" +#undef FindResource + + +void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZRoom* room = (ZRoom*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_Room)); + + writer->Write((uint32_t)room->commands.size()); + + for (size_t i = 0; i < room->commands.size(); i++) + { + ZRoomCommand* cmd = room->commands[i]; + + writer->Write((uint32_t)cmd->cmdID); + + switch (cmd->cmdID) + { + case RoomCommand::SetTransitionActorList: + { + SetTransitionActorList* cmdTrans = (SetTransitionActorList*)cmd; + + writer->Write((uint32_t)cmdTrans->transitionActors.size()); + + for (const TransitionActorEntry& entry : cmdTrans->transitionActors) + { + writer->Write(entry.frontObjectRoom); + writer->Write(entry.frontTransitionReaction); + writer->Write(entry.backObjectRoom); + writer->Write(entry.backTransitionReaction); + writer->Write(entry.actorNum); + writer->Write(entry.posX); + writer->Write(entry.posY); + writer->Write(entry.posZ); + writer->Write(entry.rotY); + writer->Write(entry.initVar); + } + } + break; + case RoomCommand::SetActorList: + { + SetActorList* cmdSetActorList = (SetActorList*)cmd; + + // There are instance of the amount of actors in the file differing from the size listed in the command. + // This can cause issues if we export actors with garbage data, so let's trust the command size + writer->Write((uint32_t)cmdSetActorList->numActors); + + for (const auto& entry : cmdSetActorList->actorList->actors) + { + writer->Write(entry.actorNum); + writer->Write(entry.posX); + writer->Write(entry.posY); + writer->Write(entry.posZ); + writer->Write(entry.rotX); + writer->Write(entry.rotY); + writer->Write(entry.rotZ); + writer->Write(entry.params); + } + } + break; + case RoomCommand::SetWind: + { + SetWind* cmdSetWind = (SetWind*)cmd; + + writer->Write(cmdSetWind->windWest); // 0x04 + writer->Write(cmdSetWind->windVertical); // 0x05 + writer->Write(cmdSetWind->windSouth); // 0x06 + writer->Write(cmdSetWind->clothFlappingStrength); // 0x07 + } + break; + case RoomCommand::SetTimeSettings: + { + SetTimeSettings* cmdTime = (SetTimeSettings*)cmd; + + writer->Write(cmdTime->hour); // 0x04 + writer->Write(cmdTime->min); // 0x05 + writer->Write(cmdTime->unk); // 0x06 + } + break; + case RoomCommand::SetSkyboxModifier: + { + SetSkyboxModifier* cmdSkybox = (SetSkyboxModifier*)cmd; + + writer->Write(cmdSkybox->disableSky); // 0x04 + writer->Write(cmdSkybox->disableSunMoon); // 0x05 + } + break; + case RoomCommand::SetEchoSettings: + { + SetEchoSettings* cmdEcho = (SetEchoSettings*)cmd; + + writer->Write((uint8_t)cmdEcho->echo); // 0x07 + } + break; + case RoomCommand::SetSoundSettings: + { + SetSoundSettings* cmdSound = (SetSoundSettings*)cmd; + + writer->Write((uint8_t)cmdSound->reverb); // 0x01 + + writer->Write(cmdSound->nightTimeSFX); // 0x06 + writer->Write(cmdSound->musicSequence); // 0x07 + } + break; + case RoomCommand::SetSkyboxSettings: + { + SetSkyboxSettings* cmdSkybox = (SetSkyboxSettings*)cmd; + + writer->Write((uint8_t)cmdSkybox->unk1); // 0x01 + writer->Write((uint8_t)cmdSkybox->skyboxNumber); // 0x04 + writer->Write((uint8_t)cmdSkybox->cloudsType); // 0x05 + writer->Write((uint8_t)cmdSkybox->isIndoors); // 0x06 + } + break; + case RoomCommand::SetRoomBehavior: + { + SetRoomBehavior* cmdRoom = (SetRoomBehavior*)cmd; + if (Globals::Instance->game == ZGame::MM_RETAIL) { + writer->Write(cmdRoom->gameplayFlags); + writer->Write(cmdRoom->currRoomUnk2); + writer->Write(cmdRoom->currRoomUnk5); + writer->Write(cmdRoom->msgCtxUnk); + writer->Write(cmdRoom->enablePosLights); + writer->Write(cmdRoom->kankyoContextUnkE2); + } else { + writer->Write((uint8_t)cmdRoom->gameplayFlags); // 0x01 + writer->Write(cmdRoom->gameplayFlags2); // 0x04 + } + } + break; + case RoomCommand::SetCsCamera: + { + SetCsCamera* cmdCsCam = (SetCsCamera*)cmd; + + writer->Write((uint32_t)cmdCsCam->cameras.size()); + segptr_t arrBase = cmdCsCam->cameras[0].segmentOffset; + for (const auto& c : cmdCsCam->cameras) { + writer->Write(c.type); + writer->Write(c.numPoints); + for (int16_t i = 0; i < c.numPoints; i++) { + writer->Write(cmdCsCam->points[((c.segmentOffset - arrBase) / 6) + i].scalars[0].scalarData.s16); + writer->Write(cmdCsCam->points[((c.segmentOffset - arrBase) / 6) + i].scalars[1].scalarData.s16); + writer->Write(cmdCsCam->points[((c.segmentOffset - arrBase) / 6) + i].scalars[2].scalarData.s16); + } + } + } + break; + case RoomCommand::SetMesh: + { + SetMesh* cmdMesh = (SetMesh*)cmd; + + writer->Write((uint8_t)cmdMesh->data); // 0x01 + writer->Write(cmdMesh->meshHeaderType); + + if (cmdMesh->meshHeaderType == 0 || cmdMesh->meshHeaderType == 2) + { + RoomShapeCullable* poly = (RoomShapeCullable*)cmdMesh->polyType.get(); + + writer->Write(poly->num); + + for (int i = 0; i < poly->num; i++) + WritePolyDList(writer, room, &poly->polyDLists[i]); + } + else if (cmdMesh->meshHeaderType == 1) + { + PolygonType1* poly = (PolygonType1*)cmdMesh->polyType.get(); + + writer->Write(poly->format); + + auto test = (RoomShapeDListsEntry*)&poly->polyDLists[0]; + Declaration* dListDeclOpa = poly->parent->GetDeclaration(GETSEGOFFSET(test->opa)); + Declaration* dListDeclXlu = poly->parent->GetDeclaration(GETSEGOFFSET(test->xlu)); + + if (test->opa != 0) + writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclOpa->declName.c_str())); + else + writer->Write(""); + + if (test->xlu != 0) + writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclXlu->declName.c_str())); + else + writer->Write(""); + + if (poly->format == 2) + { + writer->Write((uint32_t)poly->count); + + for (int i = 0; i < poly->count; i++) + { + writer->Write(poly->multiList[i].unk_00); + writer->Write(poly->multiList[i].id); + + Declaration* bgDecl = poly->parent->GetDeclarationRanged(GETSEGOFFSET(poly->multiList[i].source)); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(poly->multiList[i].sourceBackground, bgDecl->declName)); + + writer->Write(poly->multiList[i].unk_0C); + writer->Write(poly->multiList[i].tlut); + writer->Write(poly->multiList[i].width); + writer->Write(poly->multiList[i].height); + writer->Write(poly->multiList[i].fmt); + writer->Write(poly->multiList[i].siz); + writer->Write(poly->multiList[i].mode0); + writer->Write(poly->multiList[i].tlutCount); + } + } + else + { + writer->Write((uint32_t)1); + + writer->Write(poly->single.unk_00); + writer->Write(poly->single.id); + + Declaration* bgDecl = poly->parent->GetDeclarationRanged(GETSEGOFFSET(poly->single.source)); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(poly->single.sourceBackground, bgDecl->declName)); + + writer->Write(poly->single.unk_0C); + writer->Write(poly->single.tlut); + writer->Write(poly->single.width); + writer->Write(poly->single.height); + writer->Write(poly->single.fmt); + writer->Write(poly->single.siz); + writer->Write(poly->single.mode0); + writer->Write(poly->single.tlutCount); + } + + if (poly->dlist != 0) + WritePolyDList(writer, room, &poly->polyDLists[0]); + } + } + break; + case RoomCommand::SetCameraSettings: + //case RoomCommand::SetWorldMapVisited: MM command but you can't have 0x19 twice in a switch + { + if (Globals::Instance->game != ZGame::MM_RETAIL) { + SetCameraSettings* cmdCam = (SetCameraSettings*)cmd; + + writer->Write((uint8_t)cmdCam->cameraMovement); // 0x01 + writer->Write(cmdCam->mapHighlight); // 0x04 + } + } + break; + case RoomCommand::SetLightList: + { + SetLightList* cmdLight = (SetLightList*)cmd; + + writer->Write((uint32_t)cmdLight->lights.size()); + + for (size_t i = 0; i < cmdLight->lights.size(); i++) + { + writer->Write(cmdLight->lights[i].type); + writer->Write(cmdLight->lights[i].x); + writer->Write(cmdLight->lights[i].y); + writer->Write(cmdLight->lights[i].z); + writer->Write(cmdLight->lights[i].r); + writer->Write(cmdLight->lights[i].g); + writer->Write(cmdLight->lights[i].b); + writer->Write(cmdLight->lights[i].drawGlow); + writer->Write(cmdLight->lights[i].radius); + } + } + break; + case RoomCommand::SetLightingSettings: + { + SetLightingSettings* cmdLight = (SetLightingSettings*)cmd; + + writer->Write((uint32_t)cmdLight->settings.size()); // 0x01 + + for (const LightingSettings& setting : cmdLight->settings) + { + writer->Write(setting.ambientClrR); + writer->Write(setting.ambientClrG); + writer->Write(setting.ambientClrB); + + writer->Write(setting.diffuseClrA_R); + writer->Write(setting.diffuseClrA_G); + writer->Write(setting.diffuseClrA_B); + + writer->Write(setting.diffuseDirA_X); + writer->Write(setting.diffuseDirA_Y); + writer->Write(setting.diffuseDirA_Z); + + writer->Write(setting.diffuseClrB_R); + writer->Write(setting.diffuseClrB_G); + writer->Write(setting.diffuseClrB_B); + + writer->Write(setting.diffuseDirB_X); + writer->Write(setting.diffuseDirB_Y); + writer->Write(setting.diffuseDirB_Z); + + writer->Write(setting.fogClrR); + writer->Write(setting.fogClrG); + writer->Write(setting.fogClrB); + + writer->Write(setting.unk); + writer->Write(setting.drawDistance); + } + } + break; + case RoomCommand::SetRoomList: + { + SetRoomList* cmdRoom = (SetRoomList*)cmd; + + writer->Write((uint32_t)cmdRoom->romfile->numRooms); // 0x01 + + for (size_t i = 0;i < cmdRoom->romfile->numRooms; i++) + { + //std::string roomName = StringHelper::Sprintf("%s/%s_room_%i", (StringHelper::Split(room->GetName(), "_")[0] + "_scene").c_str(), StringHelper::Split(room->GetName(), "_scene")[0].c_str(), i); + std::string roomName; + if (Globals::Instance->game != ZGame::MM_RETAIL) + roomName = OTRExporter_DisplayList::GetPathToRes(room, StringHelper::Sprintf("%s_room_%i", StringHelper::Split(room->GetName(), "_scene")[0].c_str(), i)); + else + { + // Alt headers use the rooms from their parent room. For example we want SPOT00/SPOT00_room_00, not SPOT00/SPOT00Set_00A050_room_00 + if (room->zroomType == ZResourceType::AltHeader) + roomName = OTRExporter_DisplayList::GetPathToRes(room, StringHelper::Sprintf("%s_room_%02d", cmd->parent->GetName().c_str(), i)); + else + roomName = OTRExporter_DisplayList::GetPathToRes(room, StringHelper::Sprintf("%s_room_%02d", StringHelper::Split(room->GetName(), "_scene")[0].c_str(), i)); + } + + writer->Write(roomName); + writer->Write(cmdRoom->romfile->rooms[i].virtualAddressStart); + writer->Write(cmdRoom->romfile->rooms[i].virtualAddressEnd); + } + } + break; + case RoomCommand::SetCollisionHeader: + { + SetCollisionHeader* cmdCollHeader = (SetCollisionHeader*)cmd; + + Declaration* colHeaderDecl = room->parent->GetDeclaration(cmdCollHeader->segmentOffset); + std::string path = OTRExporter_DisplayList::GetPathToRes(room, colHeaderDecl->declName); + writer->Write(path); + } + break; + case RoomCommand::SetEntranceList: + { + SetEntranceList* cmdEntrance = (SetEntranceList*)cmd; + + writer->Write((uint32_t)cmdEntrance->entrances.size()); + + for (Spawn entry : cmdEntrance->entrances) + { + writer->Write((uint8_t)entry.startPositionIndex); + writer->Write((uint8_t)entry.roomToLoad); + } + } + break; + case RoomCommand::SetSpecialObjects: + { + SetSpecialObjects* cmdSpecObj = (SetSpecialObjects*)cmd; + + writer->Write((uint8_t)cmdSpecObj->elfMessage); // 0x01 + writer->Write((uint16_t)cmdSpecObj->globalObject); // 0x06 + } + break; + case RoomCommand::SetStartPositionList: + { + SetStartPositionList* cmdStartPos = (SetStartPositionList*)cmd; + + uint32_t baseStreamEnd = writer->GetStream().get()->GetLength(); + + writer->Write((uint32_t)cmdStartPos->actors.size()); // 0x01 + + for (const ActorSpawnEntry& entry : cmdStartPos->actors) + { + writer->Write(entry.actorNum); + writer->Write(entry.posX); + writer->Write(entry.posY); + writer->Write(entry.posZ); + writer->Write(entry.rotX); + writer->Write(entry.rotY); + writer->Write(entry.rotZ); + writer->Write(entry.params); + } + } + break; + case RoomCommand::SetAlternateHeaders: + { + SetAlternateHeaders* cmdHeaders = (SetAlternateHeaders*)cmd; + + writer->Write((uint32_t)cmdHeaders->headers.size()); + + for (size_t i = 0; i < cmdHeaders->headers.size(); i++) + { + uint32_t seg = cmdHeaders->headers[i] & 0xFFFFFFFF; + std::string headerName = ""; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(seg, room->parent, "", headerName, res->parent->workerID); + if (headerName == "NULL") + writer->Write(""); + else + { + std::string name = OTRExporter_DisplayList::GetPathToRes(room, headerName); + writer->Write(name); + } + } + } + break; + case RoomCommand::SetExitList: + { + SetExitList* cmdExit = (SetExitList*)cmd; + + writer->Write((uint32_t)cmdExit->exits.size()); + + for (size_t i = 0; i < cmdExit->exits.size(); i++) + writer->Write(cmdExit->exits[i]); + } + break; + case RoomCommand::SetObjectList: + { + SetObjectList* cmdSetObjectList = (SetObjectList*)cmd; + + writer->Write((uint32_t)cmdSetObjectList->objects.size()); + + for (size_t i = 0; i < cmdSetObjectList->objects.size(); i++) + writer->Write(cmdSetObjectList->objects[i]); + } + break; + case RoomCommand::SetCutscenes: + { + SetCutscenes* cmdSetCutscenes = (SetCutscenes*)cmd; + + std::string listName; + if (Globals::Instance->game == ZGame::MM_RETAIL) { + writer->Seek(-4, SeekOffsetType::Current); + writer->Write((uint32_t)RoomCommand::SetCutscenesMM); + writer->Write(cmdSetCutscenes->numCutscenes); + for (size_t i = 0; i < cmdSetCutscenes->cutsceneEntries.size(); i++) { + Globals::Instance->GetSegmentedPtrName(cmdSetCutscenes->cutsceneEntries[i].segmentPtr, room->parent, "CutsceneData", listName, res->parent->workerID); + std::string fName = OTRExporter_DisplayList::GetPathToRes(room, listName); + writer->Write(fName); + writer->Write(cmdSetCutscenes->cutsceneEntries[i].exit); + writer->Write(cmdSetCutscenes->cutsceneEntries[i].entrance); + writer->Write(cmdSetCutscenes->cutsceneEntries[i].flag); + + MemoryStream* csStream = new MemoryStream(); + BinaryWriter csWriter = BinaryWriter(csStream); + OTRExporter_Cutscene cs; + ZResource* newCs = res->parent->FindResource(cmdSetCutscenes->cutsceneEntries[i].segmentPtr & 0x00FFFFFF); + cs.Save((ZCutscene*)newCs, "", &csWriter); + + AddFile(fName, csStream->ToVector()); + } + } + else { + Globals::Instance->GetSegmentedPtrName(cmdSetCutscenes->cmdArg2, room->parent, "CutsceneData", listName, res->parent->workerID); + std::string fName = OTRExporter_DisplayList::GetPathToRes(room, listName); + writer->Write(fName); + + MemoryStream* csStream = new MemoryStream(); + BinaryWriter csWriter = BinaryWriter(csStream); + OTRExporter_Cutscene cs; + cs.Save(cmdSetCutscenes->cutscenes[0], "", &csWriter); + + AddFile(fName, csStream->ToVector()); + } + } + break; + case RoomCommand::SetPathways: + { + SetPathways* cmdSetPathways = (SetPathways*)cmd; + + writer->Write((uint32_t)cmdSetPathways->pathwayList.pathways.size()); + + for (size_t i = 0; i < cmdSetPathways->pathwayList.pathways.size(); i++) + { + Declaration* decl = room->parent->GetDeclaration(GETSEGOFFSET(cmdSetPathways->pathwayList.pathways[i].listSegmentAddress)); + //std::string path = StringHelper::Sprintf("%s\\%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), decl->varName.c_str()); + std::string path = OTRExporter_DisplayList::GetPathToRes(room, decl->declName); + writer->Write(path); + + MemoryStream* pathStream = new MemoryStream(); + BinaryWriter pathWriter = BinaryWriter(pathStream); + OTRExporter_Path pathExp; + pathExp.Save(&cmdSetPathways->pathwayList, outPath, &pathWriter); + + AddFile(path, pathStream->ToVector()); + } + } + break; + case RoomCommand::EndMarker: + break; + case RoomCommand::SetActorCutsceneList: + { + SetActorCutsceneList* list = (SetActorCutsceneList*)cmd; + writer->Write((uint32_t)list->cutscenes.size()); + + for (const auto& e : list->cutscenes) { + writer->Write(e.priority); + writer->Write(e.length); + writer->Write(e.csCamId); + writer->Write(e.scriptIndex); + writer->Write(e.additionalCsId); + writer->Write(e.endSfx); + writer->Write(e.customValue); + writer->Write(e.hudVisibility); + writer->Write(e.endCam); + writer->Write(e.letterboxSize); + } + break; + } +#ifdef GAME_MM + case RoomCommand::SetAnimatedMaterialList: { + SetAnimatedMaterialList* list = (SetAnimatedMaterialList*)cmd; + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmd->cmdArg2, cmd->parent, "AnimatedMaterial", listName, + res->parent->workerID); + listName = OTRExporter_DisplayList::GetPathToRes(room, listName); + writer->Write(listName); + + MemoryStream* animatedMatStream = new MemoryStream(); + BinaryWriter animatedMatWriter = BinaryWriter(animatedMatStream); + OTRExporter_TextureAnimation texAnim; + + texAnim.Save(&list->textureAnimation, outPath, &animatedMatWriter); + + AddFile(listName, animatedMatStream->ToVector()); + + break; + } +#endif + case RoomCommand::SetMinimapList: { + SetMinimapList* list = (SetMinimapList*)cmd; + + writer->Write((uint32_t)list->minimaps.size()); + writer->Write(list->scale); + + for (const auto& m : list->minimaps) { + writer->Write(m.unk0); + writer->Write(m.unk2); + writer->Write(m.unk4); + writer->Write(m.unk6); + writer->Write(m.unk8); + } + + break; + } + case RoomCommand::SetMinimapChests: { + SetMinimapChests* chests = (SetMinimapChests*)cmd; + + writer->Write((uint32_t)chests->chests.size()); + + for (const auto& c : chests->chests) { + writer->Write(c.unk0); + writer->Write(c.unk2); + writer->Write(c.unk4); + writer->Write(c.unk6); + writer->Write(c.unk8); + } + break; + } + default: + printf("UNIMPLEMENTED COMMAND: 0x%02X\n", (int)cmd->cmdID); + + break; + } + } +} + +void OTRExporter_Room::WritePolyDList(BinaryWriter* writer, ZRoom* room, RoomShapeDListsEntry* dlist) +{ + writer->Write(dlist->polyType); + + switch (dlist->polyType) + { + case 2: + writer->Write(dlist->x); + writer->Write(dlist->y); + writer->Write(dlist->z); + writer->Write(dlist->unk_06); + [[fallthrough]]; + default: + //writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(res).c_str(), dListDeclOpa->varName.c_str())); + + if (dlist->opaDList != nullptr) + { + auto opaDecl = room->parent->GetDeclaration(GETSEGOFFSET(dlist->opaDList->GetRawDataIndex())); + writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(room).c_str(), opaDecl->declName.c_str())); + } + else + writer->Write(""); + + if (dlist->xluDList != nullptr) + { + auto xluDecl = room->parent->GetDeclaration(GETSEGOFFSET(dlist->xluDList->GetRawDataIndex())); + writer->Write(StringHelper::Sprintf("%s/%s", OTRExporter_DisplayList::GetParentFolderName(room).c_str(), xluDecl->declName.c_str())); + } + else + writer->Write(""); + break; + } +} diff --git a/OTRExporter/OTRExporter/RoomExporter.h b/OTRExporter/OTRExporter/RoomExporter.h new file mode 100644 index 000000000..08306b7cb --- /dev/null +++ b/OTRExporter/OTRExporter/RoomExporter.h @@ -0,0 +1,15 @@ +#pragma once +#define NO_GDI +#define WIN32_LEAN_AND_MEAN +#include "ZResource.h" +#include "Exporter.h" +#include "ZRoom/ZRoom.h" + +class RoomShapeDListsEntry; + +class OTRExporter_Room : public OTRExporter +{ +public: + void WritePolyDList(BinaryWriter* writer, ZRoom* room, RoomShapeDListsEntry* dlist); + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/SkeletonExporter.cpp b/OTRExporter/OTRExporter/SkeletonExporter.cpp new file mode 100644 index 000000000..b6c869427 --- /dev/null +++ b/OTRExporter/OTRExporter/SkeletonExporter.cpp @@ -0,0 +1,39 @@ +#include "SkeletonExporter.h" +#include +#include +#include "DisplayListExporter.h" + +void OTRExporter_Skeleton::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZSkeleton* skel = (ZSkeleton*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_Skeleton)); + + writer->Write((uint8_t)skel->type); + writer->Write((uint8_t)skel->limbType); + + writer->Write((uint32_t)skel->limbCount); + writer->Write((uint32_t)skel->dListCount); + + writer->Write((uint8_t)skel->limbsTable->limbType); + writer->Write((uint32_t)skel->limbsTable->count); + + for (size_t i = 0; i < skel->limbsTable->count; i++) + { + Declaration* skelDecl = skel->parent->GetDeclarationRanged(GETSEGOFFSET(skel->limbsTable->limbsAddresses[i])); + + std::string name; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(skel->limbsTable->limbsAddresses[i], skel->parent, "", name, res->parent->workerID); + if (foundDecl) + { + if (name.at(0) == '&') + name.erase(0, 1); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(res, name)); + } + else + { + writer->Write(""); + } + } +} diff --git a/OTRExporter/OTRExporter/SkeletonExporter.h b/OTRExporter/OTRExporter/SkeletonExporter.h new file mode 100644 index 000000000..36ffcf269 --- /dev/null +++ b/OTRExporter/OTRExporter/SkeletonExporter.h @@ -0,0 +1,14 @@ +#pragma once + +#include "ZResource.h" +#include "ZTexture.h" +#include "ZDisplayList.h" +#include "ZSkeleton.h" +#include "Exporter.h" +#include + +class OTRExporter_Skeleton : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/SkeletonLimbExporter.cpp b/OTRExporter/OTRExporter/SkeletonLimbExporter.cpp new file mode 100644 index 000000000..0e8b19c9a --- /dev/null +++ b/OTRExporter/OTRExporter/SkeletonLimbExporter.cpp @@ -0,0 +1,180 @@ +#include "SkeletonLimbExporter.h" +#include "DisplayListExporter.h" +#include +#include + +void OTRExporter_SkeletonLimb::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZLimb* limb = (ZLimb*)res; + + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::SOH_SkeletonLimb)); + + writer->Write((uint8_t)limb->type); + writer->Write((uint8_t)limb->skinSegmentType); + + if (limb->skinSegmentType == ZLimbSkinType::SkinType_Normal && limb->type == ZLimbType::Skin) + { + auto childDecl = limb->parent->GetDeclaration(GETSEGOFFSET(limb->skinSegment)); + + if (childDecl != nullptr) + writer->Write(OTRExporter_DisplayList::GetPathToRes(limb, childDecl->declName)); + else + writer->Write(""); + } + else + { + writer->Write(""); + } + + writer->Write((uint16_t)limb->segmentStruct.totalVtxCount); + writer->Write((uint32_t)limb->segmentStruct.limbModifications_arr.size()); + + for (auto item : limb->segmentStruct.limbModifications_arr) + { + writer->Write(item.unk_4); + + writer->Write((uint32_t)item.skinVertices_arr.size()); + + for (auto item2 : item.skinVertices_arr) + { + writer->Write(item2.index); + writer->Write(item2.s); + writer->Write(item2.t); + writer->Write(item2.normX); + writer->Write(item2.normY); + writer->Write(item2.normZ); + writer->Write(item2.alpha); + } + + writer->Write((uint32_t)item.limbTransformations_arr.size()); + + for (auto item2 : item.limbTransformations_arr) + { + writer->Write(item2.limbIndex); + writer->Write(item2.x); + writer->Write(item2.y); + writer->Write(item2.z); + writer->Write(item2.scale); + } + } + + if (limb->segmentStruct.dlist != SEGMENTED_NULL) + { + auto skinGfxDecl = limb->parent->GetDeclaration(GETSEGOFFSET(limb->segmentStruct.dlist)); + + if (skinGfxDecl != nullptr) + { + writer->Write(OTRExporter_DisplayList::GetPathToRes(limb, skinGfxDecl->declName)); + } + else + { + writer->Write(""); + } + } + else + { + writer->Write(""); + } + + writer->Write(limb->legTransX); + writer->Write(limb->legTransY); + writer->Write(limb->legTransZ); + writer->Write(limb->rotX); + writer->Write(limb->rotY); + writer->Write(limb->rotZ); + + if (limb->childPtr != 0) + { + std::string name; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(limb->childPtr, limb->parent, "", name, res->parent->workerID); + if (foundDecl) + { + if (name.at(0) == '&') + name.erase(0, 1); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(limb, name)); + } + else + { + writer->Write(""); + } + } + else + { + writer->Write(""); + } + + if (limb->siblingPtr != 0) + { + std::string name; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(limb->siblingPtr, limb->parent, "", name, res->parent->workerID); + if (foundDecl) + { + if (name.at(0) == '&') + name.erase(0, 1); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(limb, name)); + } + else + { + writer->Write(""); + } + } + else + { + writer->Write(""); + } + + if (limb->dListPtr != 0) + { + std::string name; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(limb->dListPtr, limb->parent, "", name, res->parent->workerID); + if (foundDecl) + { + if (name.at(0) == '&') + name.erase(0, 1); + + ZFile* assocFile = Globals::Instance->GetSegment(GETSEGNUM(limb->dListPtr), res->parent->workerID); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(assocFile->resources[0], name)); + } + else + { + writer->Write(""); + } + } + else + { + writer->Write(""); + } + + if (limb->dList2Ptr != 0) + { + std::string name; + bool foundDecl = Globals::Instance->GetSegmentedPtrName(limb->dList2Ptr, limb->parent, "", name, res->parent->workerID); + if (foundDecl) + { + if (name.at(0) == '&') + name.erase(0, 1); + + ZFile* assocFile = Globals::Instance->GetSegment(GETSEGNUM(limb->dList2Ptr), res->parent->workerID); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(assocFile->resources[0], name)); + } + else + { + writer->Write(""); + } + } + else + { + writer->Write(""); + } + + writer->Write(limb->transX); + writer->Write(limb->transY); + writer->Write(limb->transZ); + + writer->Write(limb->childIndex); + writer->Write(limb->siblingIndex); +} diff --git a/OTRExporter/OTRExporter/SkeletonLimbExporter.h b/OTRExporter/OTRExporter/SkeletonLimbExporter.h new file mode 100644 index 000000000..000827443 --- /dev/null +++ b/OTRExporter/OTRExporter/SkeletonLimbExporter.h @@ -0,0 +1,15 @@ +#pragma once + +#include "ZResource.h" +#include "ZTexture.h" +#include "ZDisplayList.h" +#include "ZSkeleton.h" +#include "ZLimb.h" +#include "Exporter.h" +#include + +class OTRExporter_SkeletonLimb : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/TextExporter.cpp b/OTRExporter/OTRExporter/TextExporter.cpp new file mode 100644 index 000000000..1dde7cf11 --- /dev/null +++ b/OTRExporter/OTRExporter/TextExporter.cpp @@ -0,0 +1,19 @@ +#include "TextExporter.h" +#include "../ZAPD/ZFile.h" + +void OTRExporter_Text::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZText* txt = (ZText*)res; + + WriteHeader(txt, outPath, writer, static_cast(SOH::ResourceType::SOH_Text)); + + writer->Write((uint32_t)txt->messages.size()); + + for (size_t i = 0; i < txt->messages.size(); i++) + { + writer->Write(txt->messages[i].id); + writer->Write(txt->messages[i].textboxType); + writer->Write(txt->messages[i].textboxYPos); + writer->Write(txt->messages[i].msg); + } +} diff --git a/OTRExporter/OTRExporter/TextExporter.h b/OTRExporter/OTRExporter/TextExporter.h new file mode 100644 index 000000000..2eaf2ce71 --- /dev/null +++ b/OTRExporter/OTRExporter/TextExporter.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ZResource.h" +#include "ZText.h" +#include "Exporter.h" +#include + +class OTRExporter_Text : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/TextMMExporter.cpp b/OTRExporter/OTRExporter/TextMMExporter.cpp new file mode 100644 index 000000000..f74d6866e --- /dev/null +++ b/OTRExporter/OTRExporter/TextMMExporter.cpp @@ -0,0 +1,25 @@ +#ifdef GAME_MM +#include "TextMMExporter.h" +#include "../ZAPD/ZFile.h" + +void OTRExporter_TextMM::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZTextMM* txt = (ZTextMM*)res; + + WriteHeader(txt, outPath, writer, static_cast(SOH::ResourceType::TSH_TextMM)); + + writer->Write((uint32_t)txt->messages.size()); + + for (size_t i = 0; i < txt->messages.size(); i++) + { + writer->Write(txt->messages[i].id); + writer->Write(txt->messages[i].textboxType); + writer->Write(txt->messages[i].textboxYPos); + writer->Write(txt->messages[i].icon); + writer->Write(txt->messages[i].nextMessageID); + writer->Write(txt->messages[i].firstItemCost); + writer->Write(txt->messages[i].secondItemCost); + writer->Write(txt->messages[i].msg); + } +} +#endif \ No newline at end of file diff --git a/OTRExporter/OTRExporter/TextMMExporter.h b/OTRExporter/OTRExporter/TextMMExporter.h new file mode 100644 index 000000000..d236fd536 --- /dev/null +++ b/OTRExporter/OTRExporter/TextMMExporter.h @@ -0,0 +1,13 @@ +#pragma once +#ifdef GAME_MM +#include "ZResource.h" +#include "ZTextMM.h" +#include "Exporter.h" +#include + +class OTRExporter_TextMM : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; +#endif diff --git a/OTRExporter/OTRExporter/TextureAnimationExporter.cpp b/OTRExporter/OTRExporter/TextureAnimationExporter.cpp new file mode 100644 index 000000000..a5a578f36 --- /dev/null +++ b/OTRExporter/OTRExporter/TextureAnimationExporter.cpp @@ -0,0 +1,126 @@ +#ifdef GAME_MM +#define NO_GDI +#define WIN32_LEAN_AND_MEAN +#include "ZFile.h" +#include +#include "TextureAnimationExporter.h" +#include "DisplayListExporter.h" +#include + +#undef FindResource + +void OTRExporter_TextureAnimation::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + auto* anim = (ZTextureAnimation*)res; + WriteHeader(res, outPath, writer, static_cast(SOH::ResourceType::TSH_TexAnim), 0); + writer->Write((uint32_t)anim->entries.size()); + + for (const auto& e : anim->entries) { + auto* params = (ZTextureAnimationParams*)res->parent->FindResource(Seg2Filespace(e.paramsPtr, res->parent->baseAddress)); + writer->Write(e.segment); + writer->Write((int16_t)e.type); + switch ((TextureAnimationParamsType)e.type) { + case TextureAnimationParamsType::SingleScroll: + case TextureAnimationParamsType::DualScroll: { + auto* scrollParams = (TextureScrollingParams*)params; + + writer->Write(scrollParams->rows[0].xStep); + writer->Write(scrollParams->rows[0].yStep); + writer->Write(scrollParams->rows[0].width); + writer->Write(scrollParams->rows[0].height); + if (scrollParams->count == 2) { + writer->Write(scrollParams->rows[1].xStep); + writer->Write(scrollParams->rows[1].yStep); + writer->Write(scrollParams->rows[1].width); + writer->Write(scrollParams->rows[1].height); + } + break; + } + + case TextureAnimationParamsType::ColorChange: + case TextureAnimationParamsType::ColorChangeLERP: + case TextureAnimationParamsType::ColorChangeLagrange: { + auto* colorParams = (TextureColorChangingParams*)params; + writer->Write(colorParams->animLength); + writer->Write(colorParams->colorListCount); + + if (colorParams->frameDataListAddress != 0) { // NULL + writer->Write((uint16_t)colorParams->frameDataList.size()); + for (const auto f : colorParams->frameDataList) { + writer->Write(f); + } + } else { + writer->Write((uint16_t)0); + } + + if (colorParams->primColorListAddress != 0) { // NULL + writer->Write((uint16_t)colorParams->primColorList.size()); + for (const auto prim : colorParams->primColorList) { + writer->Write(prim.r); + writer->Write(prim.g); + writer->Write(prim.b); + writer->Write(prim.a); + writer->Write(prim.lodFrac); + } + } else { + writer->Write((uint16_t)0); + } + + if (colorParams->envColorListAddress != 0) { // NULL + writer->Write((uint16_t)colorParams->envColorList.size()); + for (const auto env : colorParams->envColorList) { + writer->Write(env.r); + writer->Write(env.g); + writer->Write(env.b); + writer->Write(env.a); + } + } else { + writer->Write((uint16_t)0); + } + + break; + } + case TextureAnimationParamsType::TextureCycle: { + auto* cycleParams = (TextureCyclingParams*)params; + + writer->Write(cycleParams->cycleLength); + // Texture list may or may not be the same size as the index list, so we need to write the size of the list for the importer + writer->Write((uint32_t)cycleParams->textureList.size()); + + for (const auto t : cycleParams->textureList) { + std::string name; + bool found = Globals::Instance->GetSegmentedPtrName(GETSEGOFFSET(t), res->parent, "", name, res->parent->workerID); + + if (!found) { + ZTexture* tex = (ZTexture*)res->parent->FindResource(t & 0x00FFFFFF); + if (tex != nullptr) { + name = tex->GetName(); + found = true; + } + } + if (found) + { + if (name.at(0) == '&') + name.erase(0, 1); + + writer->Write(OTRExporter_DisplayList::GetPathToRes(res, name)); + } + else + { + spdlog::error("Texture not found: 0x{:X}", t); + writer->Write(""); + } + } + for (const auto index : cycleParams->textureIndexList) { + writer->Write(index); + } + break; + } + case TextureAnimationParamsType::Empty: { + writer->Write(SEGMENTED_NULL); + break; + } + } + } +} +#endif diff --git a/OTRExporter/OTRExporter/TextureAnimationExporter.h b/OTRExporter/OTRExporter/TextureAnimationExporter.h new file mode 100644 index 000000000..c84da05ba --- /dev/null +++ b/OTRExporter/OTRExporter/TextureAnimationExporter.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef GAME_MM + +#include "ZResource.h" +#include "ZTextureAnimation.h" +#include "Exporter.h" +#include + +class OTRExporter_TextureAnimation : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; +#endif diff --git a/OTRExporter/OTRExporter/TextureExporter.cpp b/OTRExporter/OTRExporter/TextureExporter.cpp new file mode 100644 index 000000000..4f854cea5 --- /dev/null +++ b/OTRExporter/OTRExporter/TextureExporter.cpp @@ -0,0 +1,32 @@ +#include "TextureExporter.h" +#include "../ZAPD/ZFile.h" + +void OTRExporter_Texture::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZTexture* tex = (ZTexture*)res; + + WriteHeader(tex, outPath, writer, static_cast(Fast::ResourceType::Texture)); + + auto start = std::chrono::steady_clock::now(); + + //printf("Exporting Texture %s\n", tex->GetName().c_str()); + + writer->Write((uint32_t)tex->GetTextureType()); + writer->Write((uint32_t)tex->GetWidth()); + writer->Write((uint32_t)tex->GetHeight()); + + writer->Write((uint32_t)tex->GetRawDataSize()); + + if (tex->parent != nullptr) { + auto data = tex->parent->GetRawData(); + writer->Write((char*)data.data() + tex->GetRawDataIndex(), tex->GetRawDataSize()); + } + + auto end = std::chrono::steady_clock::now(); + size_t diff = std::chrono::duration_cast(end - start).count(); + + //printf("Exported Texture %s in %zums\n", tex->GetName().c_str(), diff); + + //if (diff > 2) + //printf("Export took %lms\n", diff); +} \ No newline at end of file diff --git a/OTRExporter/OTRExporter/TextureExporter.h b/OTRExporter/OTRExporter/TextureExporter.h new file mode 100644 index 000000000..cdf7491a8 --- /dev/null +++ b/OTRExporter/OTRExporter/TextureExporter.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ZResource.h" +#include "ZTexture.h" +#include "Exporter.h" +#include + +class OTRExporter_Texture : public OTRExporter +{ +public: + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/VersionInfo.cpp b/OTRExporter/OTRExporter/VersionInfo.cpp new file mode 100644 index 000000000..39bb70ea7 --- /dev/null +++ b/OTRExporter/OTRExporter/VersionInfo.cpp @@ -0,0 +1,29 @@ +#include "VersionInfo.h" +#include +#ifdef GAME_MM +#include "../../mm/2s2h/resource/type/2shResourceType.h" +#elif GAME_OOT +#include "../../soh/soh/resource/type/SohResourceType.h" +#endif +std::map resourceVersions; + +void InitVersionInfo() +{ + resourceVersions = std::map { + { static_cast(SOH::ResourceType::SOH_Animation), 0 }, + { static_cast(Fast::ResourceType::Texture), 0 }, + { static_cast(SOH::ResourceType::SOH_PlayerAnimation), 0 }, + { static_cast(Fast::ResourceType::DisplayList), 0 }, + { static_cast(SOH::ResourceType::SOH_Room), 0 }, + { static_cast(SOH::ResourceType::SOH_CollisionHeader), 0 }, + { static_cast(SOH::ResourceType::SOH_Skeleton), 0 }, + { static_cast(SOH::ResourceType::SOH_SkeletonLimb), 0 }, + { static_cast(Fast::ResourceType::Matrix), 0 }, + { static_cast(SOH::ResourceType::SOH_Path), 0 }, + { static_cast(Fast::ResourceType::Vertex), 0 }, + { static_cast(SOH::ResourceType::SOH_Cutscene), 0 }, + { static_cast(SOH::ResourceType::SOH_Array), 0 }, + { static_cast(SOH::ResourceType::SOH_Text), 0 }, + { static_cast(Ship::ResourceType::Blob), 0 }, + }; +} diff --git a/OTRExporter/OTRExporter/VersionInfo.h b/OTRExporter/OTRExporter/VersionInfo.h new file mode 100644 index 000000000..51515d481 --- /dev/null +++ b/OTRExporter/OTRExporter/VersionInfo.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include +#include + +extern std::map resourceVersions; diff --git a/OTRExporter/OTRExporter/VtxExporter.cpp b/OTRExporter/OTRExporter/VtxExporter.cpp new file mode 100644 index 000000000..cb1b178f1 --- /dev/null +++ b/OTRExporter/OTRExporter/VtxExporter.cpp @@ -0,0 +1,44 @@ +#include "VtxExporter.h" +#include +#include "VersionInfo.h" + + +void OTRExporter_Vtx::SaveArr(ZResource* res, const fs::path& outPath, const std::vector& vec, BinaryWriter* writer) +{ + WriteHeader(res, outPath, writer, static_cast(Fast::ResourceType::Vertex)); + + for (auto& res: vec) { + ZVtx* vtx = (ZVtx*)res; + writer->Write(vtx->x); + writer->Write(vtx->y); + writer->Write(vtx->z); + writer->Write(vtx->flag); + writer->Write(vtx->s); + writer->Write(vtx->t); + writer->Write(vtx->r); + writer->Write(vtx->g); + writer->Write(vtx->b); + writer->Write(vtx->a); + } + +} + +void OTRExporter_Vtx::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZVtx* vtx = (ZVtx*)res; + + WriteHeader(res, outPath, writer, static_cast(Fast::ResourceType::Vertex)); + + writer->Write((uint32_t)1); //Yes I'm hard coding it to one, it *should* be fine. + + writer->Write(vtx->x); + writer->Write(vtx->y); + writer->Write(vtx->z); + writer->Write(vtx->flag); + writer->Write(vtx->s); + writer->Write(vtx->t); + writer->Write(vtx->r); + writer->Write(vtx->g); + writer->Write(vtx->b); + writer->Write(vtx->a); +} diff --git a/OTRExporter/OTRExporter/VtxExporter.h b/OTRExporter/OTRExporter/VtxExporter.h new file mode 100644 index 000000000..fe67087a9 --- /dev/null +++ b/OTRExporter/OTRExporter/VtxExporter.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ZResource.h" +#include "ZVtx.h" +#include "Exporter.h" +#include + +class OTRExporter_Vtx : public OTRExporter +{ +public: + void SaveArr(ZResource* res, const fs::path& outPath, const std::vector&, BinaryWriter* writer); + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/command_macros_base.h b/OTRExporter/OTRExporter/command_macros_base.h new file mode 100644 index 000000000..1506c781d --- /dev/null +++ b/OTRExporter/OTRExporter/command_macros_base.h @@ -0,0 +1,32 @@ +#ifndef COMMAND_MACROS_BASE_H +#define COMMAND_MACROS_BASE_H + +/** + * Command Base macros intended for use in designing of more specific command macros + * Each macro packs bytes (B), halfwords (H) and words (W, for consistency) into a single word + */ + +#define _SHIFTL(v, s, w) \ + ((unsigned int) (((unsigned int)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((unsigned int)(((unsigned int)(v) >> (s)) & ((0x01 << (w)) - 1))) + +//#define CMD_BBBB(a, b, c, d) (_SHIFTL(a, 24, 8) | _SHIFTL(b, 16, 8) | _SHIFTL(c, 8, 8) | _SHIFTL(d, 0, 8)) +#define CMD_BBBB(a, b, c, d) (_SHIFTL(d, 24, 8) | _SHIFTL(c, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)) + +//#define CMD_BBH(a, b, c) (_SHIFTL(a, 24, 8) | _SHIFTL(b, 16, 8) | _SHIFTL(c, 0, 16)) +#define CMD_BBH(a, b, c) (_SHIFTL(a, 0, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(c, 16, 16)) + +//#define CMD_HBB(a, b, c) (_SHIFTL(a, 16, 16) | _SHIFTL(b, 8, 8) | _SHIFTL(c, 0, 8)) +#define CMD_HBB(a, b, c) (_SHIFTL(c, 24, 8) | _SHIFTL(b, 16, 8) | _SHIFTL(a, 0, 16)) + +//#define CMD_HH(a, b) (_SHIFTL(a, 16, 16) | _SHIFTL(b, 0, 16)) +#define CMD_HH(a, b) (_SHIFTL(b, 16, 16) | _SHIFTL(a, 0, 16)) + +#define CMD_W(a) (a) + +#define CMD_F(a) {(a)} + +#define CMD_PTR(a) (u32)(a) + +#endif \ No newline at end of file diff --git a/OTRExporter/OTRExporter/z64cutscene.h b/OTRExporter/OTRExporter/z64cutscene.h new file mode 100644 index 000000000..d5eb89482 --- /dev/null +++ b/OTRExporter/OTRExporter/z64cutscene.h @@ -0,0 +1,290 @@ +#ifndef Z64CUTSCENE_H +#define Z64CUTSCENE_H + +#if 0 +#include + +typedef struct { + /* 0x00 */ u16 entrance; // entrance index upon which the cutscene should trigger + /* 0x02 */ u8 ageRestriction; // 0 for adult only, 1 for child only, 2 for both ages + /* 0x03 */ u8 flag; // eventChkInf flag bound to the entrance cutscene + /* 0x04 */ void* segAddr; // segment offset location of the cutscene +} EntranceCutscene; // size = 0x8 + +typedef struct { + /* 0x00 */ s8 continueFlag; + /* 0x01 */ s8 cameraRoll; + /* 0x02 */ u16 nextPointFrame; + /* 0x04 */ f32 viewAngle; // in degrees + /* 0x08 */ Vec3s pos; +} CutsceneCameraPoint; // size = 0x10 + +typedef struct { + /* 0x00 */ Vec3f at; + /* 0x0C */ Vec3f eye; + /* 0x18 */ s16 roll; + /* 0x1A */ s16 fov; +} CutsceneCameraAngle; // size = 0x1C + +typedef struct { + /* 0x0 */ CutsceneCameraPoint* atPoints; + /* 0x4 */ CutsceneCameraPoint* eyePoints; + /* 0x8 */ s16 relativeToPlayer; +} CutsceneCameraMove; // size = 0xC + +typedef struct { + /* 0x00 */ u16 base; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdBase; // size = 0x6 + +typedef struct { + /* 0x00 */ u8 unk_00; + /* 0x01 */ u8 setting; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdEnvLighting; // size = 0x6 + +typedef struct { + /* 0x00 */ u8 unk_00; + /* 0x01 */ u8 sequence; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdMusicChange; // size = 0x6 + +typedef struct { + /* 0x00 */ u16 type; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdMusicFade; // size = 0x6 + +typedef struct { + /* 0x00 */ u16 unk_00; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + /* 0x06 */ u8 unk_06; + /* 0x07 */ u8 unk_07; + /* 0x08 */ u8 unk_08; +} CsCmdUnknown9; // size = 0xA + +typedef struct { + /* 0x00 */ u16 unk_00; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + /* 0x06 */ u8 hour; + /* 0x07 */ u8 minute; +} CsCmdDayTime; // size = 0x8 + +typedef struct { + /* 0x00 */ u16 base; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + /* 0x06 */ u16 type; + /* 0x08 */ u16 textId1; + /* 0x0A */ u16 textId2; +} CsCmdTextbox; // size = 0xC + +typedef struct { + /* 0x00 */ u16 action; // "dousa" + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + union { + /* 0x06 */ Vec3s rot; + /* 0x06 */ Vec3us urot; + }; + /* 0x0C */ Vec3i startPos; + /* 0x18 */ Vec3i endPos; + /* 0x24 */ Vec3i normal; +} CsCmdActorAction; // size = 0x30 +#endif + +typedef enum { + CS_STATE_IDLE, + CS_STATE_SKIPPABLE_INIT, + CS_STATE_SKIPPABLE_EXEC, + CS_STATE_UNSKIPPABLE_INIT, + CS_STATE_UNSKIPPABLE_EXEC +} CutsceneState; + +typedef enum { + CS_CMD_00 = 0x0000, + CS_CMD_CAM_EYE = 0x0001, + CS_CMD_CAM_AT = 0x0002, + CS_CMD_MISC = 0x0003, + CS_CMD_SET_LIGHTING = 0x0004, + CS_CMD_CAM_EYE_REL_TO_PLAYER = 0x0005, + CS_CMD_CAM_AT_REL_TO_PLAYER = 0x0006, + CS_CMD_07 = 0x0007, + CS_CMD_08 = 0x0008, + CS_CMD_09 = 0x0009, + CS_CMD_TEXTBOX = 0x0013, + CS_CMD_SET_PLAYER_ACTION = 0x000A, + CS_CMD_SET_ACTOR_ACTION_1 = 0x000F, + CS_CMD_SET_ACTOR_ACTION_2 = 0x000E, + CS_CMD_SET_ACTOR_ACTION_3 = 0x0019, + CS_CMD_SET_ACTOR_ACTION_4 = 0x001D, + CS_CMD_SET_ACTOR_ACTION_5 = 0x001E, + CS_CMD_SET_ACTOR_ACTION_6 = 0x002C, + CS_CMD_SET_ACTOR_ACTION_7 = 0x001F, + CS_CMD_SET_ACTOR_ACTION_8 = 0x0031, + CS_CMD_SET_ACTOR_ACTION_9 = 0x003E, + CS_CMD_SET_ACTOR_ACTION_10 = 0x008F, + CS_CMD_SCENE_TRANS_FX = 0x002D, + CS_CMD_NOP = 0x000B, + CS_CMD_PLAYBGM = 0x0056, + CS_CMD_STOPBGM = 0x0057, + CS_CMD_FADEBGM = 0x007C, + CS_CMD_SETTIME = 0x008C, + CS_CMD_TERMINATOR = 0x03E8, + CS_CMD_END = 0xFFFF +} CutsceneCmd; + +/** + * Special type for blocks of cutscene data, asm-processor checks + * arrays for CutsceneData type and converts floats within the array + * to their IEEE-754 representation. The array must close with }; + * on its own line. + * + * Files that contain this type that are included in other C files + * must include an 'EARLY' qualifier to inform asm-processor that it + * must recursively process that include. + * + * Example: #include "file.c" EARLY + */ + + +typedef union CutsceneData { + int i; + float f; + short s[2]; + char b[4]; +} CutsceneData; + +#define CS_CMD_CONTINUE 0 +#define CS_CMD_STOP -1 + +// TODO confirm correctness, clarify names +typedef enum { + /* 0x00 */ INVALID_DESTINATION_0, + /* 0x01 */ CUTSCENE_MAP_GANON_HORSE, + /* 0x02 */ CUTSCENE_MAP_THREE_GODESSES_POST_DEKU_TREE, + /* 0x03 */ GERUDO_VALLEY_DIN, + /* 0x04 */ DEATH_MOUNTAIN_TRAIL_NAYRU, + /* 0x05 */ KOKIRI_FOREST_FARORE, + /* 0x06 */ CUTSCENE_MAP_TRIFORCE_CREATION, + /* 0x07 */ KOKIRI_FOREST_RECEIVE_KOKIRI_EMERALD, + /* 0x08 */ TEMPLE_OF_TIME_AFTER_USE_MS, + /* 0x09 */ GERUDO_VALLEY_DIN_2, + /* 0x0A */ LINKS_HOUSE_INTRO, + /* 0x0B */ KOKIRI_FOREST_INTRO, + /* 0x0C */ DEATH_MOUNTAIN_TRAIL_AFTER_GORON_RUBY, + /* 0x0D */ ZORAS_FOUNTAIN_AFTER_ZORAS_SAPPHIRE, + /* 0x0E */ KOKIRI_FOREST_AFTER_KOKIRI_EMERALD, + /* 0x0F */ TEMPLE_OF_TIME_KOKIRI_EMERALD, //unused + /* 0x10 */ TEMPLE_OF_TIME_GORON_RUBY, //unused + /* 0x11 */ TEMPLE_OF_TIME_ZORAS_SAPPHIRE, //unused + /* 0x12 */ TEMPLE_OF_TIME_AFTER_USE_MS_FIRST, + /* 0x13 */ DEATH_MOUNTAIN_TRAIL_AFTER_INTRO, + /* 0x14 */ INVALID_DESTINATION_14, + /* 0x15 */ LAKE_HYLIA_WATER_RISES, + /* 0x16 */ DESERT_COLOSSUS_REQUIEM, + /* 0x17 */ CUTSCENE_MAP_CURSE_YOU, + /* 0x18 */ JABU_JABU_INTRO, + /* 0x19 */ CHAMBER_OF_SAGES_LIGHT_MEDALLION, + /* 0x1A */ TEMPLE_OF_TIME_KOKIRI_EMERALD_2, //duplicate of 0x000F + /* 0x1B */ TEMPLE_OF_TIME_GORON_RUBY_2, //duplicate of 0x0010 + /* 0x1C */ TEMPLE_OF_TIME_ZORAS_SAPPHIRE_2, //duplicate of 0x0011 + /* 0x1D */ CHAMBER_OF_SAGES_FOREST_MEDALLION, + /* 0x1E */ CHAMBER_OF_SAGES_FIRE_MEDALLION, + /* 0x1F */ CHAMBER_OF_SAGES_WATER_MEDALLION, + /* 0x20 */ HYRULE_FIELD_FLASHBACK, //lacs part 4 + /* 0x21 */ HYRULE_FIELD_AFTER_LAKE_HYLIA_OWL, + /* 0x22 */ CUTSCENE_MAP_GANON_AFTER_USE_MS, + /* 0x23 */ HYRULE_FIELD_INTRO_ZELDA_ESCAPE, + /* 0x24 */ INVALID_DESTINATION_24, + /* 0x25 */ INVALID_DESTINATION_25, + /* 0x26 */ CUTSCENE_MAP_SHEIKAH_LEGEND, //lacs part 2 + /* 0x27 */ TEMPLE_OF_TIME_ZELDA_REVEAL, //lacs part 3 + /* 0x28 */ TEMPLE_OF_TIME_GET_LIGHT_ARROWS, //lacs part 5 + /* 0x29 */ LAKE_HYLIA_AFTER_BLUE_WARP, + /* 0x2A */ KAKARIKO_VILLAGE_DRAIN_WELL, + /* 0x2B */ WINDMILL_AFTER_DRAIN_WELL, + /* 0x2C */ TEMPLE_OF_TIME_AFTER_DOOR_OF_TIME_OPENS, + /* 0x2D */ INVALID_DESTINATION_2D, + /* 0x2E */ TEMPLE_OF_TIME_AFTER_USE_MS_FIRST_2, // duplicate of 0x0012 + /* 0x2F */ KAKARIKO_VILLAGE_NOCTURNE_PART_2, + /* 0x30 */ DESERT_COLOSSUS_AFTER_REQUIEM, + /* 0x31 */ TEMPLE_OF_TIME_AFTER_LIGHT_ARROWS, + /* 0x32 */ KAKARIKO_VILLAGE_AFTER_NOCTURNE, + /* 0x33 */ HYRULE_FIELD_IMPA_ESCORT_CS, + /* 0x34 */ TEMPLE_OF_TIME_SONG_OF_TIME, + /* 0x35 */ HYRULE_FIELD_AFTER_SONG_OF_TIME, + /* 0x36 */ GERUDO_VALLEY_CREDITS, + /* 0x37 */ GERUDO_FORTRESS_CREDITS, + /* 0x38 */ KAKARIKO_VILLAGE_CREDITS, + /* 0x39 */ DEATH_MOUNTAIN_TRAIL_CREDITS_1, + /* 0x3A */ GORON_CITY_CREDITS, // unused? + /* 0x3B */ LAKE_HYLIA_CREDITS, + /* 0x3C */ ZORAS_FOUNTAIN_CREDITS, // unused + /* 0x3D */ ZORAS_DOMAIN_CREDITS, + /* 0x3E */ KOKIRI_FOREST_CREDITS_1, + /* 0x3F */ KOKIRI_FOREST_CREDITS_2, + /* 0x40 */ HYRULE_FIELD_CREDITS, + /* 0x41 */ LON_LON_RANCH_CREDITS_1, + /* 0x42 */ KAKARIKO_VILLAGE_AFTER_TRAIL_OWL, + /* 0x43 */ HTRULE_FIELD_UNUSED_ENTRANCE, + /* 0x44 */ CUTSCENE_MAP_FIRE, + /* 0x45 */ KOKIRI_FOREST_POST_FOREST_MEDALLION, + /* 0x46 */ DEATH_MOUNTAIN_TRAIL_CREDITS_2, + /* 0x47 */ TEMPLE_OF_TIME_CREDITS, + /* 0x48 */ ZELDAS_COURTYARD_CREDITS, + /* 0x49 */ LON_LON_RANCH_CREDITS_1_2, // duplicate of 0x0041 + /* 0x4A */ LON_LON_RANCH_CREDITS_2, + /* 0x4B */ LON_LON_RANCH_CREDITS_3, + /* 0x4C */ LON_LON_RANCH_CREDITS_4, + /* 0x4D */ LON_LON_RANCH_CREDITS_5, + /* 0x4E */ LON_LON_RANCH_CREDITS_6, + /* 0x4F */ LON_LON_RANCH_NO_CS_1, + /* 0x50 */ LON_LON_RANCH_NO_CS_2, + /* 0x51 */ LON_LON_RANCH_NO_CS_3, + /* 0x52 */ LON_LON_RANCH_NO_CS_4, + /* 0x53 */ LON_LON_RANCH_NO_CS_5, + /* 0x54 */ LON_LON_RANCH_NO_CS_6, + /* 0x55 */ LON_LON_RANCH_NO_CS_7, + /* 0x56 */ LON_LON_RANCH_NO_CS_8, + /* 0x57 */ LON_LON_RANCH_NO_CS_9, + /* 0x58 */ LON_LON_RANCH_NO_CS_10, + /* 0x59 */ LON_LON_RANCH_NO_CS_11, + /* 0x5A */ LON_LON_RANCH_NO_CS_12, + /* 0x5B */ LON_LON_RANCH_NO_CS_13, + /* 0x5C */ LON_LON_RANCH_NO_CS_14, + /* 0x5D */ LON_LON_RANCH_NO_CS_15, + /* 0x5E */ LON_LON_RANCH_NO_CS_EPONAS_SONG, + /* 0x5F */ CONDITIONAL_DESTINATION, // TODO more descriptive name? + /* 0x60 */ DESERT_COLOSSUS_SPIRIT_BLUE_WARP, + /* 0x61 */ GRAVEYARD_AFTER_SHADOW_BLUE_WARP, + /* 0x62 */ DEATH_MOUNTAIN_CRATER_AFTER_FIRE_BLUE_WARP, + /* 0x63 */ SACRED_FOREST_MEADOW_AFTER_FOREST_BLUE_WARP, + /* 0x64 */ KOKIRI_FOREST_AFTER_FOREST_BLUE_WARP, + /* 0x65 */ DESERT_COLOSSUS_AFTER_SILVER_GAUNTLETS, + /* 0x66 */ TEMPLE_OF_TIME_FRONT_OF_PEDESTAL, + /* 0x67 */ HYRULE_FIELD_TITLE_SCREEN, + /* 0x68 */ SPIRIT_TEMPLE_BOSS_TITLE_SCREEN, + /* 0x69 */ GRAVEYARD_SUNS_SONG, + /* 0x6A */ ROYAL_FAMILYS_TOMB_SUNS_SONG, + /* 0x6B */ GANONS_CASTLE_AFTER_FOREST_TRIAL, + /* 0x6C */ GANONS_CASTLE_AFTER_WATER_TRIAL, + /* 0x6D */ GANONS_CASTLE_AFTER_SHADOW_TRIAL, + /* 0x6E */ GANONS_CASTLE_AFTER_FIRE_TRIAL, + /* 0x6F */ GANONS_CASTLE_AFTER_LIGHT_TRIAL, + /* 0x70 */ GANONS_CASTLE_AFTER_SPIRIT_TRIAL, + /* 0x71 */ GANONS_CASTLE_DISPEL_BARRIER_IF_CONDITIONS, + /* 0x72 */ HYRULE_FIELD_INTRO, + /* 0x73 */ HYRULE_FIELD_AFTER_IMPA_ESCORT, + /* 0x74 */ DESERT_COLOSSUS_SPIRIT_BLUE_WARP_2, + /* 0x75 */ HYRULE_FIELD_SKY, + /* 0x76 */ GANON_BATTLE_TOWER_COLLAPSE, + /* 0x77 */ ZELDAS_COURTYARD_RECEIVE_LETTER +} CutsceneTerminatorDestination; + +#endif \ No newline at end of file diff --git a/OTRExporter/OTRExporter/z64cutscene_commands.h b/OTRExporter/OTRExporter/z64cutscene_commands.h new file mode 100644 index 000000000..5d440cce9 --- /dev/null +++ b/OTRExporter/OTRExporter/z64cutscene_commands.h @@ -0,0 +1,448 @@ +#ifndef Z64CUTSCENE_COMMANDS_H +#define Z64CUTSCENE_COMMANDS_H + +#include "command_macros_base.h" +#include "z64cutscene.h" + +/** + * ARGS + * s32 totalEntries (e), s32 endFrame (n) + * FORMAT + * eeeeeeee nnnnnnnn + * size = 0x8 + */ +#define CS_BEGIN_CUTSCENE(totalEntries, endFrame) CMD_W(totalEntries), CMD_W(endFrame) + + /** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * 00000001 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_POS_LIST CS_CAM_EYE_LIST +#define CS_CAM_EYE_LIST(startFrame, endFrame) \ + CS_CMD_CAM_EYE, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) + + /** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_POS CS_CAM_EYE +#define CS_CAM_EYE(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + + /** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * 00000002 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_FOCUS_POINT_LIST CS_CAM_AT_LIST +#define CS_CAM_AT_LIST(startFrame, endFrame) \ + CS_CMD_CAM_AT, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) + + /** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_FOCUS_POINT CS_CAM_AT +#define CS_CAM_AT(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 00000003 eeeeeeee + * size = 0x8 + */ +#define CS_MISC_LIST(entries) CS_CMD_MISC, CMD_W(entries) + + /** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * uuuussss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU + * size = 0x30 + */ +#define CS_MISC(unk, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7, unused8, unused9, unused10) \ + CMD_HH(unk, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), CMD_W(unused8), CMD_W(unused9), CMD_W(unused10) + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 00000004 eeeeeeee + * size = 0x8 + */ +#define CS_LIGHTING_LIST(entries) CS_CMD_SET_LIGHTING, CMD_W(entries) + + /** + * ARGS + * s16 setting (m), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * mmmmssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_LIGHTING(setting, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(setting, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + + /** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , may be consistently zero + * 00000005 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_POS_PLAYER_LIST CS_CAM_EYE_REL_TO_PLAYER_LIST +#define CS_CAM_EYE_REL_TO_PLAYER_LIST(startFrame, endFrame) \ + CS_CMD_CAM_EYE_REL_TO_PLAYER, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) + + /** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_POS_PLAYER CS_CAM_EYE_REL_TO_PLAYER +#define CS_CAM_EYE_REL_TO_PLAYER(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + + /** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , may be consistently zero + * 00000006 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_FOCUS_POINT_PLAYER_LIST CS_CAM_AT_REL_TO_PLAYER_LIST +#define CS_CAM_AT_REL_TO_PLAYER_LIST(startFrame, endFrame) \ + CS_CMD_CAM_AT_REL_TO_PLAYER, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) + /** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_FOCUS_POINT_PLAYER CS_CAM_AT_REL_TO_PLAYER +#define CS_CAM_AT_REL_TO_PLAYER(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + + /** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * 00000007 uuuussss eeeeUUUU + * size = 0xC + */ +#define CS_CMD_07_LIST(unk, startFrame, endFrame, unused) \ + CS_CMD_07, CMD_HH(unk, startFrame), CMD_HH(endFrame, unused) + + /** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CMD_07(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + + /** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * 00000008 uuuussss eeeeUUUU + * size = 0xC + */ +#define CS_CMD_08_LIST(unk, startFrame, endFrame, unused) \ + CS_CMD_08, CMD_HH(unk, startFrame), CMD_HH(endFrame, unused) + + /** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CMD_08(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 00000009 eeeeeeee + * size = 0x8 + */ +#define CS_CMD_09_LIST(entries) CS_CMD_09, CMD_W(entries) + + /** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e), s16 unk2 (v), s16 unk3 (w), s16 unk4 (x) + * FORMAT + * Capital U is Unused + * uuuussss eeeevvww xxUUUUUU + * size = 0xC + */ +#define CS_CMD_09(unk, startFrame, endFrame, unk2, unk3, unk4, unused0, unused1) \ + CMD_HH(unk, startFrame), CMD_HBB(endFrame, unk2, unk3), CMD_BBH(unk4, unused0, unused1) + + /** + * ARGS + * s32 cmdType (c), s32 entries (e) + * FORMAT + * cccccccc eeeeeeee + * size = 0x8 + */ +#define CS_UNK_DATA_LIST(cmdType, entries) CMD_W(cmdType), CMD_W(entries) + + /** + * ARGS + * s32 unk1 (a), s32 unk2 (b), s32 unk3 (c), s32 unk4 (d), s32 unk5 (e), s32 unk6 (f), + * s32 unk7 (g), s32 unk8 (h), s32 unk9 (i), s32 unk10 (j), s32 unk11 (k), s32 unk12 (l) + * FORMAT + * aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh iiiiiiii jjjjjjjj kkkkkkkk llllllll + * size = 0x30 + */ +#define CS_UNK_DATA(unk1, unk2, unk3, unk4, unk5, unk6, unk7, unk8, unk9, unk10, unk11, unk12) \ + CMD_W(unk1), CMD_W(unk2), CMD_W(unk3), CMD_W(unk4), CMD_W(unk5), CMD_W(unk6), \ + CMD_W(unk7), CMD_W(unk8), CMD_W(unk9), CMD_W(unk10), CMD_W(unk11), CMD_W(unk12) + + /** + * ARGS + * s32 cmdType (c), s32 entries (e) + * FORMAT + * cccccccc eeeeeeee + * size = 0x8 + */ +#define CS_NPC_ACTION_LIST(cmdType, entries) CMD_W(cmdType), CMD_W(entries) + + /** + * ARGS + * s16 npcAction (a), s16 startFrame (s), s16 endFrame (e), + * s16 rotX (u), s16 rotY (v), s16 rotZ (w), + * s32 startX (i), s32 startY (j), s32 startZ (k), + * s32 endX (l), s32 endY (m), s32 endZ (n), + * f32 normX (x), f32 normY (y), f32 normZ (z), + * FORMAT + * aaaassss eeeeuuuu vvvvwwww iiiiiiii jjjjjjjj kkkkkkkk llllllll mmmmmmmm nnnnnnnn xxxxxxxx yyyyyyyy zzzzzzzz + * size = 0x30 + */ +#define CS_NPC_ACTION(npcAction, startFrame, endFrame, rotX, rotY, rotZ, startX, startY, startZ, endX, endY, endZ, normX, normY, normZ) \ + CMD_HH(npcAction, startFrame), CMD_HH(endFrame, rotX), CMD_HH(rotY, rotZ), \ + CMD_W(startX), CMD_W(startY), CMD_W(startZ), \ + CMD_W(endX), CMD_W(endY), CMD_W(endZ), \ + CMD_F(normX), CMD_F(normY), CMD_F(normZ) + + /** + * ARGS + * s32 cmdType (c), s32 entries (e) + * FORMAT + * cccccccc eeeeeeee + * size = 0x8 + */ +#define CS_PLAYER_ACTION_LIST(entries) CS_CMD_SET_PLAYER_ACTION, CMD_W(entries) + + /** + * ARGS + * s16 linkAction (a), s16 startFrame (s), s16 endFrame (e), + * s16 rotX (u), s16 rotY (v), s16 rotZ (w), + * s32 startX (i), s32 startY (j), s32 startZ (k), + * s32 endX (l), s32 endY (m), s32 endZ (n), + * f32 normX (x), f32 normY (y), f32 normZ (z), + * FORMAT + * aaaassss eeeeuuuu vvvvwwww iiiiiiii jjjjjjjj kkkkkkkk llllllll mmmmmmmm nnnnnnnn xxxxxxxx yyyyyyyy zzzzzzzz + * size = 0x30 + */ +#define CS_PLAYER_ACTION(linkAction, startFrame, endFrame, rotX, rotY, rotZ, startX, startY, startZ, endX, endY, endZ, normX, normY, normZ) \ + CS_NPC_ACTION(linkAction, startFrame, endFrame, rotX, rotY, rotZ, startX, startY, startZ, endX, endY, endZ, normX, normY, normZ) + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 00000013 eeeeeeee + * size = 0x8 + */ +#define CS_TEXT_LIST(entries) CS_CMD_TEXTBOX, CMD_W(entries) + + /** + * ARGS + * s16 messageId (i), s16 startFrame (s), s16 endFrame (e), s16 type (o), + * s16 topOptionBranch (y), s16 bottomOptionBranch (n) + * FORMAT + * iiiissss eeeeoooo yyyynnnn + * size = 0xC + */ +#define CS_TEXT_DISPLAY_TEXTBOX(messageId, startFrame, endFrame, type, topOptionBranch, bottomOptionBranch) \ + CMD_HH(messageId, startFrame), CMD_HH(endFrame, type), CMD_HH(topOptionBranch, bottomOptionBranch) + + /** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * FFFFssss eeeeFFFF FFFFFFFF + * size = 0xC + */ +#define CS_TEXT_NONE(startFrame, endFrame) \ + CS_TEXT_DISPLAY_TEXTBOX(0xFFFF, startFrame, endFrame, 0xFFFF, 0xFFFF, 0xFFFF) + + /** + * ARGS + * s16 ocarinaSongAction (o), s16 startFrame (s), s16 endFrame (e), s16 topOptionBranch (i) + * FORMAT + * oooossss eeee0002 iiiiFFFF + * size = 0xC + */ +#define CS_TEXT_LEARN_SONG(ocarinaSongAction, startFrame, endFrame, messageId) \ + CS_TEXT_DISPLAY_TEXTBOX(ocarinaSongAction, startFrame, endFrame, 0x0002, messageId, 0xFFFF) + + /** + * ARGS + * s16 transitionType (t), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , endFrame duplicate + * 0000002D 00000001 ttttssss eeeeUUUU + * size = 0x10 + */ +#define CS_SCENE_TRANS_FX(transitionType, startFrame, endFrame) \ + CS_CMD_SCENE_TRANS_FX, 0x00000001, CMD_HH(transitionType, startFrame), CMD_HH(endFrame, endFrame) + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 00000056 eeeeeeee + * size = 0x8 + */ +#define CS_PLAY_BGM_LIST(entries) CS_CMD_PLAYBGM, CMD_W(entries) + + /** + * ARGS + * s16 sequence (q), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * qqqqssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_PLAY_BGM(sequence, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(sequence, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 00000057 eeeeeeee + * size = 0x8 + */ +#define CS_STOP_BGM_LIST(entries) CS_CMD_STOPBGM, CMD_W(entries) + + /** + * ARGS + * s16 sequence (q), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * uuqqssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_STOP_BGM(sequence, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(sequence, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 0000007C eeeeeeee + * size = 0x8 + */ +#define CS_FADE_BGM_LIST(entries) CS_CMD_FADEBGM, CMD_W(entries) + + /** + * ARGS + * s16 fadeType (t), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * ttttssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_FADE_BGM(fadeType, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(fadeType, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + + /** + * ARGS + * s32 entries (e) + * FORMAT + * 0000008C eeeeeeee + * size = 0x8 + */ +#define CS_TIME_LIST(entries) CS_CMD_SETTIME, CMD_W(entries) + + /** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e), s8 hour (h), s8 min (m) + * FORMAT + * Capital U is Unused + * uuuussss eeeehhmm UUUUUUUU + * size = 0xC + */ +#define CS_TIME(unk, startFrame, endFrame, hour, min, unused) \ + CMD_HH(unk, startFrame), \ + CMD_HBB(endFrame, hour, min), \ + CMD_W(unused) + + /** + * ARGS + * CutsceneTerminatorDestination dest (d), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , endFrame duplicate + * 000003E8 00000001 ddddssss eeeeUUUU + * size = 0x10 + */ +#define CS_TERMINATOR(dest, startFrame, endFrame) \ + CS_CMD_TERMINATOR, 0x00000001, CMD_HH(dest, startFrame), CMD_HH(endFrame, endFrame) + + /** + * Marks the end of a cutscene + */ +#define CS_END() 0xFFFFFFFF, 0x00000000 + +#endif \ No newline at end of file diff --git a/OTRExporter/assets/accessibility/texts/filechoose_eng.json b/OTRExporter/assets/accessibility/texts/filechoose_eng.json new file mode 100644 index 000000000..b43719926 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/filechoose_eng.json @@ -0,0 +1,29 @@ +{ + "file1": "File 1", + "file2": "File 2", + "file3": "File 3", + "options": "Options", + "copy": "Copy", + "erase": "Erase", + "quit": "Quit", + "confirm": "Yes", + "end": "End", + "hyphen": "Hyphen", + "period": "Period", + "space": "Space", + "backspace": "Backspace", + "capital_letter": "Capital $0", + "audio_stereo": "Sound - Stereo", + "audio_mono": "Sound - Mono", + "audio_headset": "Sound - Headset", + "audio_surround": "Sound - Surround", + "target_switch": "Targeting Mode - Switch", + "target_hold": "Targeting Mode - Hold", + "language_english": "Language - English", + "language_german": "Language - German", + "language_french": "Language - French", + "quest_sel_vanilla": "Quest - Original", + "quest_sel_mq": "Quest - Master Quest", + "quest_sel_randomizer": "Quest - Randomizer", + "quest_sel_boss_rush": "Quest - Boss Rush" +} \ No newline at end of file diff --git a/OTRExporter/assets/accessibility/texts/filechoose_fra.json b/OTRExporter/assets/accessibility/texts/filechoose_fra.json new file mode 100644 index 000000000..f76861f21 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/filechoose_fra.json @@ -0,0 +1,29 @@ +{ + "file1": "Fichier 1", + "file2": "Fichier 2", + "file3": "Fichier 3", + "options": "Options", + "copy": "Copier", + "erase": "Effacer", + "quit": "Retour", + "confirm": "Oui", + "end": "Fin", + "hyphen": "Trait d'union", + "period": "Point", + "space": "Espace", + "backspace": "Retour arrière", + "capital_letter": "Majuscule $0", + "audio_stereo": "Son - Stéréo", + "audio_mono": "Son - Mono", + "audio_headset": "Son - Casque", + "audio_surround": "Son - Surround", + "target_switch": "Visée - Fixe", + "target_hold": "Visée - Maintenue", + "language_english": "Langue - Anglaise", + "language_german": "Langue - Allemande", + "language_french": "Langue - Français", + "quest_sel_vanilla": "Quête - Originale", + "quest_sel_mq": "Quête - Master Quest", + "quest_sel_randomizer": "Quête - Randomizer", + "quest_sel_boss_rush": "Quête - Boss Rush" +} \ No newline at end of file diff --git a/OTRExporter/assets/accessibility/texts/filechoose_ger.json b/OTRExporter/assets/accessibility/texts/filechoose_ger.json new file mode 100644 index 000000000..2ad1322fd --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/filechoose_ger.json @@ -0,0 +1,29 @@ +{ + "file1": "Datei 1", + "file2": "Datei 2", + "file3": "Datei 3", + "options": "Optionen", + "copy": "Kopieren", + "erase": "Löschen", + "quit": "Zurück", + "confirm": "Ja", + "end": "Ende", + "hyphen": "Bindestrich", + "period": "Punkt", + "space": "Raum", + "backspace": "Rücktaste", + "capital_letter": "Großbuchstabe $0", + "audio_stereo": "Sound - Stereo", + "audio_mono": "Sound - Mono", + "audio_headset": "Sound - Kopfhörer", + "audio_surround": "Sound - Surround", + "target_switch": "Zielerfassung - Einmal drücken", + "target_hold": "Zielerfassung - Trigger halten", + "language_english": "Sprache - Englisch", + "language_german": "Sprache - Deutsch", + "language_french": "Sprache - Französisch", + "quest_sel_vanilla": "Quest - Original", + "quest_sel_mq": "Quest - Master Quest", + "quest_sel_randomizer": "Quest - Randomizer", + "quest_sel_boss_rush": "Quest - Bosse Rush" +} \ No newline at end of file diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json new file mode 100644 index 000000000..10b016b32 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_eng.json @@ -0,0 +1,231 @@ +{ + "health": "Health - $0 Hearts", + "magic": "Magic - $0", + "rupees": "Rupees - $0", + "floor": "Floor $0", + "basement": "Basement $0", + "item_menu": "Select Item", + "map_menu": "Map - $0", + "quest_menu": "Quest Status", + "equip_menu": "Equipment", + "overworld": "Overworld", + "equipped": "$0 - Equipped", + "save_prompt": "Would you like to save?", + "game_saved": "Game saved", + "assigned_to": "Assigned to $0", + "0": "Deku Stick - $0", + "1": "Deku Nut - $0", + "2": "Bomb - $0", + "3": "Fairy Bow - $0", + "4": "Fire Arrow", + "5": "Din's Fire", + "6": "Fairy Slingshot - $0", + "7": "Fairy Ocarina", + "8": "Ocarina of Time", + "9": "Bombchu - $0", + "10": "Hookshot", + "11": "Longshot", + "12": "Ice Arrow", + "13": "Farore's Wind", + "14": "Boomerang", + "15": "Lens of Truth", + "16": "Magic Beans - $0", + "17": "Megaton Hammer", + "18": "Light Arrow", + "19": "Nayru's Love", + "20": "Empty Bottle", + "21": "Red Potion", + "22": "Green Potion", + "23": "Blue Potion", + "24": "Fairy", + "25": "Fish", + "26": "Milk Bottle", + "27": "Ruto's Letter", + "28": "Blue Fire", + "29": "Bugs", + "30": "Big Poe", + "31": "Milk Bottle (Half)", + "32": "Poe", + "33": "Weird Egg", + "34": "Chicken", + "35": "Zelda's Letter", + "36": "Keaton Mask", + "37": "Skull Mask", + "38": "Spooky Mask", + "39": "Bunny Mask", + "40": "Goron Mask", + "41": "Zora Mask", + "42": "Gerudo Mask", + "43": "Mask of Truth", + "44": "Sold Out", + "45": "Pocket Egg", + "46": "Pocket Cucco", + "47": "Cojiro", + "48": "Odd Mushroom", + "49": "Odd Potion", + "50": "Saw", + "51": "Broken Sword", + "52": "Prescription", + "53": "Eyeball Frog", + "54": "Eyedrops", + "55": "Claim Check", + "56": "Bow Fire Arrow", + "57": "Bow Ice Arrow", + "58": "Bow Light Arrow", + "59": "Kokiri Sword", + "60": "Master Sword", + "61": "Giant's Knife", + "62": "Deku Shield", + "63": "Hylian Shield", + "64": "Mirror Shield", + "65": "Kokiri Tunic", + "66": "Goron Tunic", + "67": "Zora Tunic", + "68": "Kokiri Boots", + "69": "Iron Boots", + "70": "Hover Boots", + "71": "Bullet Bag (Holds 30)", + "72": "Bullet Bag (Holds 40)", + "73": "Bullet Bag (Holds 50)", + "74": "Quiver (Holds 30)", + "75": "Quiver (Holds 40)", + "76": "Quiver (Holds 50)", + "77": "Bomb Bag (Holds 20)", + "78": "Bomb Bag (Holds 30)", + "79": "Bomb Bag (Holds 40)", + "80": "Goron's Bracelet", + "81": "Silver Gauntlets", + "82": "Golden Gauntlets", + "83": "Silver Scale", + "84": "Golden Scale", + "85": "Giant's Knife (Broken)", + "86": "WALLET ADULT", + "87": "Giant's Wallet", + "88": "Deku Seeds", + "89": "Fishing Pole", + "90": "Minuet of Forest", + "91": "Bolero of Fire", + "92": "Serenade of Water", + "93": "Requiem of Spirit", + "94": "Nocturne of Shadow", + "95": "Prelude of Light", + "96": "Zelda's Lullaby", + "97": "Epona's Song", + "98": "Saria's Song", + "99": "Sun's Song", + "100": "Song of Time", + "101": "Song of Storms", + "102": "Forest Medallion", + "103": "Fire Medallion", + "104": "Water Medallion", + "105": "Spirit Medallion", + "106": "Shadow Medallion", + "107": "Light Medallion", + "108": "Kokiri's Emerald", + "109": "Goron's Ruby", + "110": "Zora Sapphire", + "111": "Stone of Agony", + "112": "Gerudo's Card", + "113": "Skulltula Token - $0", + "114": "Piece of Heart - $0", + "115": "Piece of Heart", + "116": "Boss Key", + "117": "Compass", + "118": "Dungeon Map", + "119": "Small Key", + "120": "MAGIC SMALL", + "121": "MAGIC LARGE", + "122": "Biggoron's Sword", + "123": "INVALID 1", + "124": "INVALID 2", + "125": "INVALID 3", + "126": "INVALID 4", + "127": "INVALID 5", + "128": "INVALID 6", + "129": "INVALID 7", + "130": "Milk", + "131": "Recovery Heart", + "132": "Green Rupee", + "133": "Blue Rupee", + "134": "Red Rupee", + "135": "Purple Rupee", + "136": "Gold Rupee", + "137": "INVALID 8", + "138": "STICKS 5", + "139": "STICKS 10", + "140": "NUTS 5", + "141": "NUTS 10", + "142": "BOMBS 5", + "143": "BOMBS 10", + "144": "BOMBS 20", + "145": "BOMBS 30", + "146": "ARROWS SMALL", + "147": "ARROWS MEDIUM", + "148": "ARROWS LARGE", + "149": "SEEDS 30", + "150": "BOMBCHUS 5", + "151": "BOMBCHUS 20", + "152": "STICK UPGRADE 20", + "153": "STICK UPGRADE 30", + "154": "NUT UPGRADE 30", + "155": "NUT UPGRADE 40", + "255": "", + "256": "Haunted Wasteland", + "257": "Gerudos Fortress", + "258": "Gerudo Valley", + "259": "Hylia Lakeside", + "260": "Lon Lon Ranch", + "261": "Market", + "262": "Hyrule Field", + "263": "Death Mountain", + "264": "Kakariko Village", + "265": "Lost Woods", + "266": "Kokiri Forest", + "267": "Zoras Domain", + "268": "", + "269": "", + "270": "", + "271": "", + "272": "", + "273": "", + "274": "", + "275": "", + "276": "", + "277": "", + "278": "", + "279": "", + "280": "", + "281": "", + "282": "", + "283": "", + "284": "", + "285": "", + "286": "", + "287": "", + "288": "", + "289": "", + "290": "", + "291": "", + "292": "Hyrule Field", + "293": "Kakariko Village", + "294": "Graveyard", + "295": "Zoras River", + "296": "Kokiri Forest", + "297": "Sacred Forest Meadow", + "298": "Lake Hylia", + "299": "Zoras Domain", + "300": "Zoras Fountain", + "301": "Gerudo Valley", + "302": "Lost Woods", + "303": "Desert Colossus", + "304": "Gerudo's Fortress", + "305": "Haunted Wasteland", + "306": "Market", + "307": "Hyrule Castle", + "308": "Death Mountain Trail", + "309": "Death Mountain Crater", + "310": "Goron City", + "311": "Lon Lon Ranch", + "312": "Question Mark", + "313": "Ganon's Castle" +} diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json new file mode 100644 index 000000000..820eb4cbc --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_fra.json @@ -0,0 +1,231 @@ +{ + "health": "Vie - $0 Coeurs", + "magic": "Magie - $0", + "rupees": "Rubis - $0", + "floor": "Étage $0", + "basement": "Sous-sol $0", + "item_menu": "Inventaire", + "map_menu": "Carte - $0", + "quest_menu": "Statut de la quête", + "equip_menu": "Equipment", + "overworld": "Surmonde", + "equipped": "$0 - Équipé", + "save_prompt": "Voulez-vous sauvegarder?", + "game_saved": "Jeu sauvegardé", + "assigned_to": "Assigné au $0", + "0": "Bâton Mojo - $0", + "1": "Noix Mojo - $0", + "2": "Bombes - $0", + "3": "Arc des Fées - $0", + "4": "Flèche de Feu", + "5": "Feu de Din", + "6": "Lance-Pierre des Fées - $0", + "7": "Ocarina des Fées", + "8": "Ocarina of Temps", + "9": "Missiles Teigneux - $0", + "10": "Grappin", + "11": "Super Grappin", + "12": "Flèche de Glace", + "13": "Vent de Farore", + "14": "Boomerang", + "15": "Monocle de Vérité", + "16": "Haricot Magique - $0", + "17": "Masse des Titans", + "18": "Flèche de Lumière", + "19": "Amour de Nayru", + "20": "Bouteille Vide", + "21": "Potion Rouge", + "22": "Potion Verte", + "23": "Potion Bleue", + "24": "Fée", + "25": "Poisson", + "26": "Lait de Lon Lon", + "27": "Lettre de Ruto", + "28": "Flammme Bleue", + "29": "Insectes", + "30": "Âme", + "31": "Lait de Lon Lon (moitié)", + "32": "Esprit", + "33": "Oeuf Curieux", + "34": "Poulet", + "35": "Lettre de Zelda", + "36": "Masque du Renard", + "37": "Masque de Mort", + "38": "Masque d'Effroi", + "39": "Masque du Lapin", + "40": "Masque de Goron", + "41": "Masque de Zora", + "42": "Masque de Gerudo", + "43": "Masque de Vérité", + "44": "VENDU", + "45": "Oeuf de Poche", + "46": "Cocotte de poche", + "47": "P'tit Poulet", + "48": "Champignon suspect", + "49": "Mixture suspecte", + "50": "Scie du chasseur", + "51": "Épée de Goron (brisée)", + "52": "Ordonnance", + "53": "Crapaud-qui-louche", + "54": "Gouttes", + "55": "Certificat", + "56": "Arc et Flèche de Feu", + "57": "Arc et Flèche de Glace", + "58": "Arc et Flèche de Lumière", + "59": "Épée Kokiri", + "60": "Épée de Légende", + "61": "Lame des Géants", + "62": "Bouclier Mojo", + "63": "Bouclier Hylien", + "64": "Bouclier Miroir", + "65": "Tunique Kokiri", + "66": "Tunique Goron", + "67": "Tunique Zora", + "68": "Bottes Kokiri", + "69": "Bottes de plomb", + "70": "Bottes des airs", + "71": "Sac de graines (Contient 30)", + "72": "Sac de graines (Contient 40)", + "73": "Sac de graines (Contient 50)", + "74": "Carquois (Contient 30)", + "75": "Carquois (Contient 40)", + "76": "Carquois (Contient 50)", + "77": "Sac de bombes (Contient 20)", + "78": "Sac de bombes (Contient 30)", + "79": "Sac de bombes (Contient 40)", + "80": "Bracelet Goron", + "81": "Gantelets d'argent", + "82": "Gentelets d'or", + "83": "Écaille d'argent", + "84": "Écaille d'or", + "85": "Lame des Géants (Brisée)", + "86": "GRANDE BOURSE", + "87": "Bourse de Géant", + "88": "Deku Seeds", + "89": "Canne à pèche", + "90": "Menuet des Bois", + "91": "Boléro du Feu", + "92": "Sérénade de l'Eau", + "93": "Requiem des Esprits", + "94": "Nocturne de l'Ombre", + "95": "Prélude de la Lumière", + "96": "Berceuse de Zelda", + "97": "Chant d'Epona", + "98": "Chant de Saria", + "99": "Chant du Soleil", + "100": "Chant du Temps", + "101": "Chant des Tempêtes", + "102": "Médaillon de la Forêt", + "103": "Médaillon du Feu", + "104": "Médaillon de l'Eau", + "105": "Médaillon de l'Esprit", + "106": "Médaillon de l'Ombre", + "107": "Médaillon de la Lumière", + "108": "Émeraude Kokiri", + "109": "Rubis Goron", + "110": "Saphir Zora", + "111": "Pierre de Souffrance", + "112": "Carte Gerudo", + "113": "Skulltula d'or - $0", + "114": "Quart de Coeur - $0", + "115": "Quart de Coeur", + "116": "Clé d'or", + "117": "Boussole", + "118": "Carte du Donjon", + "119": "Petite Clé", + "120": "PETITE BOUTEILLE DE MAGIE", + "121": "GRANDE BOUTEILLE DE MAGIE", + "122": "Épée de Biggoron", + "123": "INVALIDE 1", + "124": "INVALIDE 2", + "125": "INVALIDE 3", + "126": "INVALIDE 4", + "127": "INVALIDE 5", + "128": "INVALIDE 6", + "129": "INVALIDE 7", + "130": "Lait de Lon Lon", + "131": "Coeur de Vie", + "132": "Rubis Vert", + "133": "Rubis Bleu", + "134": "Rubis Rouge", + "135": "Rubis Pourpre", + "136": "Énorme Rubis", + "137": "INVALIDE 8", + "138": "BÂTON MOJO 5", + "139": "BÂTON MOJO 10", + "140": "NOIX MOJO 5", + "141": "NOIX MOJO 10", + "142": "BOMBES 5", + "143": "BOMBES 10", + "144": "BOMBES 20", + "145": "BOMBES 30", + "146": "ARROWS SMALL", + "147": "ARROWS MEDIUM", + "148": "ARROWS LARGE", + "149": "GRAINES MOJO 30", + "150": "MISSILES TEIGNEUX 5", + "151": "MISSILES TEIGNEUX 20", + "152": "AMÉLIORATION BÂTON MOJO 20", + "153": "AMÉLIORATION BÂTON MOJO 30", + "154": "AMÉLIORATION NOIX MOJO 30", + "155": "AMÉLIORATION NOIX MOJO 40", + "255": "", + "256": "Désert Hanté", + "257": "Forteresse Gerudo", + "258": "Vallée Gerudo", + "259": "Laboratoire du Lac", + "260": "Ranch Lon Lon", + "261": "Place du Marché", + "262": "Plaine d'Hyrule", + "263": "Montagne du Péril", + "264": "Village Cocorico", + "265": "Bois Perdus", + "266": "Forêt Kokiri", + "267": "Domaine Zora", + "268": "", + "269": "", + "270": "", + "271": "", + "272": "", + "273": "", + "274": "", + "275": "", + "276": "", + "277": "", + "278": "", + "279": "", + "280": "", + "281": "", + "282": "", + "283": "", + "284": "", + "285": "", + "286": "", + "287": "", + "288": "", + "289": "", + "290": "", + "291": "", + "292": "Plaine d'Hyrule", + "293": "Village Cocorico", + "294": "Cimetière", + "295": "Rivière Zora", + "296": "Forêt Kokiri", + "297": "Bosquet Sacré", + "298": "Lac Hylia", + "299": "Domaine Zora", + "300": "Fountaine Zora", + "301": "Vallée Gerudo", + "302": "Bois Perdus", + "303": "Colosse du Désert", + "304": "Forteresse Gerudo", + "305": "Désert Hanté", + "306": "Place du Marché", + "307": "Château d'Hyrule", + "308": "Chemin du Péril", + "309": "Cratère du Péril", + "310": "Village Goron", + "311": "Ranch Lon Lon", + "312": "Point d'interrogation", + "313": "Château de Ganon" +} diff --git a/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json b/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json new file mode 100644 index 000000000..630d933fc --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/kaleidoscope_ger.json @@ -0,0 +1,231 @@ +{ + "health": "Energie - $0 Herzen", + "magic": "Magie - $0", + "rupees": "Rubine - $0", + "floor": "Etage $0", + "basement": "Keller $0", + "item_menu": "Gegenstände", + "map_menu": "Karte - $0", + "quest_menu": "Quest Status", + "equip_menu": "Ausrüstung", + "overworld": "Überwelt", + "equipped": "$0 - Ausgerüstet", + "save_prompt": "Spielstand sichern?", + "game_saved": "Spielstand gesichert", + "assigned_to": "$0 zugeordnet", + "0": "Deku-Stab - $0", + "1": "Deku-Nuß - $0", + "2": "Bombe - $0", + "3": "Feen-Bogen - $0", + "4": "Feuer-Pfeil", + "5": "Dins Feuerinferno", + "6": "Feen-Schleuder - $0", + "7": "Feen-Okarina", + "8": "Okarina der Zeit", + "9": "Krabbelmine - $0", + "10": "Fanghaken", + "11": "Enterhaken", + "12": "Eis-Pfeil", + "13": "Farores Donnersturm", + "14": "Bumerang", + "15": "Auge der Wahrheit", + "16": "Wundererbsen - $0", + "17": "Stahlhammer", + "18": "Licht-Pfeil", + "19": "Nayrus Umarmung", + "20": "Flasche", + "21": "Rotes Elixier", + "22": "Grünes Elixier", + "23": "Blaues Elixier", + "24": "Fee", + "25": "Fisch", + "26": "Milch", + "27": "Brief", + "28": "Blaues Feuer", + "29": "Käfer", + "30": "Nachtschwärmer", + "31": "Milch (1/2)", + "32": "Irrlicht", + "33": "Seltsames Ei", + "34": "Huhn", + "35": "Zeldas Brief", + "36": "Fuchs-Maske", + "37": "Schädel-Maske", + "38": "Geister-Maske", + "39": "Hasenohren", + "40": "Goronen-Maske", + "41": "Zora-Maske", + "42": "Gerudo-Maske", + "43": "Maske des Wissens", + "44": "Verkauft", + "45": "Ei", + "46": "Kiki", + "47": "Henni", + "48": "Schimmelpilz", + "49": "Modertrank", + "50": "Säge", + "51": "Goronen-Schwert (zerbrochen)", + "52": "Rezept", + "53": "Glotzfrosch", + "54": "Augentropfen", + "55": "Zertifikat", + "56": "Bogen Feuer-Pfeil", + "57": "Bogen Eis-Pfeil", + "58": "Bogen Licht-Pfeil", + "59": "Kokiri-Schwert", + "60": "Master-Schwert", + "61": "Langschwert", + "62": "Deku-schild", + "63": "Hylia-Schild", + "64": "Spiegel-Schild", + "65": "Kokiri-Rüstung", + "66": "Goronen-Rüstung", + "67": "Zora-Rüstung", + "68": "Lederstiefel", + "69": "Eisenstiefel", + "70": "Gleitstiefel", + "71": "Munitionstasche (30)", + "72": "Munitionstasche (40)", + "73": "Munitionstasche (50)", + "74": "Köcher (30)", + "75": "Köcher (40)", + "76": "Köcher (50)", + "77": "Bombentasche (20)", + "78": "Bombentasche (30)", + "79": "Bombentasche (40)", + "80": "Goronen-Armband", + "81": "Krafthandschuh", + "82": "Titanhandschuh", + "83": "Silberschuppe", + "84": "Goldschuppe", + "85": "Langschwert (gebrochen)", + "86": "Große Börse", + "87": "Riesenbörse", + "88": "Deku-Kerne", + "89": "Angel", + "90": "Menuett des Waldes", + "91": "Bolero des Feuers", + "92": "Serenade des Wassers", + "93": "Requiem der Geister", + "94": "Nocturne des Schattens", + "95": "Kantate des Lichts", + "96": "Zeldas Wiegenlied", + "97": "Eponas Lied", + "98": "Salias Lied", + "99": "Hymne der Sonne", + "100": "Hymne der Zeit", + "101": "Song of Storms", + "102": "Amulett des Waldes", + "103": "Amulett des Feuers", + "104": "Amulett des Wassers", + "105": "Amulett der Geister", + "106": "Amulett des Schattens", + "107": "Amulett des Lichts", + "108": "Kokiri-Smaragd", + "109": "Goronen-Opal", + "110": "Zora-Saphir", + "111": "Stein des Wissens", + "112": "Gerudo-Paß", + "113": "Skulltula-Symbol - $0", + "114": "Herzteil - $0", + "115": "Herzteil", + "116": "Master-Schlüssel", + "117": "Kompaß", + "118": "Labyrinth-Karte", + "119": "Kleiner Schlüssel", + "120": "MAGIE KLEIN", + "121": "MAGIE GROß", + "122": "Biggoron-Schwert", + "123": "UNGÜLTIG 1", + "124": "UNGÜLTIG 2", + "125": "UNGÜLTIG 3", + "126": "UNGÜLTIG 4", + "127": "UNGÜLTIG 5", + "128": "UNGÜLTIG 6", + "129": "UNGÜLTIG 7", + "130": "Milch", + "131": "Herz", + "132": "ein Rubin", + "133": "5 Rubine", + "134": "20 Rubine", + "135": "50 Rubine", + "136": "200 Rubine", + "137": "UNGÜLTIG 8", + "138": "STÄBE 5", + "139": "STÄBE 10", + "140": "NÜSSE 5", + "141": "NÜSSE 10", + "142": "BOMBEN 5", + "143": "BOMBEN 10", + "144": "BOMBEN 20", + "145": "BOMBEN 30", + "146": "PFEILE KLEIN", + "147": "PFEILE MITTEL", + "148": "PFEILE GROß", + "149": "KERNE 30", + "150": "KRABBELMINEN 5", + "151": "KRABBELMINEN 20", + "152": "STAB UPGRADE 20", + "153": "STAB UPGRADE 30", + "154": "NUß UPGRADE 30", + "155": "NUß UPGRADE 40", + "255": "", + "256": "Gespensterwüste", + "257": "Gerudo-Festung", + "258": "Gerudotal", + "259": "Hylia-See", + "260": "Lon Lon-Farm", + "261": "Marktplatz", + "262": "Hylianische Steppe", + "263": "Todesberg", + "264": "Kakariko", + "265": "Verlorene Wälder", + "266": "Kokiri-Wald", + "267": "Zoras Reich", + "268": "", + "269": "", + "270": "", + "271": "", + "272": "", + "273": "", + "274": "", + "275": "", + "276": "", + "277": "", + "278": "", + "279": "", + "280": "", + "281": "", + "282": "", + "283": "", + "284": "", + "285": "", + "286": "", + "287": "", + "288": "", + "289": "", + "290": "", + "291": "", + "292": "Hylianische Steppe", + "293": "Kakariko", + "294": "Friedhof", + "295": "Zora-Fluss", + "296": "Kokiri-Wald", + "297": "Heilige Lichtung", + "298": "Hylia-See", + "299": "Zoras Reich", + "300": "Zoras Quelle", + "301": "Gerudotal", + "302": "Verlorene Wälder", + "303": "Wüstenkoloss", + "304": "Gerudo-Festung", + "305": "Gespensterwüste", + "306": "Marktplatz", + "307": "Schloß Hyrule", + "308": "Pfad zum Todesberg", + "309": "Todeskrater", + "310": "Goronia", + "311": "Lon Lon-Farm", + "312": "Fragezeichen", + "313": "Teufelsturm" +} diff --git a/OTRExporter/assets/accessibility/texts/misc_eng.json b/OTRExporter/assets/accessibility/texts/misc_eng.json new file mode 100644 index 000000000..6bff3b328 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/misc_eng.json @@ -0,0 +1,24 @@ +{ + "minutes_plural" : "$0 minutes", + "minutes_singular" : "$0 minute", + "seconds_plural" : "$0 seconds", + "seconds_singular" : "$0 second", + "input_button_a": "the A button", + "input_button_b": "the B button", + "input_button_c": "the C button", + "input_button_l": "the L button", + "input_button_r": "the R button", + "input_button_z": "the Z button", + "input_button_c_up": "C Up", + "input_button_c_down": "C Down", + "input_button_c_left": "C Left", + "input_button_c_right": "C Right", + "input_analog_stick": "the Analog Stick", + "input_d_pad": "the D-Pad", + "input_d_pad_up": "D-Pad Up", + "input_d_pad_down": "D-Pad Down", + "input_d_pad_left": "D-Pad Left", + "input_d_pad_right": "D-Pad Right", + "yes": "Yes", + "no": "No" +} diff --git a/OTRExporter/assets/accessibility/texts/misc_fra.json b/OTRExporter/assets/accessibility/texts/misc_fra.json new file mode 100644 index 000000000..0d9073e50 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/misc_fra.json @@ -0,0 +1,24 @@ +{ + "minutes_plural" : "$0 minutes", + "minutes_singular" : "$0 minute", + "seconds_plural" : "$0 secondes", + "seconds_singular" : "$0 seconde", + "input_button_a": "le bouton A", + "input_button_b": "le bouton B", + "input_button_c": "le bouton C", + "input_button_l": "le bouton L", + "input_button_r": "le bouton R", + "input_button_z": "le bouton Z", + "input_button_c_up": "C Haut", + "input_button_c_down": "C Bas", + "input_button_c_left": "C Gauche", + "input_button_c_right": "C Droit", + "input_analog_stick": "le Stick Analogique", + "input_d_pad": "D-Pad", + "input_d_pad_up": "D-Pad Haut", + "input_d_pad_down": "D-Pad Bas", + "input_d_pad_left": "D-Pad Gauche", + "input_d_pad_right": "D-Pad Droit", + "yes": "Oui", + "no": "Non" +} diff --git a/OTRExporter/assets/accessibility/texts/misc_ger.json b/OTRExporter/assets/accessibility/texts/misc_ger.json new file mode 100644 index 000000000..2e07143f0 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/misc_ger.json @@ -0,0 +1,24 @@ +{ + "minutes_plural" : "$0 Minuten", + "minutes_singular" : "eine Minute", + "seconds_plural" : "$0 Sekunden", + "seconds_singular" : "eine Sekunde", + "input_button_a": "den A-Knopf", + "input_button_b": "den B-Knopf", + "input_button_c": "den C-Knopf", + "input_button_l": "den L-Knopf", + "input_button_r": "den R-Knopf", + "input_button_z": "den Z-Knopf", + "input_button_c_up": "C Oben", + "input_button_c_down": "C Unten", + "input_button_c_left": "C Links", + "input_button_c_right": "C Rechts", + "input_analog_stick": "den Analog-Stick", + "input_d_pad": "das Steuerkreuz", + "input_d_pad_up": "Steuerkreuz Oben", + "input_d_pad_down": "Steuerkreuz Unten", + "input_d_pad_left": "Steuerkreuz Links", + "input_d_pad_right": "Steuerkreuz Rechts", + "yes": "Ja", + "no": "Nein" +} diff --git a/OTRExporter/assets/accessibility/texts/scenes_eng.json b/OTRExporter/assets/accessibility/texts/scenes_eng.json new file mode 100644 index 000000000..7f9397bbf --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/scenes_eng.json @@ -0,0 +1,112 @@ +{ + "0": "Inside the Deku Tree", + "1": "Dodongo's Cavern", + "2": "Inside Jabu-Jabu's Belly", + "3": "Forest Temple", + "4": "Fire Temple", + "5": "Water Temple", + "6": "Spirit Temple", + "7": "Shadow Temple", + "8": "Bottom of The Well", + "9": "Ice Cavern", + "10": "", // Stairs to Ganondorf's Lair (No title card) + "11": "Gerudo Training Ground", + "12": "Thieves' Hideout", + "13": "Ganon's Castle", + "14": "", // Escape from Ganon's Castle (No title card) + "15": "", // Escape from Ganon's Castle 5 (No title card)x + "16": "Treasure Box Shop", + "17": "Parasitic Armored Arachnid - Gohma", + "18": "Infernal Dinosaur - King Dodongo", + "19": "Bio-electric Anemone - Barinade", + "20": "Evil Spirit from Beyond - Phantom Ganon", + "21": "Subterranean Lava Dragon - Volvagia", + "22": "Giant Aquatic Amoeba - Morpha", + "23": "Sorceress Sisters - Twinrova", + "24": "Phantom Shadow Beast - Bongo Bongo", + "25": "Great King of Evil - Ganondorf", + "26": "", + "27": "", // Entrance to Market (No title card) + "28": "", + "29": "", + "30": "Back Alley", + "31": "Back Alley", + "32": "Market", + "33": "Market", + "34": "Market", + "35": "", // Temple of Time Exterior (No title card) + "36": "SCENE_SHRINE_N", + "37": "SCENE_SHRINE_R", + "38": "", // House of the Know-it-All Brothers (No title card) + "39": "", // House of Twins (No title card) + "40": "", // House of the Great Mido (No title card) + "41": "", // Saria's House (No title card) + "42": "", // Kakariko House 1 (No title card) + "43": "", // Back Alley House 1 (No title card) + "44": "Bazaar", + "45": "Kokiri Shop", + "46": "Goron Shop", + "47": "Zora Shop", + "48": "", // Closed Shop (No title card) + "49": "Potion Shop", + "50": "", // Bombchu Shop (No title card) + "51": "Happy Mask Shop", + "52": "", // Link's House (No title card) + "53": "", // Dog Lady's House (No title card) + "54": "Stable", + "55": "", // Impa's House (No title card) + "56": "Lakeside Laboratory", + "57": "", // Running Man's Tent (No title card) + "58": "Gravekeepers Hut", + "59": "Great Fairy's Fountain", + "60": "Fairy's Fountain", + "61": "Great Fairy's Fountain", + "62": "", // Grottos (No title card) + "63": "", // Tomb 1 (No title card) + "64": "", // Tomb 2 (No title card) + "65": "Royal Family's Tomb", + "66": "Shooting Gallery", + "67": "Temple of Time", + "68": "Chamber of The Sages", + "69": "Castle Courtyard", + "70": "Castle Courtyard", + "71": "", // Goddesses Cutscene (No title card) + "72": "Unknown Place", + "73": "Fishing Pond", + "74": "Castle Courtyard", + "75": "Bombchu Bowling Alley", + "76": "", // Lon Lon Ranch House/Silo (No title card) + "77": "", // Guard House (No title card) + "78": "", // Potion Shop (No title card) + "79": "Ganon", + "80": "House of Skulltula", + "81": "Hyrule Field", + "82": "Kakariko Village", + "83": "Graveyard", + "84": "Zora's River", + "85": "Kokiri Forest", + "86": "Sacred Forest Meadow", + "87": "Lake Hylia", + "88": "Zoras Domain", + "89": "Zoras Fountain", + "90": "Gerudo Valley", + "91": "Lost Woods", + "92": "Desert Colossus", + "93": "Gerudo's Fortress", + "94": "Haunted Wasteland", + "95": "Hyrule Castle", + "96": "Death Mountain Trail", + "97": "Death Mountain Crater", + "98": "Goron City", + "99": "Lon Lon Ranch", + "100": "", + "101": "", // Debug: Test Map (No title card) + "102": "", // Debug: Test Room (No title card) + "103": "", // Debug: Depth Test (No title card) + "104": "", // Debug: Stalfos Miniboss Room (No title card) + "105": "", // Debug: Stalfos Boss Room (No title card) + "106": "", // Debug: Dark Link Room (No title card) + "107": "", + "108": "", // Debug: SRD Room (No title card) + "109": "" // Debug: Treasure Chest Warp (No title card) +} \ No newline at end of file diff --git a/OTRExporter/assets/accessibility/texts/scenes_fra.json b/OTRExporter/assets/accessibility/texts/scenes_fra.json new file mode 100644 index 000000000..fa36c8840 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/scenes_fra.json @@ -0,0 +1,112 @@ +{ + "0": "Abre Mojo", + "1": "Caverne Dodongo", + "2": "Ventre de Jabu-Jabu", + "3": "Temple de la Forêt", + "4": "Temple du Feu", + "5": "Temple de l'Eau", + "6": "Temple de l'Esprit", + "7": "Temple de l'Ombre", + "8": "Puits", + "9": "Caverne Polaire", + "10": "", // Escaliers vers le Repaire de Ganondorf (No title card) + "11": "Gymnase Gerudo", + "12": "Repaire des Voleurs", + "13": "Tour de Ganon", + "14": "", // Fuite du Château de Ganon (No title card) + "15": "", // Fuite du Château de Ganon 5 (No title card) + "16": "Chasse aux Trésors", + "17": "Monstre Insectoide Géant - Gohma", + "18": "Dinosaure Infernal - King Dodongo", + "19": "Anémone Bio-Electrique - Barinade", + "20": "Esprit Maléfique de l'Au-Delà - Ganon Spectral", + "21": "Dragon des Profondeurs - Volcania", + "22": "Amibe Aquatique Géante - Morpha", + "23": "Sorcières Jumelles - Duo Maléfique", + "24": "Monstre de l'Ombre - Bongo Bongo", + "25": "Seigneur du Malin - Ganondorf", + "26": "", + "27": "", // Entrée vers le Marché (No title card) + "28": "", + "29": "", + "30": "Ruelle", + "31": "Ruelle", + "32": "Place du Marché", + "33": "Place du Marché", + "34": "Place du Marché", + "35": "", // Extérieur du Temple du Temps (No title card) + "36": "SCENE_SHRINE_N", + "37": "SCENE_SHRINE_R", + "38": "", // Cabane des Frères Je-Sais-Tout (No title card) + "39": "", // Cabane des Jumelles (No title card) + "40": "", // Cabane du Grand Mido (No title card) + "41": "", // Cabane de Saria (No title card) + "42": "", // Maison du Village Cocorico 1 (No title card) + "43": "", // Maison de la Ruelle 1 (No title card) + "44": "Bazar", + "45": "Boutique Kokiri", + "46": "Boutique Goron", + "47": "Boutique Zora", + "48": "", // Magasin Fermé (No title card) + "49": "Apothicaire", + "50": "", // Magasin de Missiles (No title card) + "51": "Foire aux Masques", + "52": "", // Cabane de Link (No title card) + "53": "", // Dog Lady's House (No title card) + "54": "Étable", + "55": "", // Maison d'Impa (No title card) + "56": "Laboratoire du Lac", + "57": "", // Tente du Marathonien (No title card) + "58": "Cabane du fossoyeur", + "59": "Fountaine Royale des Fées", + "60": "Fountaine des Fées", + "61": "Fountaine Royale des Fées", + "62": "", // Grottes (No title card) + "63": "", // Tombe 1 (No title card) + "64": "", // Tombe 2 (No title card) + "65": "Tombe Royale", + "66": "Jeu d'adresse", + "67": "Temple du Temps", + "68": "Sanctuaire des Sages", + "69": "Cour du Château", + "70": "Cour du Château", + "71": "", // Goddesses Cutscene (No title card) + "72": "Endroit Inconnu", + "73": "Étang", + "74": "Cour du Château", + "75": "Bowling Teigneux", + "76": "", // Lon Lon Ranch House/Silo (No title card) + "77": "", // Guard House (No title card) + "78": "", // Potion Shop (No title card) + "79": "Ganon", + "80": "Maison des Araignées", + "81": "Plaine d'Hyrule", + "82": "Village Cocorico", + "83": "Cimetière", + "84": "Fleuve Zora", + "85": "Forêt Kokiri", + "86": "Bosquet Sacré", + "87": "Lac Hylia", + "88": "Domaine Zora", + "89": "Fontaine Zora", + "90": "Vallée Gerudo", + "91": "Bois Perdu", + "92": "Colosse du Désert", + "93": "Forteresse Gerudo", + "94": "Désert Hanté", + "95": "Château d'Hyrule", + "96": "Chemin du Péril", + "97": "Cratère du Péril", + "98": "Village Goron", + "99": "Ranch Lon Lon", + "100": "", + "101": "", // Debug: Test Map (No title card) + "102": "", // Debug: Test Room (No title card) + "103": "", // Debug: Depth Test (No title card) + "104": "", // Debug: Stalfos Miniboss Room (No title card) + "105": "", // Debug: Stalfos Boss Room (No title card) + "106": "", // Debug: Dark Link Room (No title card) + "107": "", + "108": "", // Debug: SRD Room (No title card) + "109": "" // Debug: Treasure Chest Warp (No title card) +} \ No newline at end of file diff --git a/OTRExporter/assets/accessibility/texts/scenes_ger.json b/OTRExporter/assets/accessibility/texts/scenes_ger.json new file mode 100644 index 000000000..a3f7370e0 --- /dev/null +++ b/OTRExporter/assets/accessibility/texts/scenes_ger.json @@ -0,0 +1,112 @@ +{ + "0": "Im Deku-Baum", + "1": "Dodongos Höhle", + "2": "Jabu-Jabus Bauch", + "3": "Waldtempel", + "4": "Feuertempel", + "5": "Wassertempel", + "6": "Geistertempel", + "7": "Schattentempel", + "8": "Grund des Brunnens", + "9": "Eishöhle", + "10": "", // Treppe zu Ganondorfs Verließ (Keine Title-Card) + "11": "Gerudo-Arena", + "12": "Diebesversteck", + "13": "Ganons Schloß", + "14": "", // Flucht aus Ganons Schloß (Keine Title-Card) + "15": "", // Flucht aus Ganons Schloß 5 (Keine Title-Card) + "16": "Truhenlotterie", + "17": "Gepanzerter Spinnenparasit - Gohma", + "18": "Infernosaurus - King Dodongo", + "19": "Elektroterristrisches Biotentakel - Barinade", + "20": "Reitendes Unheil - Phantom-Ganon", + "21": "Subterraner Lavadrachoid - Volvagia", + "22": "Aquamöbes Wassertentakel - Morpha", + "23": "Höllische Hexenarmada - Killa Ohmaz", + "24": "Bestialische Schattenmonstrosität - Bongo Bongo", + "25": "Großmeister des Bösen - Ganondorf", + "26": "", + "27": "", // Eingang zum Marktplatz (Keine Title-Card) + "28": "", + "29": "", + "30": "Seitenstraße", + "31": "Seitenstraße", + "32": "Marktplatz", + "33": "Marktplatz", + "34": "Marktplatz", + "35": "", // Vor der Zitadelle der Zeit (Keine Title-Card) + "36": "SCENE_SHRINE_N", + "37": "SCENE_SHRINE_R", + "38": "", // Haus der Allwissenden Brüder (Keine Title-Card) + "39": "", // Haus der Zwillinge (Keine Title-Card) + "40": "", // Midos Haus (Keine Title-Card) + "41": "", // Salias Haus (Keine Title-Card) + "42": "", // Kakariko Haus 1 (Keine Title-Card) + "43": "", // Steinstraßen Haus 1 (Keine Title-Card) + "44": "Basar", + "45": "Kokiri-Laden", + "46": "Goronen-Laden", + "47": "Zora-Laden", + "48": "", // Geschlossener Laden (Keine Title-Card) + "49": "Magie-Laden", + "50": "", // Krabbelminen-Laden (Keine Title-Card) + "51": "Maskenhändler", + "52": "", // Links Haus (Keine Title-Card) + "53": "", // Haus der Hunde-Dame (Keine Title-Card) + "54": "Stall", + "55": "", // Impas Haus (Keine Title-Card) + "56": "Hylia-See Laboratorium", + "57": "", // Zelt des Rennläufers (Keine Title-Card) + "58": "Hütte des Totengräbers", + "59": "Feen-Quelle", + "60": "Feen-Brunnen", + "61": "Feen-Quelle", + "62": "", // Grotten (Keine Title-Card) + "63": "", // Grab 1 (Keine Title-Card) + "64": "", // Grab 2 (Keine Title-Card) + "65": "Königsgrab", + "66": "Schießbude", + "67": "Zitadelle der Zeit", + "68": "Halle der Weisen", + "69": "Burghof", + "70": "Burghof", + "71": "", // Göttinnen Cutscene (Keine Title-Card) + "72": "Unbekannter Ort", + "73": "Fischweiher", + "74": "Burghof", + "75": "Minenbowlingbahn", + "76": "", // Lon Lon-Farm Haus/Silo (Keine Title-Card) + "77": "", // Wachposten (Keine Title-Card) + "78": "", // Magie-Laden (Keine Title-Card) + "79": "Ganon", + "80": "Skulltulas Haus", + "81": "Hylianische Steppe", + "82": "Kakariko", + "83": "Friedhof", + "84": "Zora-Fluß", + "85": "Kokiri-Wald", + "86": "Waldlichtung", + "87": "Hylia-See", + "88": "Zoras Reich", + "89": "Zoras Quelle", + "90": "Gerudotal", + "91": "Verlorene Wälder", + "92": "Wüstenkoloss", + "93": "Gerudo-Festung", + "94": "Geisterwüste", + "95": "Schloß Hyrule", + "96": "Pfad zum Todesberg", + "97": "Todeskrater", + "98": "Goronia", + "99": "Lon Lon-Farm", + "100": "", + "101": "", // Debug: Test Karte (Keine Title-Card) + "102": "", // Debug: Test Raum (Keine Title-Card) + "103": "", // Debug: Tiefen Test (Keine Title-Card) + "104": "", // Debug: Stalfos-Ritter Miniboss Raum (Keine Title-Card) + "105": "", // Debug: Stalfos-Ritter Boss Raum (Keine Title-Card) + "106": "", // Debug: Schwarzer Link Raum (Keine Title-Card) + "107": "", + "108": "", // Debug: SRD Raum (Keine Title-Card) + "109": "" // Debug: Schatzkisten Teleport (Keine Title-Card) +} \ No newline at end of file diff --git a/OTRExporter/assets/fonts/Fipps-Regular.otf b/OTRExporter/assets/fonts/Fipps-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..9334dad594277ba8339786217d75260e58436dc8 GIT binary patch literal 34220 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 z zLez*~hndkpYWdzs{Xh7nLPfop`Q}CC^G|GF|55M9ryC7too)UR_dqD(&%wr{p=vU& z>)XeWg84gpO0VcbWY?9XihFEJknIufUN4rS{`r zAL#W2{M)Y9sBNkj-}`Yq8GpTM6F#@%*Y!AmrkbM`s%5yV7w5A0wpDGxwOO?kN9%BO ziR#9+J^003u?e4R@O=xe?@?><`wkqh#ql;A_oF3VyA(9G5T9qFXI>j_d0|NljW~C< z+)dcndsa;ag#62fAS>v{FLJK z67+Bhpu1e4?NQCp&65Fl4|+I3wc%G{;|%n;Mb0f2{A~xmmf{LxvtKo-#cGmTg6q}; ze}sP90a$28t5f7(u?1q*wUL!pvSqCl?>ZTIvKrj4i61ODMZD)4(ylX>{FMa z-Cm4=7$Q7VFmFvbB1Lg74%UJ>yH#C?E7!>Vvw;iF9I2Dkcr1P=HZN1T0kM4?cs@Me zgq~En4OC%hvq;a2faA^hHLJ!Q6smFHq4D92c_rAK^-FJ~B0!I%n!5Q-9sm(6p!N8_m_tXEy)+wDHp}pZ2wu<616n`NN5) zpZLw`vFUTBKR83rxOB!>X8dtx^UVI4pPKotlZKqsb<({j{p{ppPrmBpXJ-wWwSLyU zvwnTbxu^X4)U!|Bb?URH)tol}v@1`0=Cq&ZDsvlh59D5(J$m-lv!899(0WDdKepAk z-QJ#QKd1dm9l4HcI-c(M&728ymdv?h&RcVHbFY~D=IK|Q{_S~7=iNK+i}U_#e&hVU z`JbBqFAHiHtX=TPg5NBhw(zQjpFQK)Gp;-1pBB|E>Rj~rnayYRp83q;>c#68fAOsO zXZ_QXGnQPlRhd1vQW&mDE{taC3v_w(n~oOj20pF8h2=Z`#p(fOC3pFjUsUE{h| zb$z(&H!DwHdC$r(Ur>F)DHm+J;OSN4R&}qsulwZgtGoYW^^DbbtvPAUooilSyJ78* zda8R)?zz3^rFE%w7q0urx*x2MuWwp^&$mOwlli(terRR{Qj=VyFR!( zvb%ftt-C+F`|T?xUa{kfZ(SL^GI!;rSAO)$KV3EPsw=Mg;kzfld*i#me)Y*$Uw`!v z-!t_+x4-8{*G#+Swrjq3ZT8xWuKmQdKfP}9br)av)9a^QfA{sTUjO!zBl^bW$!)zy&t}5)J>ajdibWF-#p>w z9XJ2zmMynDd29WxOK!dA)*s$B@wSb({qlVc?|b}x|9bn}+dp^v@9&s)$LH_(^ZT3M zzw!N_yfbm<^gH|ReCDp1clF=(^j*LEz{wxj^MRk-ebU_@{b2Hg8$S5e56%0~XFl}P z51;qpYd#YF$h$uBqmR~q^p212zvt|GzI4xTKQ`fG+dh{6c*Vy*e(&-3e*V7neeb&O z{rA0a-)r|L@1K7E`up#^|KIO_^Anps@skH)57a-f^nra3eEq@d2bVl}@q>3i_|`+K zAKLZMT@O9}(AOXO)x+B!{?jMV`Q($I{KX@qA6fFq_DA0T$WxE}^QQv*JLXfnKK1IS zTR;8LPrvo(X^-X~-Jd@te^LJFeR^MFU-Q0u_r3C%|Jbz09)G;)@fD9>@%Yn^|KW+5 zPjo(U+Y`?`@xv#6`{d{+r$4#z$vZ!D%x9ke%qyRH^QqBKo%+=KpUOY=m8X9GwEy(P zr`JAx-P4ag{mrNMKQr-}3!ZuQ*}2a?_1STsed@VY&+UBf`RBg#y!U+L^J|~~((`|O zVZ;k_UbyImyI*+rh5vl9^2JkL-0gBs% z{^rYXeQxIGx<0oBGLi-&z0;wzKIAiqzJbrj)RnN$nsAhXHk_%h(5LHeeTBZwtMi`o zzUY0$`=PJ>qx}~Da(}nK*T2!f#s8T9ke~Ozuiq9hs1=&5p{B$&Sk&ot=_BDchdy&pwiUH2XyM>Fo3M!|KP? zzi*s3E;_DqT=lrxaihkK88>m<>El+7>zVM_gwITPZo*3wzI=4_=v7CrIeOjE7yg(2 zA(^DP6y zyfy@LL}2a#%nC3wYM|f#9n9D-3haD)0N&P1=G)tszP;@2IdAQJ>v5&tnvK8gTVwHF z&$EC2Po>^G&Rln}_n*}A?2mu(lVg7TpFe*2$G80WMtr;W$JhLL_m405@fk|Jy7blg zueQEA`_+lB&U|&stK(nwfAsH4z4FOd9(?8ASMGV`wpY%7#eYS=qJHpZ_05Ui$Qwc_ zN_`rQepxT`?(_Z`LN7J z+1`cTaM%M`FX7GhD!lPtjW^QEcq6>IUb{EP8|wAKXQ+gSI}ASDcr`&C4=Zm5=xLT} zg*7%$ErXSJjyg}Rf>lHNiFVE<-e@oBjqxr9wOt4Q>soc4dat?_p5BMl$Ka_viWt!o z>RI(!cx_)*UsGRK-&EgM|EhkWeyRRL{aXDOIO`%0{)gA>HF?$EG;fyI;+^Jg^EP?c zc$?K9yraD{yb0bGZ;#jK%@_aXN^hCB(Mx$(dm28^F<#7zdU3B7K2HQ5aR%N}8s1%v zs#kUJWXHm@I6*AC;p%j?NX>_BzeJtkwZgLQg-vDbzvb$?;QgIyw|bAdLS3z{QG3;0 z>Na(UdcS&DeL_8`9#S{KpZ= zqVR4+53YxFxk1&c8{wtyfuDA>8mVr9{q_NMlzJaH^-k5G?pCAK?dlkK=|`&%tK-x? zuo^!KyYOB$5uVy~&{~Un1U&tKI#GQJ_W1pNBu1 zpN6;ajA~cU!Ebm0mhhIvqd=d8dSJVpi4Rx;iXIR(&q|R60QWvQ2 zAXf5i)usLgUdSu3G=Biw|24Hy{X2Z9S7CSl99Hg6Vdwu$U8H^mEB8NPps+Z~I`fS~)&(&Re zrM^I~(yR3ry;WbN`}8)wU0>(hPt!N)7Jak+ zl>W4yu5Z;d^lf^kexE)`->y&Acj#IA{rVJrr#@Amraz!_`fh!fo~=KqTlI(ZqdKqK z^oJ3@`H1e&AJuaZ&6%q|22J#FJx|{Y*?ym1pzqfU^(XWh`T@O2Kd8^t59!7FVSSeV zq+X&Q(I@I#ym4N=*WgX`8olGa?Owl^_D=Ptd*^v8yiV_2Z=QEL<9L)9`27FU-!2O; zKM(a+KCXeTFTdvh&F|&#{3U(=HQV}2;27*)!Z+AC_j~ym!rysK_`5rH_qy}$JI|N@ z=Fa^!K3)IrbD%bHPG2$r+yh-Z*l%tegPkAj_krMYzrE8o2kPsu)s};8pgsor?Qr^a zZHBLPzYWypk^B~Jv;02S$HA^Czvocj|5|MwE^Mwp_jxe*25a+h?mbw$Ja@Ql40hdM zznAyR>kju?ckkieb0qB!cI{xlJ6J-WB6IltaPHZU>%xDbc1q-R_!`%)`;GVT*xk$X z{O+!E=fl63pXW6^KhQnlbFNSK>8>yT48!7%!|jBx=dt@9zNWkncP{+N4pT z2Z9msajr{rjDrof-J2)j?<4p_p6tz|2q*Ko{Kw2LY-!2wmVT2nyYi|rn^((MbaZC3 zNOM=5wICl|dhUw+w7UGUovYSm_by+N_a=0kr1zSJ)phmtdDWR$Z4K>@!B1{m)jBD! z8}r#!>n7#B#_ZZ`{-wqF;OKK7J60##I@WaLqa7>i^Zta+CFiZEZ>X=^yCR!kycj>_ zI_t9e89bWN*_qvEuI^r&KNdgPZ`u45o}a=iUs}8(3uyLsXY3p4i#hkXMUW^mKMk%KMGkj%+@d&<)5UZHrgrBMq(jXhSQ;fn5HoN%^3$ z0dQs4?u)E$&GHN(t}_tuJs+&$;b8u4w42 z@66_NOIP4r9fxN7pOlX@=3{LWA442UFcrnuhSmmPyrH!_@2y^!*J}VpK63n|e5^4` zP*ZJdo(|M%TmoQot2%kfs&)Z2-uPH7rP?}LkFPJ_KG9e@7fI8Ko(K>C`-D~5j=c@t zoEO2Dsv}PG**XB}pv3Gnbhn!>QiI%{ACJ3mb75%Vo6;nqhStYYNgtD4*HGVieEp<+ zrg5L=b>!D}w@=DfG-3eRY(CvKpX0(oLu+R~!*5IQErV~9@|9?&N>Gvo2G#)Vd_~); z?A}$`dl<*JI|eoIJZ}Lj z@N6eWG8gdA#jmAPHOK_+Q>vx`C~nKEQy+u(E7VZah?t4jv1~=YvY|EGkxzm6k_{lZ z*6b>D|73NwMtGyOb?>TuL!uM&dnVS61Db|nY&8=nn@$;z0eQ|z{ZrqpP=W&g|g28(}x(XO>$WG4dE;7iZ{PCfa z!wM%aGAAd7P99x2xy_s$)2Q;9iSJ;1V==xlfGdmf@v|P|M7USb*V`Fxfu+OAMptX8cwheQ;inP#;&wL|kb#h~V(!~5EaN!AH zfVrU50okyjdqx8#;z6$f8BWSiDNL;%mOtV6eGy&Tu>yjO6F4=DnFCKZHD+4`-ez2< zI}Vfv5HbUT$KTbkC+O*`_EQ^X>}%4s9NaV@01yv&&nj?5_l!yTmd44$XHCkVc&JN3 z!E12&bj*nwHX%DXJC~dfT+Z9OcW%R6NSYN;5s<792PbM>I}|9L0TDGUUxmv8NX-dy zSw7J=v1jk(hHQ4$UbHu}^pfmk(^fvfO}H|fUqzvjJ8Q+`Ue?dnJ??iqCr z=wS}VhY#rm?G4Zg0HMSUASEyf5l|KsL>VM#u^Tr}5FqdlrA1L$>21q_|ZLcBTODr2HwaH^IK0s#P=r%7^Tx_f!aq$;9HEd{x_u#dT1b*;$>F_f6JA z!Jwy=oLy44xa4fECWbF?H@Oa58}lN_or z9gwXvCOSOK!MB+c>p4rrgdIs?&~q{9TEpNlH^9p^lk?NS&!-Ra`#dzKht}jXJ6aIgpmng?e#KBncD#W;{(&f+y4IJJb= z@L(yg;lVOq!-M6G$RDZMI650gT8_?Xd|aE~R^Z6|*2(KMuRE95$)`84uB6Oie7Q@yR$B6AT#=I0m4UnG$A=|Qzl3YnTO>$Lic&gcqc zgN|2!)RnqQSF4G7h^|59+)(vZU5i}P*WkN-9T|_|dW4#!N2mYkE%SfNxw#BtOeN>E3%98)t`{}XxANT zfu5u1B1`uj)vr%SuIUoB1DTKc$a*YPXXrE3BIG>IRA(aNaTc;2OOb_Jtj@Bzo^z0y zTB0t~E0BxasV-;k2YJX{u&9?JOSeodmpo6GIvZ8s|E~V5FHn2*Ds>KWJgfB@XE zwa5crh0K4CUZ>aV4Z0VZvUBx?$a|iz{zGp>F0)H-Qa{(5kqvwqS-X!&MyU@uC1#ZR zkxyb)iMgZ8kxSZXb4gc9j_GRTkgh@g=sM($_8@0;1M)@hMXu;(eG9Tgw;@AxJF-LX zM`q|Qg}p{epf`zocK*pVNP*|6YGye?fmy|AYRL&B*&V~z1{u8H+MM3+^&ec#ss6KmQ@^F(220r5R4;;TZ4CL^1oFlyWTi65MOPwAU5zYt z4YJU+$X5?XR(d2d%5}&Lk4AQxS?hXav>K4Lo`8(-G2XG>amZ^iyEVz1?496E@unhw z+>HEj3v%7ly&2w2?}Y2XftWz0;8ao9`_^-uVn~k$0xI z*gMNx;w?pHdAa%^a^>gPJlT2P`CgZ|(!0Q0<#l_jy*1uiug6>Gt@k!~z21e$G7m+L zcbFQE4DU$f&FfH0yAiqT&E6K|Vc$UQ^Y4+7J`MTT>yYcMMD{j7b~lRr@Hk|6Bgh$# zL4LRu8Q4|G;^w3p{c-h}w^dbneaJaKj7;u}>hs8yg>N0Xgv-ksH4N z+2MDq&v`eg+1}0GE#9r(ZQlF5+r2xy_j`9D`~L&v#Qzof^qY`PZbOFr`^X~yP<_w4 z%lm+LxA(zd(ei~0qqA3U>$$imHhXjTnr&OR#Aa_@zjaH`M)+5~+t%#Zylzv^rOCFn zTl>4$tm)a(A8lXLjjQmnZEJUbv_tNVcF2#(j>2WJ4%=FXZEbF`we;LITQ_g+Hti+n z7VZg7U){Yeb$anaZ{GaqJOkW3+uS_UTy$Q4@20gqsrki=(+fhaMi+Lk+0ow|=?{{ojA3alg&78Sm$CmZo+jeZ;)V-rWcBX+Ly2RYO#N4|i zbZ>O2oJuV%f)y;oprXs<+SraQy;GW|&W6Gh$JC)XG52{v#VHxw_7_Dbts?}f2m+gz`m#$GeJ3yT-0H-=hGZ(P5vr)SHi?k#J3 z*F-lNyl*mi-xRto*o5ArTg>?_=KPk>dA|pk+-i`%wFK!~4M0&W5+S{8Gxu%_-5cF5 zr&8OCFh(|Q-Ligr(9fCempkmtwnlfDPIi=Zvcryghn?9SGP66(*e@xzo4Txc9KBq+ zOJsxKRt+w4(dwrWd-)8aNX2;WRe{Z+& zwcGpK?fvbxpLW|%hrO=Dj-$g~-(j!su-DD8?ar~+&#~9fvDeQr*SAbH@U={}{WRPA znk}5o_Wou|A1#ia-Fe$?vz^Ztdw+{NZ~JSu^Vw|SY_{~!V&}ct;;F^KVe!-KzPsz} z_?qqfPqY0^v;9r8@J@5^+UuuTIHy@Sr&)NWS@@<|xTZO{T>G~FX%@a|7Ooa|orBwr z-}PhRZL#gQ*#25EJ~ zw#CnEb6<<&>)H0Y+3q?EN2|TQ)wbJe+ikVix7q93EWB+NPwn=;b_-v-y}#Yw-){S9 zxA%A0>pJW>I_&iw_WBNc-5lHQ9DDs7d;J`H{Ty?B%Txnj%TzZWJ0G*{_3ie1drM^Y zroIi`L2FNccVuoi3?LDZ%@7bBecOAXQi5}F%J0DGWgBo5%!ADpS$BaLv!A?Z@$0h~%2Y!}Q)ag(y0>iY z@7dJTYw2dXVdN>Zr}*t13)yPZl-YBFjvd2{~N5^^z^-Xj9L+{#gIE-tP4~`lQbmC?NU1c8mFL z6i?HMQv`B+o*3EQ*S)qUShH!z>IfFaZ|je(?d{&Yb<5h+<{jI6*K}{iTM3UtM2YU)xaK+|qz0 z*l(rvCH&jJYZq?%^Bqj(@7nd&o?Q}`GVN5t7rk}?)8V-je65pMm)e&?j47Ge0)HGU;_G&zrXZJ*kBY(lIX*nIBnLmzyegBz0o9xdkzlL)_DuLy`ONVF&{csjk8R zDyz*alc<~60T8>8o2wh}SzX`K(9%%bunScPyLRn=YY#tm?eZ?-`xjhq2?|vuucjm?f!PqgQkE$CrazrpZkP(}aV%*h<(Gzt;OLL73 zld~oNIPlsA8FFnsN4Xzk=G@sA>NYm+dMl0j6NK1bWsS5Oe+;uZR(;xF;H0FEXVS5F zAI2HWq_@B?&%_ggh)7Tw>G3d|y34Qfh>Pl!7mEceqdExY2N*xlhnq9;^lo!^Z08Z& z-kCc-9#_gCAU-_qro6HulTIZQv1lZ~#QD`j615X+Y8zT2W#}LkI8^L6Q*Mx9XaETC zUn2qv=-4laB8NC?7>?*lgL=l`qCKW{751IztJYYvP^orCv>(-8+V^@807)h)l07Pu z3A$7e%v+$Us?uF5onE*g4iqI;4oP{5#QX%X(jm=M>B`-INjsgnaY{9etIs+FVb($v z0y?Pyg&1}KUW6qY3T#mW=JpG$;b;NaAwCRxERu*H{0dTfD~;m;DCKE`ol{~uQjrNF zeU&;BRAjbbMMN@@PWDv$Rp4;dMM?o$Dk`E~DjFqPf*_2R)8zh&NM^TbAlP|iEp+B4 z$Kye8Fp3Fc0fFAdT;@9s0P47VT3pt0_kB{DDKcf7SdoLeGqAaA$u1cAWgtQ z1^yvw_zH87k9cLBde}-6a7;So`F$CUIBaSQVv+G!GTu`GOkg#L5J);L9JFu&Sj&s8 ztV}?r%#Tr;$jxa#wcFf{u;St0-kCd^sGuk%mjpEdl}9-ms)Q=)2d4<{qJaqF0CEs6 zQKC<(5u+WAsOU=u19PC{B0)0I2ja$t6E+1YmA&lyx8hhijt!y zf;2!N#xStr#Htw|Vs<$Ah`b@n#)=N}u^%?X9&m~M(TI~$8mSSUaXL4mbwvhJ7HSld zKzUF$X^=@e-lI|}zYBZuh!{zB0b`^RY-+T!sz4>D$n6z6v-_Rg51m^SAjAWxx2Avw z>RcmHDK_Mv@JqQg1q+mmQb>vYrGhjhNo$2hCaAx&G@_~^L8L0!ozOw0A8Z*~fjvYr z5t<)EkWn6@dr~?Yjdi7AbU^9q%F0NWibNJJs7ZRhuU1mhV#yh3B-{|x_0D(LQfKbi zQKOWaFy82!QKLtJP{>HgL%<3v;HQ>0Nr?o3<`F#M$L0v-izAthPLPPog=*1n0BbNV z-=IvjwBm?{KZWg0(#u^2S2M9HD+OYKCF?_;1S*mYBH$$zk3z11R;<+t*#ce?tQAP* zWZvuR)NZ*McIOe?-I>FNS_cqFra&Oz6AYYxP$sovOH+E{C2G`KBcKVfKtuY27I}@D zn`_5Rdk}#=$lN6SXaZwVLEH~|R4nF1Wjr3~N@}sAQPpSV83vP*8A&BZf?CB05>Rw~ z3rzc#dhc~$6G9tyJ*;8i7JLghFa!Hx5W}1+SU+{@tlT0<-MEf=eiT%fNNXr{tYCzh zXU!a~1;apYGMQNkzRS$da0O{yLDm-OqY~(u-U1RRC|n_jYNLi5YK0a+2(-hDE#`!V zPyqA_bK%ZTE!t&3Sw%%<-;ioA1Ex=BfV8wqMbarsSJ@|}K_pfx zXP2=VRavq7FKP#NSrA}VcGxr%foiw}lSI2rEO+n&L@N2Cg`^@@hq2=Ey}8y{?*Xk!ub@lve&Rs)^VHtuRV<7Z0hXSeaw?K~(l#E8^60>Nh(o83XAYSGi4rD%!o*au_ z*XPy_sYG^Gt26)v;lca~KzJ&Ee-#g6>)>&TG6ck_lr^taJPzGhRaM;ueYJ@26ZP<5 zoMC(*{Iqn!n5SYh|1c9!aTBnGj9pk0me!aHMEJN{Uy{`3p?8SRqcqz#G;uPY~OeymF~lkE24e{ z^pc7rSRpzy1)T|75bBBX7)T7U08**s%1WI~&QH=44>uD}Cw9N1b_4~^axSxos=%VO z7{hWBBe&B|TBqhU{3oWfjZ=h#%>U`xFWMq0bwZB2>iH;_>wz9{OiLvebv97J_K8{B zhmlPF^EbYEGbK2f46H?rhouC4)T<0vO+lnChHw|Htq85HLZA=9%UD;E#v=qoBqE}2 zeiT$RmAAk_A@v;IJ)OBBuo2*b7Hoo)wF!n>cmXp^HERPn&>4I+;8RjiUZBUKJ;b=( zon{oxFn|*Y$Oi1)7>h(*ln(j_0SAPq5?vXFqc9MRbtN@u90QrgyI|k#K9bvD?=Vbv zknmhZC_EP)A2qEd_u>cWfXxP%0;EZ7Bl;IWPW_mGhG>DL; zKxut2++y))tWsQ~Ewnt{NN743XUq*o?9kAF za9BozHS`%AKq)^Nj{SwzPy+6&48#QO;x%!nDG}NY1?n;-fjS3SeJz>z%^^j;7RwB_ zfCVmuXCHR`oLn2dyC{6ac*tT2YkU*LN2EzCmdcY~3Vf}sm$Y-$k&!z^=m93uS4Z-@RLIusjw4Ic^_QDy>Fzy#7oECc8D)ip2x zOt{RU8U~L^(qM6|5fg#}krzb`m{7;%jy_NWM&QRG+XAd4RDud6niwH|zmRCPqfVnZ z-r5uP6^E;j8|B5nC@tfHMvLx#JRXv)IX2)&X(L0z z#vqc8u!+INY&c*b(MPz;M5BItIJ>la5jl8=%b?Q&HrQ z7%NIdFj$+(s&G0o)m54CQYjEkBvRCovlv-WX~c})!R-h_i>|DTyEt@6<`vRe5`-pq zLb*U+N(NKjZPcWwGiff1_fu4ww4L!Ms0hhGMi@U}#y^uG5@Z?RI8Y73SO$q<)(|sE zyXa~(8tF>GsKVf5vE<5(Gnh}3d(&P79>WpdEJ@n|VulsQi7iBgpndF!jrJ+%9}d=p zK}<4Q+=RFGPy>-iTomC&Gs<}?9tkLC)4rcx4ui%^Mi3TCWDr3}AlM#@S>HRQecw4p z$)pRlW3zmOh$<@=RYE$&XvW0zD>lxRh*#OP^rZVK9sVX+JSK*Wz+`|(2i)a`{n65=Z`P;2_YXzFc0EPkaCr)$zy{1v7mjj5;(bsK@4XWYO9 z3NYnwh^V&|1wr^a!*7@qH$HlDx!frQgQ?)XNixy7Z-^}V!Akf+(3^ugVhBsg1&kJPZ#?>^0E{rGSys zm^#@HYnbT;=RJ|*40y%auTh%~y&eq=1RnCRQmTM@1keL994^V0Xkc8L5XJl3x4r|nc-07v>gWxMvI`B^XhY)E2 z0Kpi^+W8KGFd(j!qAE#IA+JHGhuIICqL$*H!|)qIokNN-n(X=p5nGZj?YTysLFHqk z3#4UZ^c!Nfu0km>ABN3eq#s5YKs?|oK?PtS^@K?~%vpa0Jcy+q!h&0mboYyvN|~nNFg@~y_#Ti z8MTg7*BQQ=l+y4rBSD{G`;0HfB(fN=r_y3iu~01D1z08GGFh%i)M)o%-5_3_DPRa0 zj!29IF@)4B62m301c}`ebL=KcjqJxs#sp(XX0j!f#6ii}Xi}AG=gGN=w5#-T*6TUD z(DMdY77T?rX-#D$Z5shi`wS`@6^AwIl-x;@9nr{+D6B-qDm{j*eU}RHyacFR#Ls{d z#dHIO2dRSGrYHv)n2~nq)kFZOq>BBHR?nFkI^B6XafHj`8Q8IzG^;Uun7}>^2Bc;Z z8j1EuL?Lc$3sQ<1&eY1v3OEH4O`2cuc|vVv5-9dN;>IL|jPSOLB8QXeHiRq&G6gA( zKuH?bQa^!o#UwQ7HgrnkNU(3;kkh4wWVopg38iltTr5#B7$7iO;waIIR{F*>}eN@xL&K}Cfm;NVJQ8|bcTBz_lVU@R|Vqe09^jIG#e zRiY9K2c zuLu9q)-`!y7}U^}q+`fsO684Aa>1yBTU(UK5tvP@%}kC3O9hB~P@m)3Nrr9-GBt&i zqGs(Cnu>g&OFDD4QS2-oO+^uZi^t3mW% zhtw$8nBXNLk|^xJXmnAO2+XLAiG9Ztqm0WVh$LlGK+(=Kjs!l!Ixzyjvnh9i%n8d( z9WipOq)?1iMv;gX#t8V3M#t$Y9` z$z?RG_sUD`R3YD`Svgu=N}VSX0O&f#t=Tf-gh~ z*dRd=qos@u13v{5#OMb`5th059``kzeIy4XNDdx(zmT=mk@By+U%FzLu>+$n!)eE% zPcP0QS?rb_Bv73L=uGi6Bul&tY2tQt<*esn+bs@ZksHRfRQV3V_RhUXXJB@Iu zg``-)2YPBo(F3WM7!+Xr*TmjIlm!kYWRY+qDa+`RTCq<9?3EoLX%vPx4m5_rH8EE_ zPYdCNWO7lGEL2)MmKG6iYG6R!7BK2}0mlLbC}^+|9WoKeS>Kgh>V(jsFva6~x?Xz@{wa6iPCxJ0cE%7EQm{=4dvT&g!+v#|@2D6J9H>M5Ii?Im18qNhu8RY>lzqN;) zE$GEDEQ#W%=x^7lb93iFx};-?bWbG~AwV@`VkYc^#R(>)i#jk$nM@|Kk{T%@PB-JQ zPSg~uQU(mghJlr$Isogk^_*b=PvDgpei_K2nJ{^aMLF;=>QNG&I5e35BuOBVaJF_T zC4Pbk#u&1GQnNAWx)(zdLKI>YGkEG7(lB@vlh8;=G0qu91FjjWE5j%01bhU80r83? z%=C<(29Xvj_){rY=95e&x+;vVZnAhxk`9{5s|;uI$|)G!LnIc-*yuBe9L~C#{SXcf zH5anohMF^F)QoBtrqu=^=V^|dkK{I~?lY4xNN$bUZVg$=a_Mr~#!v+1lwy{)K`Z5y zQcB&d9>2p_c`YDe5J>DEFy&V)qf<2&W_-r$-VW z8Mms0@=&xfof2weyX3}=l-7ymV@{i4SYZxIM9a*@5sR1vKZm6hxnp5&9-Fh zBh`t73)Q2J393YRf{|*v!@+?$TP8Y9%%MzRGR;{|ZG+KTN+isx99Hw7dNP@YcbJ6m zPNo>>bfPtit6=(<33VWs!Oz+T1kq?x zI?gc6fFI-+j>0hrE#)F6O-;F@9Eh4Iv`ZhtSP8hekThuscpqBPR?{17ihK+eNS!G6 zVe_2bM|9(%Dx#dQaz+w9!7UV72=P;!fji{IbV5jG@wbrJ5}V2BQc}%nNaG^4K_a+|=f1xo{1NO7gUEo2TX2xlj?h4_1~{;-d6uQ9wM0 z#z~vuWK<*~zv0>Z28tS}ydmfc)eM7M4)h_6-B4c(@kL_(?%+2 zD-;GHwVPBunW%@PjgAMBMw6un^A@C_UA?Y~G@h0NA8Z(kcPyN^4x=3$3Fv~X_e#R?`kbi9hlaJDl-+a-1%S5d zj^zbdF&A(JOp*QRNm%d4hO@+w-cx3ui}oo-2Lq8W$P4 z8N0C#Id5Mb!Cj?$HMU`;|DssYhRgth$dyp==Oa-}1A~8r1?aBY(bc7}G{-FoNG2u5jEV-S(ixr(xvY{Ka{F$C3;Sb` zbi?V$0OLfBjf~Od-|rx_^Be{;T)8!zem^KKjasRg@*&7$$ed$~0xUZVEYhI!jMat= z?19h~{OJn#)9@x*M9IQP@GVqk!hka>SRl>t81V=s($N%W6LaOL!f?U?wJ624lVUx- zqgwVbNC3vnqFMPxwztzr@p%9|sJ22qBl6XZa+eX5DT9Aya)85++d&7zq^!o{S94U5N(e;BWg+hn)Im7Lum zwXY5&$gcY87APYECcqgx0f(S^h%!U<_)#=ysl`-YwZe?3iF;t`D9~7uaq#M$8qkpr zPnSfGgnZclfpcY~mx=SFeExzIyPId)fcrbJx7`m-lMJ)0|%eXrIRIvP{P`D zxSE+`@Gu#VLWFvf*gy`>)I%ZRD`1+iQW>gbrHOMexP0nqI>DKnG{>>4lC&&QR!aCYKq`+06E{3C%l)zW8b+L1$!!j}SwmeeCkMSvVgF-EI4#cXRZn~S&Ryc?JP!QffkMJEU;io3MK=q&`a&-Vy+bd zg(Mc9iJyb`NJwLwdSI}FEXB0@|HzusX1OUDK@bv#m~%} z5_2P>iDlU3)T0j1%n5enT9Gb5#a@C-g6LU8f0ot+L#7vOArlORjwtT`LGCuP#Z10T zJYe!6(nYoy!GgXCgdqi}S&2tGNNAA_S5~V|hHoEBq297IMOiH3fR2yFLLTD@aziYD zI4DVK4t9sE*kOX{z$9^Kjbd456C^I=E!ZX|DIPf}W3Iqp5d7k;MBdv9MdXc8K*zOM z2&%;xRD*LI$EJwHJz{GKUn9f@2RW%&?&Vx$n>faROP0KzVOm2u-l3X<*Uj3ngSI!g z$}qgmX@w(T$PDjN3Ln(1)ClYslhBN?}w-ia1 z*%vNiGJ&oNoUMdmCA)8M@sf*lNmHb!vbNw>?>wx=4kzH{Mo&5Kn7~MROARKgW7ym9 zyg`F~55H_|3-k`(71W>bBME;e>cKsK3$y%%G@A_wP@RJ=uyD=VMY+Nf zuJRUOlk7ZL5eo*s?DfjYN}J`WJx_8cE4!FllTCQ{*7l6xpp%X__uZLX%)SG9YFE6&K>!5dExs zFZF0yaq+wvdz;O)A=MF&VP_GB%8}7P1wd3qDx!t$Pb*DIiC!F+&p`-RRJa93b=b;_ zIbZCn@Ru}$J=mB@Dy_!=2JABRLddO}B@ZyCOl1ZvOiKdB<|I~ZXvX11JZeFVO(B)y zrWgyU7OHsAQU(1Blw*X{lro+l#19fC$etvfD)D_;m;tOH69JyIiys)hO(v2o9W3ak zBWt5GH*)YQ$0GGRbK5wL1uifHCm~4G#}EVEF=3DLolY6&!2VPUONL|EDF+0AeSzWV zBAx~Vq^H8qz+ouVNEM4fLL1JQ8Y%jXChAd~y^t+owxBek6G4Y^28Q6IBq`xyqg|~V zB+<2!h!G)w(#)DLB6zvtpuCJVvaFjLgqd3%Gr#jjI&_|$RirG*- zAZJ5!AP7^=*&yID)-F=752;!!b+t|(2HgD!X$EUx!}W1`#Ko0Cu_(7%nKl$lkXgpOvnj32-c8Aj*Y)UDBGeB@+8iSy%eHpQuJu`sKHPxtiCkt zRv3ylBub~<;v%ZfRAFD$P@z+sG?I=WA^474IgF~Of-E*V3S5rp zo9x8wRI`j&c(^E)+;3Jc6=|B;w6ShatTu?#xJcwA@W3h<4=%iibv(cWj6!VM#+Wc| zWRMDFGB;omRh5FdXbq)yX(djf{LY&RUJyCwfDA&jF8#HyqwXLKF@ zO^2a_XbYS4A?lkr-T31`Sfv4T7-KZUP%X;v?xH}uF=2(Byohy(AqC6PM}6@_ICD_`7k>u~Qm=#E=}7Iu=YLMOmbu|~Tbu+$sa zu=&Dbe8g!z%!<5Z=YSL?HiMf8 z5}BC}yCx7b%}9P*4tuy@;S*=TEw9C3P01v{N4C0}blo})^&Od?x6!<3b zN@N=PBFRfh4xs{5hiRO=Yo8%8m4Rc~p#&(Eok@~OR~1h>r?U!(z_}|~b|@=po8+D( zcP2U9o3P@|DD<$-N<(KkbdqP88DqtRaI8==hht1+<_`@nf!y#|+>TSQEV$Q3Xze&G z3s=GH%&UXvp15WWW*~^uuoe1b=Z3Xt_`!@AfWMeI$2v3O!fXl2OaYg1!X*Rh0EJs> z1sz#dlWMsbX$!ly1WU@8J24Vs*M5XDASjAH6JvwaLz{CKs#r3Tj73l_9H`e(-sbcu;o!QC{?`(#0fJbE0f}kp%JO?O{2coky3MYjVz$lkiY;(0-OX-gd;F& z)>0C{FdbRGMATKu2FwMZ3bMsaCpYyxbY-)IC4i-dYFpVXh81gejEFX93PE6F5m>Pe zw>QAL-gR_w3_-El*U?fG9BJ1@4j6 zArRB^BB*jjPWeFDj|`m)KZMoANzP+A@|zDCcMyqJ)E>YtXwT{p{f1B=h9>Sc;|>_5 zuq!@79K@J5E!s7?RR|Si;zahrR?g1MiQt6vegN-s2!<|PS8z_OC zqK=Te*Z?L+$kT`x1WjygAPj%8c7w2=om&cD7|R`CcxO0@fPg|qmBlZ3v2fTiP*}B$AZb|i7CjQYd)as+oDQ3RxfQ)!ZrQ-OX47RqFdL_jd+f{=0E5p>)C=G@e@*b*6h7UxDF z9DshT&0^=~z}Sad#!TA!cE9`E=jtF*e#_XIK(~^Fe%XpfqPg}XoGEC|Lrm!(&dPsv- zebOAlKU(B~fK{cmd1w&1N@LkiLLyIA*U!$K3OX^BiZP`lF%%O+l2~rVLK&9+8+|WD zmj~*5^k=PqmXwLUr(gyZaQg~3WEx}uMdY1{dpRA*7;6ffu%w^^QRiI24GOWMC8&t4 zE75>Zj56sk!Ho9{qjHg1)O$oRa#OVEU?#RWhgJ_-ts-+HI$YGC$>mV^K>`|!#f-sN z!dSZx?=KZg1+pb{p-4T>BK1_tMa46jf^P-+N4%QFwBlU30+JH5m&I{K^b%AEgF-PB z3@d|Z!~3+>aU>s9FvPe_f#mgN&LI;h3+)c7rzq6J+cJd|%M=i8Or!vEVXH(0(o8jp zXc_@IiP(v(=dT0d;C{qbrDEa88#vhhkl{fyat6!4l(wjeAP__J#ksLGM(yNijM|?o zJ**-Hh550Bk0+ZHW52cxLzEN7=y}+o0vc*jTs8(iXaLw~C`U^UM$u4ZJU|U4DPi%0 z$qC@SYWrmBcO}aOKhUfg3}xXi7}f7UtQDgB1tP4w=>R@^TOq zt5JTDFO)2UQp6^3R6O#IKZB; z2`;aNV2(jBTY)U;qBM(qCC&~!rql6wx;9N_kyJ@M6;BB)85U*(4rV8@xnpfj0Ui$A zu0`c$bOt1`FsVEYRK*#!5CH(}UE#+`R&bM0EFK~&I;BbQt~~5Pip4_^LAsfx*8-7f zNl-S(%eVO9qH9t%X&dwHs0%WMuyE&*(%E4RsgK&6Vh*rSi@+5NAaG;m8oQkH<5IVw z_3KfMH zsRfuIItmkm*(yv+(S9SHW)Ka+6NoDC@O_tSJnZkJ+z>^JYSe}IfV(wT|QDKl% zF}$L%l$7cAPl#-{On=(IU`63d013>e~Kt$xs6- z4owwp^1@6N=ZX9j)j_w@A^3o@-so4Y63exgk(wtOFp4lgXD@3IH`yLfu?-K9akd!mg1- z8AJeTtKxXTmJtEQA)O~hv}O~=gve6s(TCEzp?P!}UP4LeCg2YdV=JIwC_|-Swg4ue zaxy^-FhXj`kI^!K>%A`iO-8|f4p(=ZEZ9j3 zrT4VzqI_U3pL&#xYIz)3BIZE}TQft#>tgaC-Oc^DB&d#U@ja9lW0x%4os1$>as;;z zp00p4mnaEMiE#BVr&)*)AK?TrjBJ~$z%29G#Q$u@wzzC;*l5q=BU>DhF~mie_8UoOkXsKr@3kffJ@~vc zRpegqREl13iACj}k$RXG6$Oden-Mm)l@-qu z=a7hXCY6{kP(7R@B(Gt=h@8RzAT7`(IhLYI=oi0%ewks|Y3LV=HDXabpa)zDL(y)q zS=?*WEO(^Fl%!{)y&BI}IP~S%jDl&2@{~l9TaFYAPG3?IqOFAS44Uk=j>NptSk?3z z09{mTY&ybBu-KV!Pf%D-zcr4ayEsTV4m~uMNaAz@J=VhIw1ak~N10|LOc4t&#*&%2 zIw?0@#wd;n7vb2lmf`|XU_{23Ny0h+P9@=zC>X z&>@nKc67!wDH|PZ_kw-}lLDQBM@~A1M`>nk#Ufv3)WqDeaD;r3NMJfnB{wUPN=e|a zP{7HJG3~w~TsDh;5cq6RLdD|RiF;RU+i*x}(-G|Q%^FO!T=sMj<1eyJ7y&?!^A?LU z!Fb^|L#fA+QTTk2g1-SODDVj;-#lbZian1XfT;$#xHDHh2w#aa>$V61bRZfePpJQB zte||8W9RQ6Qi}obR;}=~5BcPmJ`svBGb`Z^>1TpmDW!~N5;)960tSOABc8(eBA;vo zf~#qNz$Vw&A4e>s0GuIlZtDwxUAS=-7-tckZ07>FxgmRMH*{HM57UU7GGgo$_yokQ zdy*+DjNd?DS4#(&b;vXe2P7JXUI^U{x(6%r-0T{1OtxH52)V5-3|SANflpOTML|p!?a0?zqhhjL&n0Syb;H5pLS`*h`EumcO`)hc{79S{(5FkdN=yiZ>%vcT7ewb4gSF+m>lJ6htk3=1K8zAt15$;8 zA!3L`idINCIEZw6T*P58YW~|kj3Vs|J4b}}+rpwtyWQyD_F)XA2pzdGTvASLCTdbf zYa*v9IX`49lIbW6OY(s`31siF4N?+IeG;WxG8RizAjMZw*~m6;GkGVrmO!O1Vt@%g zs_ZcDMOkaYzc6XaFp56&WR{vBGSx;U1huj>eb?YpU|cM zKisv^jutb@l}@#am4^vLoUzv!!>5v(;A(7JV8nuDQ!OStue+FX(Na#ErA{_QK$XDy zrr}}*h-JISEWo53v!=Ox8XEwX6CgRn4vq_D2ggNx%3gW3+*F!OFc!IkV@qfUM`Nuc zb|qGm@ub}Djo4}c^<2z^1jXGOV=>K8J|Dp&nalz9eVz&rBivi4h}VBjtkh6{p;5uZ zK@@;%RnVcWX@dcOkWm$LSj^y>f=AXIQX{YJ}fc^L?x?KnIOMKRw%$#bKTJbZ|3icg+JeiYjYB(bk< zGG6+m>EiQe3lD2;;azwbJgbdx`_99=Phu&Ig_oq`WRnD1xsWW*Q$Y~5S`1$y17mlF z3}1{0zBVbI;)dP|amrPONSnl$6LqN9VMfUYPi5XDDhQv&W|lzz1EH1ZBnJSTme7=n z32vqpnSYw$A_EKl8mPup9os5da09|2pdCC*88Crb3#GD5jSv1znBp#7peD;7mSW+% zFenpxg?yO5?WVVQ1=2-G+5cbMO>ZMO+V1*@cmbXu4UUYYlH5Tt1u>a|nB*?E9(TF5 zE3Awx<1-dAT?ioKaW@CqJ5G}$z!Q|DrcT*VXL0j9xYbNz@SV{N3ZM|7l4S~Bs>Hqj zukM^D&+Fg{e?Fat+RN2~#XY?o(id|SN9N4#bSPC5c;F&pZ01ptgFdeV(^C*zMs#t) z#EcN9hYm;<^v-?nh5ccHe{Y zWHAoZXeh!hJJ3nU(=71;^~s(|Vz;7Im$9b)_{@ zl&G2#6~-0zaHe)<8Ch{3tiffg>>Nl%LserDCN`AxNOA>E1W~kNU9Z&HqFuNd8FGdw z3~~;V*-8*mFbV0}r!bW6PFC>x!@CF8V4(OXfB*nt6&bdxOs0lIcu=aFjE;Lv>`O#= zP1rbrZL1jx!*b?_RpG&Gjj`JMFw~tuA9)5 zS^7>iq69=9v4IV8S3+h=9SAlG4pm*0Y#%5~W+5S23u3kpl&}t~n8Z?$8D%yfXmNW) zN==vGCoCIFt3mAEfqWio+17zcc!5V+4FZ8+XNU95f0!ilE?>kIEMokD>xy2SnXLmW zLW;R1pr^2S=?E%93cvsi^i5ET@fk-p<#5c!1_Z1L5tpNsY)}w#0NEU}Dg^O1aD?m} z=c+;ugL-H|W8Xq-GKnWHMe)QXQ#n8(XK5IZl@VZo4@<*ChsNXPvFasTV!BNqNI3E^ z9>{EhQ?f-Mvu|WgBg%!SFC7j=oOPYlxi75+5s^V1l!)5>#i6eX8|+m(H&E6VAC=4gf$lNhRa8a;8qpXTV1a1xk!2g&sNB{${{lXz#MR+(7%Am0mO8 z{$_U4xx*PPR>DSWf-fuEfV={ptgm0g9Bq`zMZXJiw@AQf0@llMHo?(Y!Y3twg(o~0 zoPR#yc{&h7LPLk(S{ljC5c&9LY#1C_w&DDh?qthnO|t?7PjG|efSp8prn<5!6(p#A zuu(t#Mu{BZ`E8)37z&hS|F;n%h7KK3sKGE&r4S#eu978%00b7AK*uMdmBUEwEE8z0 z9?X|9HT`YhRIZ-9OJ5aPlX4cJj8v}50ayY!iD{s!Eo?{FIU z-`t~5sHNmh#t(TXG|ZK#fkH(fe5sIl1*cO0m=z3?5Cy%vC$ezwJ2Z7R!Bi?;J>Zg1j2qdCRkN*U zasTBaOg4H1njqgA(7?~2UlEfQ!a|%8UR(-3=LNa*A?qtpw~Fd>$VEJEb2*&PSejrK zilVu~&+Xw6jb>0IDITZPjn$<~Wn-q`8}LCwHAN~$HLLYLAsETu#2PuxL0h0-sa7H5 zV-yGA^Ss;&#AG7Lc%=MUU~UmJs@>fYp#9NDsUA`Y$u}q*ca07na0WsyO@Q-DuGqA_Ce<@ zg@^f;-v;kt=V12@{Md817aj%#fy#gN%m5TN{|Yfm*otL5>Ti4C{cR7ta5dEbtv&Fp z&V-z-mfZ^qYu;Fg61*9DemcQG!CB(w~Ik(TP-Ai%G-6VM;RS3r67e8&ZSF zY|te}W=%xpYRuz$v6%K^Tj09l2@jFgqAX$LbsIYw?*3W@^S`m=YRI;OT`nT3BvF}2 zQb*9%L39$<^8_5w-h(+K)hZ?m$wNw~Ftm8`vaz~QPlY&~0ND-0SnRECRx}OLkx5$d zDcrQ}Xkkisc<2AnJXAt^+g(7|RWbqB2$~R^=WT%o!eS+O^vR_3S0~TF9lb!dP(LaH zyiAolAkwjoF>gVZ$(xE8VD>h?0HCAk6ImB-|vkj5iPz*Fj-0p|KRR zeN;rKFH}KP%9BlbdR?))2+)_KE;drC|6D$Un>U)uFISgA$CgI3TY+fu$LzC4(c?!= zBSL(PmL`0zwnFc5_RicCj3M;S7+ey$M(N!y)Zvb)W?f9mLI68hB7e*Sx(ug-fr{9{ zA(iqa6}vIDz)<%HBn7bx2sE;1`(}9bC_Ynmq&bD{Y0J-b<`7bWH7CzTi46x|(?PAT zmPL$afg7Ty%x9UAbI+B2)1OozvRV<<;IOux>4zb+4l&LxzDILy-BE!sldA!itFe61 zT8aZ*4hISai+IkYF%PQ}pc5{w!v3!;(`OvQ?)l2_prD2kS=xzcwE$~OjmC3lP@m`d zSYIiV;dw`S3(;y-5JL?h(!6L5c~_WY5L`&^!fSn9XiNFrr-br~d2>7v6ow2QQD(B` zbqhdh$e^?21YKqx{5F)mSXX7V$RYM z8}tZVdCzgUsZJ-);vzbNNl%Is8GJ8d0FgDMLp@?S! zi}a|3v!3xJnBwA@(At>{;DViBOM=tj4EK*B;msjM92=_{52i7BZdL$Teu;pBh9g?j z;GU~B1DjWA7`uJgmLsUBNcX{TM(jQf)MFzLlquJw;zQu^*O{&4R}QVt_#!;&awpka z{oQhJkgnK$xHos^rj8lIz|@$AF%9GJoIDfv88Uo`h-FtHi`6t_(@1r7ZEV=Y8ph;X zm`S6;1p%Xu0FoJTAbyL0npyfyg(fd591^tr@c*=TZa;Qi_kG{ze!tCyLvlz_)aVjf zmMxMR*^y-D#JO1{JMPKi}_ft-bd-bLK|NfP(gnX66ia_TFp#*6)60ss$|v`8ER& zlm7c-kt-iF|H=@G=wNr)>pmd$K8=Z|UE)aH7uROO(Ue+I1fyomguABbAR2vXsg+vF zte5)VVUzg_+Dm>1)-4;&{x&h!R4N8N)h2{f?1L=4f-56`W|AzTGYD; zo_GBo@%_Z-!NjT``-qj4g(tEP?EzAbKF={SM;VS96vcOc&VUw?oH!_eW+p-AkQ9hF9uKwZ;gAL6BA`iiugCH4 zQ*pz^QYn-{#AHtd%#R~^CDcg)u0qbc_dOAsS?jy)&t$F>}{lcrW`PwK)bT(8sA zr1(d%*XK#)=YQax2gf>SDeZRBJ=tvQVULhX=rJ$ogSaE{$e;Sjca}4*tcpAk!S)u& z4{GuYvq9(4_{;BpODR_?E8CV`gzz9LK_Y*q^Q3{FMdt!dP?epo2RL1MkbX!M^OBV! z4^SNzqe79)hLRy7<_BTpy3)4UN!Aua%15M3PF;amOg0co>_b)Ms3uAJNKa*AGCKii zwkFaZ%(WutezR7*l46NQ%CdF5giIZy(0e)GRx}pR-m7Vt8bfofq4ih#CABsjz|DoL zEm*gKk6fzOd`6BSx)#sW`P5&Te4G( zO&mB&&B{A03IZckv!3Ku_7HCue{;!L3_?6Sy__!3Lvwf*nD+bEVi9t~-iTu6_Q@*Z zHb8AynAaAR6~u5@i^A5umzCU7RAXN~7C3bRX3f@!0qsKoGhpC03lnnb#Fe#ioxp~5 z#K=B6mL4en?o4$Yn{SIKYZE?i5x#BmIPIi+)HIBX%o$3`p}P!YnuF45 z&Jf6X&N)M;-QpZE45>6?a*}%>Nn&{teR_{D;EVU#wAo*N6Mtm_Fl1^li0oF*tD{bq zItkS|Nyo{0Rh<()S4PP1FsCu%0z$`xk>CB6?;QB(F1zqS%o^RYcnH2<^V`lt-Tj7q z11o$Myq7rF7sjSM53CxM-_s{oG}w_clTg`ZVd@ca$4&NGx*zuppyb;%zr$kFYGrrM zidUDnJjRQ?EBu$%>-Rl^<-Ohww~wg>aFgAdJ_M%0+$z;&j(z@xgRPg z>YCww5<~{rB?tV)X&w1?h;aW%lW!XV&r`qBe=j0=K5Fn&ucZrJ0O zmOloMI=(k_B#r(^CJEaG1)5viHMMc{S-_DAmi`)OPb@r4{GLhVH_Xty$>FbqRgpdEu-q z&kH|WQYYyK8w+V|95CAwe|FZWblGg-nwyKEt=fh+)Mcd7BftxsqeB|OkLU+a-SlRC zf`$klA9PJaqKUPyjAksY*aeJMa(GRr09UnQ7%;09i6^}x0(tQdHZb(R{Hj6K>qX+C z_lz~7k4&=(nPzYS)@v1^*K;crU^$sMI?5?Gn-^KH*A}8$f*?Uy^kp4F?z+%~GHmlY z^;;y)db2JxDiV*XIi#aE`f(nc`aD_d0?BB(4lt6os_cq<&+rzlO`MhZPkyfJq*=7v z(Y*hHej-Rr+=LTLeR5(Mq5GS-yu&)4kBZMMU(yLR+;`sY1S9gv+q7y1VI`H!M-Ho! z8dlMdwSP{uWQe7!!djSpv;o6aL83?tI}nc9V6wRclL&686LKzfO>38CWlw4Lj5Gly zeZp}JjH2}g`%%v~r=Q_P+%&gNN`-huU<+GA^91vQBb!MD^Jf9#8thp%h-46UNM%9H z$e>J<>s~9zD>UH4SeWE1q#@{CCVpsuYS#4S&mGBW4M6tkjF9Ita3PkH+wGm>N|%yE z@f1=#XIYFD=wMfatWqv0-K zX1~$=9`2tjI8dc|zpl?aH$mrFo$YQAA)MbLz|k`&UBdio5KZ-CJRi*%!3IYwkY@5j zF)V4=Ps%d~+)!(A8+`%-f}`YQ;~xFdgi}0Ud`p}Q97A7(GJKBV8&m|*6f1B51I{g3 zv&tM(FMi9&2oebe!Ik;eoZ2xc%ILOs~ zlRtDe2jU*dxtaiD<}S-y5CLDrGF6C{j7V8uQdC5r;CF^r#Hh%QC3&k4vu9U|V|)ag z-{=e=Nh2xQ*%w}_!wOQZI|N!No2;hh6p)f!@>>1}7#$c4v7)8z6~7f{_9||-HhGD) zNxN1i)V|tGFJ|uYHqYF>HsYKPeObHFsmMM zd}#twlq8l`xa-fyj-KIP!6G(97pEMriF_+|ks@FR@u4QmKk-w?G6xm-GNO2O!VEko zR!do36eHS|=u9{kXGm}7o)NF4bwSAy8*Mbg-e*4(ggDL&jKks1h(@hGU5 zX!B^;+d<9VzqduVw)W9tLh``1d)v%mYClYWFz#Ufu$a$p%-P+~q#Jb~|6q3_oFcvc zh3B7p*3lGZD3k$+t?r^*i>HLff2j(m1epur7I&m09CU)>c_(b#y~D|hs*Hhd7MV;C z9XKc`G)!l%Uy>YBNP#OyXBdjwZWQWrKchcH6NJ zF=Ow(yY^kwV~^`Q-1f2ZkJW2$`CRkbS6o)Q#(wed15asHj(4t)vcQbK$ryfs@2xSW zQkP6^=Jcr{BS2PI6{88!J(;wbt&$kToAs+3V=;>JUyj#)J`Wt6@6N4&gybcf;y8tm z2mu4u9p3pINT@j;Lbp)Br6!9k6YfQff`0<7RD=v}Rr=L7)$^&F@f#Ew47x*t{|wVD z@Z9%_X!M!WH>Svd;4zL0^BTsx<4j}65G@5dwv6|eZ^qR$ynBMnnFHoF0atdMzrLdF zc+X&_X$q?1OWvGA_6?4Ty1ZTX#h$}PfQ(dUBrPmkcfpNjh57{J$0T);sY5zJd62Aa z*%rUiP)31X^FBI``YZBCnYuD~rQOJVAo=W2khz^GH&LtnJ>mUV+5|H1r2Egi*wVGT zYxauo3_XcOC?LJU0Uj>@zGxrGG=}(xBnG#-4>*BL?5UCa^?RcD80lfUcZ~Ihdjy|m zmWsuLRl*^FD#I?A0RNJR-$-Rhi<_ywk6#9{{> z8rF1tXoup|dG$UZkcyWibp5E-Fm_hlT7D)|g^kG|4G!u|$}p%(%7|D3z$!r%axApQ zZF!z>RKQ0`w33gWY$}D>flI|l@mUs|EFSi$o#y?4rqZ8`k=kVIFH*$a$HM~h`2 z(*hAW8ha-HrIPATh{}CV#LnGC_;AhK9^9v{yXQMgHh+BnjEX0>kkimRhd|?6M)y3L zqaUR-FpAQo>?yO7*d@GcbhMaFTB8wqwUcsgv{#%@RY1N>8raH714fph_@H_dpgiCm*0C4k_z-v}nztvNB(4 zI!z7+JHj4rjhc>z?Js}K*H1eW3})Scq45{G!#m5*MA1SyH1_pVIjU4hpr zR$J4*LkGt$mpN{?iZnaf>1ZRMiKHbymO8EU@UHyglBrTXilrEi^=P}bRl^JNP$-0U zMXMr@?e^qoXEAFzE}m*p4*JK9qS7L&9qn%YXisA->3FqZzV#~v)iK_(zK>s7{*t}$ z!~Te7%(!1P;cKQcuyz-Z_v+5@1!iA&x4SsnX)G4k7CA%k$@UZ$QP@K9Q!-cI>_Pz_ zD~0J~YTM#%?N!&kH8Wm0j-SMQFeb9#;2L(QIz}@X4Z5S1dENVVsbd3lI)jI^aj=tw zGFSTbBGmN)rr?PobsAE#Ko-8FbY)Pkp9`m9Xan!Yq9VlwXM zsY_@ftXfeCjlDFnlyB4E4}vFGuU=gl2Oj1p2IySIKC5%H2LV#6^?Cv8%>{az6bF67 zfD(lLx7RJ$`u|ATmkzNC=k4O{!3|_hL2DZYf@7=YaFIZ@2CAqtJ~!e^BfL2Yo?1S z5^}>7juoCFQ`LR} zVgUe!vLKw!{hU>5p}))M7WE!FJN0Pec_gc7pgYquqmPfrBBx?j&JPMC$=0WLtUwKj zIAP8NC&sU0A|B+Jn#U%}2M{j-V|D-P_#(;qfO^_S^8roZQNUUaS5%K^NiiD17G(S) zJy}SrWhc!wJ83pSrLoHr5RNx(JOn4uL6yp%2wX>j#{?A8Us@dpR8l>V2`O}rIwzL& zG1WN6w)>B%|tmVWyr|UU6azmX{`Tx$Sb0Po=#&Yu6JM)RCb5dMTS>rkz9T9W| zy{AH_>`8hb$oyDvLkO0>^dG?HQbB^eF-QB1I7U@P$*21reB$T$IH#0Jo1!oB zE5&{zn1J|F-HAY;5koR)=;a1DKwlgzL`SoSI;~b+oKBjj47$3V)2XB!`WtkFdJ?1z zZidXv{vz`i4yL3TO9S!R^G`mt!dYr2iPzZIcb>_oCtZE6$*1Twa^i|t@MT7hS}nf8 zpKL3XFmVZ31UMKCqQugZ4k0eKs^4huXU)=O&1se{=a6UV5{nO2Ool*zw;wqa$=a?d z*(~I^6d3hfG1~`h7t3*DWrsmVAr-VfR-%YRWqgXy)Ltb{v$E(7D~jqu&Rw#4`gkjp zWx@K@iFVi{{ZP!-45#mMy8f(B{)@YNjEG}1a|&=!L*{7AokR796-<_0~?-x)QV(|n)IQD<|E$DI*^i57bfx!%XR0lnzAyaGmOg~IjQcDXca>T zrzU}$hnp7Xs*w5uReLC6Fj>SR*R()or|cX5o6|HMj*#nEAy-1N?J?^9T2tNqdo!sw zLN%)ub1GkjVe`FtN*~D%C(!^NvU#mIO5JnTK`JN$z8YM`GP877mZhPaEIZCtmCT8= z(^wccSV2i*xV_&q&&J)}?Hc7;gzj91u)lm!A0?rL3dA!$OMbw5J6jnkVlymXXeqT; z^+S$C3LzaUlOn)7u!GgFV1s(k{6AAC#R=8wq}1CG$=jp~s)2JFtjZ^LD{0z>V4+b(w~DcH109uSaG$B^GPzLg50ISQo+Vc0N;qsRu5Q-?WVppuG1 zU^U?tb(U#Xhq%M4MkKEjaDj=K)dQP@-BW6#EVpFy3AY?!PIOY^;kCMgn#5I+;mU^u)Q77cFCbSrZkC+mqHr=@G*EswT=j zG}lC3zhF&NzFqX@4H}gNTGd3c?~kP>s(O+ZUvYynJ`V$U!kVbKwFtjvl~$Flsy?^w znrfqX(0MANpddMw>J?E|ohT&s&RTRT@Zl%6L~Bdk~CDa{Q2CVjw9 zOx|J6CTgKxbH`K?G`rN2xQd=0kr%6kqH_^=DkWQzSu$VmYcSN_BwNSwG92HPu8LDE@FcmdIndW*{aV=)+ zdd<0}Ht_76^S`-<_hM5UL<4x4IMF>-$y%OcNGY7x8_-gPI$4xFRY<4qpFE$5Blb@c zOX2EGzQ{6l-0Hq;7CE+R^4cHqVl3waq{fk(GGmmMn6TkDo>=Q z(yw*7=CNg=QZ@G+cW?@4tqmOSs$D`0UAxxKrd)y~gMu1q4GNy-RcPOvr#{#B%~H?` zn$3s+-8ggaRxN=!RA+sjEz3otn#VNtIh{S;v~5uxct~0x_JwNI)C5EQO<940duY2k zI#Aa@)N>{Ei`zBE?E-PWz$}}W{FHL(m$Z#q-vqG#>}nKO>4w&bQhli) zY;DIlA6#6@4~MbQlZlhXQ(;v>+LQk!+B}>7Q`fwcsxNuIvE6o$l0$e3Ij^y6Gjkb0 ztfRCifcu`hn?d>%W%SjE2UDMVLD4;M*L-K?ky$qmio0*=UPlY>5v@R!rayNvlh`}5>;({=_&(6JWE z&TW=6&%P5}wY=9~jIiFl$w8xffJY)Iov<*)-i?BG+%nkQ^WGnq6dl@-XPE8iq>$!e?z# zvPA8mx21hdXeOuwm!%C^8lIZ~CknIGXUry|4(inE`i8FS%TI&X015R6Ff4?8-_aSG z(s`44j5PWi#r@@5Se6iPZ`dLOQ;pe}T%baM8I=Rc4R0=Ex+b8Ma|V-hG)0S?_YkS^ zdcMDp`QrhCHCHV~FbFV+8pMS3t7ZeZ_c{5E>IhS-oWV2(9E69^_YVyYp&!8zH})hv#ejMsFD8P?ltjJccxwJi9`uu2s<3I#Vx4N5Y;C3K6PY;1b$`3a)6AjrxN`^)D+p&o zMHL9ioTwpe@3k73@E8PT%I&tX1_l-+uPzEedFaCGbmZCM*8NxNUrxIB>Gdy>W>KTy zy@GxjWT1glGFa&HL?ca18Jt^3PzBhc5k({cN_p*eQCrt|ioqr=oefCDimx2XqXnsnO{&vkj-MXFj{n8YD5TCn2TRaFaOni1fQ52JJ@H(rI- zkJuui>RI4oBs|sXT6o+k)hvhUJiM#enc0kXq!^8Ub1;(biR++0 zD|&0+3`jzrxr(ey;}LA(qgIZ{6k&X56|_~?UByMw-tRan1mf$~Rwoq<0OM5?DQ`)_ zc+^gwHz?f8F3Psl5(y<54c$5oo6A$V=-8IJb-*Mz9FddsJi^nRwpbh8RN3-z zU{ccCfpW`sk#nv)8vRDsM0!@V$-Pc9IaDE?n^2eMjqTQdI7ZXLCXtImX%_88yG+k7 z>l&iAv^9Z2m6a!H924LgjfRXWa-)VPNe)?GIMgV2Vu%W*@}!lot$iKF%IX}@ zF?7&<4^`CzFI;|}ftxrP>-F%Wivf2%%P4>=GI0KLUSjxU#KI6SnYO_+`6ZQ?fm@F5 z4)76F_?|+4DFN1tqLF`O7o3!|n2$I7%c8IN;X4j-OWUuW@1@Caj)Y~Yvu4T za~Oiqx;+jFb0Sm5S+AQDWFn#qTVQq5wG^4v1es8Cqt2ZrDfNBRDy4er``9{*>Rho}w7EB-$r0G9KCed+v38jtUd>#9Ui~NA$^ROVG4aE`jA$ zl~5odd%U};urqJh)s!X)P#)w1ByK zwco$$;*zREwgfUU`J|^ryrM|D&|_6HoYr z)R&l^PO2}-088Tag!+=>pE_TCiA)oGBe2a+ZhgsWR5tvFIkJs`L1QQBbtobzA!WNc z#i-m)Mx}k(0_bA_7=NUvEhJq+ia6N})lAm4>YD4`YV`oyFcmRUbz>?b*=8qAMN}Uv z1eh>ZK?#Hyf1EKDv5G~_ihmH+?M;$iiN<`0Wz}v`3x%>(&@)(VK+6QlXW}!OHFVO4 z-dV;BnAA9=fxCp2#TOoBT&UPpvLK`G2uOs%6+Tpm%9h4iYl`+B9s_gon#vEIuqN4^ za(mU*WLzaT3`bYiN1(PLlCgrN&uZTEl_7#35dB*`a9b3vgR8jQ3Ud8X8cPYDief|pbS zo1g|efM3Nu?QK8JW@eZ`mzFAwKhP*Cyh_T4EienpXt0c|vU;>C`(IgLHvQ=M*~Z>rP!H78DW>Ob*Rr^@m= zcjeKos`7|^M3Sj8OhKG9VW}T8p5n-f(ZFXE1xlq)h%Kv>74A76eEp941hKwWM@ebH);X`Ty%${ZF&`4R>}ilWUhl8r+C z+h9~iMMT(Qw`ES5nO>aVNiaOUhiwJ1>&1T$PWxp|)zl=eeq++;PYB~os5wxxQ^NOV zPLn|cRj|`3@bnc0J06d{kXPl09X(Vz^60j@EZ!{#bk^=Q8I0Bc^ny2|K^KR6y{PrZ zOA!ePysFhJ$O$*r7YNE%q)CX`C5C6ugATalefocO+}SQZ3`o4d@iM|lerHmax*t?$ z9Q@9$gG^j6`jaHj<8szVA+8kT&cu~JyP?5htlqLjL?u&L0y2oos}^F&u+(o#muq;w zVYUe8k)Hi(%n%B?s*A5tJW>GSv3{hr1%t^?%+>+?3qe+_P})y$5EV^ulF+QYMx^?> ze1=F==5D5KMh=R$ll?NHd&Zzvf;H&xzO+DEHPW|x%hQ!ciNwk}i0`@&SKNH#91?b( zGwQwP70x|?pVaAYLq>goS9Dl|aIc7{&sy1Nq-MjKpu=AU=GPG∓eYz|5P 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 + + + + diff --git a/OTRExporter/assets/objects/object_box/gChristmasGreenTreasureChestFrontTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gChristmasGreenTreasureChestFrontTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..03dac76dd9ad786e9ee25bd396521d206c97c6c3 GIT binary patch literal 2917 zcmV-r3!3zaP)sd^^WMI<`*yc2!+^}F8^Nv2N)TM??8KFSg8m(XD9p;eD`8fG zyIF`J2*WVL*o|}hJ}c{$S(%mR5fLupM4UKPeH$0r2NEhXE93inKfWRF{^N(9{@Gl; z#@}}wo}A#sVYe373TCEZ?8o%M8nkFFHu(%^82g|=?dT4O!fx&w=L(OR(9P%5JkDEKUzcCe8hw zoBf+K|0Zy2Jw++_{)ny}pXD65c2LB0_1KI`lPvHMDAzm!hYXMeWQqaY^@?gObk3)2 zGP5DR6wVM|N`u6zm4WXzHl&veZcs|tZymdJKv-XP?A8`jND|U^p7qrb?XI_~HUfK} zUPfhADOw>Z2?t`gCps0i&JvrjjOHH$PGsAG?iZabj@J zv%R&+saL?>) zM|VBpaO#+z?J?dI53ID6NjUt#=Y1h|z}zi5o3O-zP%uC8T-~;a^4wk?&~6L5dcmvf zDF)SzQq5;VY;P?}35Um;+f^|6^30%(q=J+WAG#3gGH4FqJHRT?2Y7C{c|T|SW{KSx zC_J}KjWLxxvVgUoa-vdK%zHsKQ=`Dlr(pm$(_y|raGl`m8Y&&!_im28EJ1lxX{pN{ zroFryrD2@!Z0k8#YLteXi;m-`1(O~;x+0&LV-f0{r<)4mpxeWGQ)6$0cVC^*T}@Fl zi<)*=t1!-Cq#RE@vArfAE9vuFM!WS?t8j#K9hsKJ!imaXY3=*wqXrZI;>`8rTFEq z-}0ZASL|OO^7a=WvE9Ds-+uO%zx$gBc3pAu@eO*?p~PXj&E2RHnj&a#vRTplAtguq zLF)(K#}T{`{^94Wn;S@dXSe)I{>@}M!k14ZKYzl$U1|FF_ z2lfuyIt-W(x1^#1XBE{{u{H4XU)}N3a>0w|o*(_eF}k{=T6#{aEvx$iqpEQb61MEo zbN#O6_-QdR`KF)wmuH5Xi{Wo^p&`}xj-Tu=;S(^|LbY=o>wC^Vzh*L5Y_A=3f|03y z)D4{_X_s>Wxyj?qpKnTOGcwS%P&?OKMfl@CIfj*GI<47nJa4`{!@3sJ-8Lg%I~!Lh z;rd7BWD@?Q5rP#|i+NfpR z6>a;!BeT-j=0nLYrwVO^?b`CU|9;8ss^hQ!ykc>vI9v+Gn$Ucl%hM^#1RwUns9hXV z;`{uG74za{h^uE`RAExmVPvajipfmz$wwtsshCa#^KIJPUUZM5JQqZipwwXU_rGaI zmRP-N)8INk)@kuMJ1kIr(=)mQjo@W4pL0Ex(tW$xmG0+27v{e_6=OM1Glm-kt3(hF zbYRN)EZ1-FdFMHLQl!Y%M$#b>JPo9MU3m6I6{c!E9&W7lp>pxsADWbj*!#Q z%(nB1J$XpQ#EIBsT8DZv%nnKo0iI);`l5M2Pzx zB$CpE{xX@VR9$mSW0vKpp^-MZAOo@mO-rut`yLh^<>G&31pZfwmk9!(ac9r z3X~24#Ik(ytUznw;_C)e2v0sMS$*B&oagvSfpebQzBw&UO`03Rycrmn9P=?--fNSF zj6%44-6oS?zHWmSWta^D2^G#*y=u^fusk<-57%$Q@4N@>P+M}$5nJB1KDFZr{?Ozx z%A;FcVpRPo^+b`#Ea!YutZO|v<|DSeYeVXklc2eGp~6LzKmY9tXFcy{%s z+45M+l@?a78lJo;Q@@Cp7`e&w#&!I(Kos1(@0iXN&IaxwE;%{o2ifwNVv#S1b3i1s zb-xLB*_4#ejgdI&p1t4{rQ& z@K}QJ!96}dJHz}STi&&P#1DKEc#AX}ca$cEBy1^2^d z6f0`ZF*-MFRXL_97KbB#AhP9GZ`%L!Yr8ZhFY-e4pcJoOHOXX;FD`j-aTypC8BJ3I z!xAaI@x>+Nm?O6Q@Bf1Ths*2c(dBiHIbzGv-_6x=jyYn>ao>#qIp+TWqA@z1pUZ>! P00000NkvXXu0mjfOx?9X literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_box/gChristmasGreenTreasureChestSideAndTopTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gChristmasGreenTreasureChestSideAndTopTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..ee03c1343a034c2477862518d38671d46b631fab GIT binary patch literal 1111 zcmV-d1gQIoP)_;GNYzstwv5As3DA*pgHDy<)qc4i3VzyS%)jx47#7kPI%Xrh5` z!1TxOzZll%*WVuT>UF}^wSX94A+P|4Bcqgd45sxjas4YoAO?u)=VdlnKaMgRRQqQw z*3KqMsRyP0r#jE~;wKx&AwX6L)ZCAkkDhJSC(|IKE^^qQ=NFTcbFF69J|M zEfu_Fz0~iv;QmM^VEroDRqhvHgJgo1`Xcd2`CjG@?me-scLbM20HJ}i5Rx3Hen1Wl z-0SF+69Kn>&ou~uw;%F%)!QjQ-7M|h%65&Lw1nFh0}E;fWUUM$)G}~-=gFEHR1M`^ z>*`tirM=@F+=0#N#S^qs#7ro&!PLN++Y4UrvRIQ?11~nv93>Vrj!JXJSb!wQJajHG zwM?GUw|~}BDeuf!mzbYfOVTo5%i3ft;gY-pGgo~}uECLt;Tzl6BATxoS3Oaq;NUHK0QXC$97@1a!B02KkhufAd7rCMG2!mx zJlL%_+V#8ClJK(n@BS=vuws+31T27^FTuULT~-lX0%KS&;!V13#CfXV%(cG~gJX|k znJW19*3}g+!CfVOS!TKzR$fX~yGSTarzL#BQ0>8NbkN;;Z;i!ikGenKYnvsmEH6VU zXAN2MMX>m^x~+q13-0R4xt^muFKOR4n%VR9-hwZ)Asv1^hODeD zA*KMyH!PKV>0-UzPxk8Ve&I3)fHXfm>^7W)F3-oV?bd8-Nu~#F02T&QY}{?ue#Q() zcriMa2(|goZS(0(zll-BRCv$c^%ke@=siJLT8! z(uH>{v*GKhv|$$6Nt|so6!lg6($#>PwQJwIyscl<+3@#cy(4(v2d-pyWX76qDKCpX zDKXW$#+$A67TPtp=7elKZmj@5- literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_box/gChristmasRedTreasureChestFrontTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gChristmasRedTreasureChestFrontTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..0ea4b9fe2c276cf1875acea68b032c0a36f746ba GIT binary patch literal 3518 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 Hu0mjfR4h1* literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_box/gGoldTreasureChestFrontTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gGoldTreasureChestFrontTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..d63811e9c8e2499857682632fb907852a7c4cdfa GIT binary patch literal 4727 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 FV1jt(8%O{E literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_box/gGoldTreasureChestSideAndTopTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gGoldTreasureChestSideAndTopTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..b2e914b4949ab3dfd798cea12a6aa6b7861c64f6 GIT binary patch literal 2338 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 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_box/gKeyTreasureChestSideAndTopTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gKeyTreasureChestSideAndTopTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..8d320f250aeadcc995df6067173f071c068f054f GIT binary patch literal 2184 zcmV;32zU31P)&9GPgm7`EeHGbx!s8a3)W;)wQKEft@VB1R&T%kwrH)f*6zbOcPDPv zj4>Ev&|0IE;^wKfQfsA@Le80-GbyDzakG|lrj)Y(Mgu6N_Mx@D6E|zO-?{yW$fu&# z`hTqf*xMT`rTFcyUI(QRsdyhigq#y1V64Gf%P-&j4WE2&HvRDrULlu^0u(UCh;t4^ z&_<(mL$KBgfO_Yhcf=U8huK=ouiy9u^I6By;Q={CrfFieT9I>M7)KtgFPNstTfh6m z9?I8VeVLpRi^ZJ7gB2I+hm2!HL>Q+L=K|Ima?U8F5fO~hj}Ta|*E~Ew=d({f$@hQs zGrsi9GdO4Q&VYb*7OfPiU@!E$A?fW#uzFPr%08G)*5FFRVpIvN+^gx z@DBCvyYI?ovtc|x=h@AM-=CiHh0{}xo_dNOeg75iogAUH1}fGVQc8pnxE@9>HkbVR zt>5wT3(v8f_jDnUVq|r&B7~0n_wRFjd_q6#n8uNu3o#~2$$((3ZwO2uf6TMnEyL4K z^VJVN;1AAm{JGDejY1pE#lp8Pm;2`Y*4MvA@Se-}66@ke9$@&_OA=X>{f{PD+8z;dzR)Y5P~P?La7C%G~4YpQZq3o7Rx0(_81Lt$SD(3L@7nd8E*~F?+}=so?^V`-LB({ zr>7jAp7O(2zs+j7;OyfwoOg&+taUi=&`P6}km7`M4(|faIS!T!Qi?e5nT8Q`}&h3X(A*M*mh3jFY)=El=m@-q!Xr&)b!EM6%&6_{xi=Tgr<#NV2wjair&|1-V z9mY8{aQhOSEGFO*dj*br5Y&J}jAhqH`zzl|yz9;k@B^Ln3aYQLa*L9R~3jwV) zKmX}#%=;d#C$__YwH9Lx)&~UP@_HcWOBCRItG~6D`Fy_HSkr|-6=}7X$~cZVZyBc% zjiT>bxpL0L7_ql$cDo%gN^^ZZP-{U%aL%>_m7<@`2+om9q2$bVd(AXW7^8_Pk#nI7 zo?#g1`koLvE-p40>(N@1a^dRwg2UATwN{k6RRXOwhX+gUog4v*7^nRS8+$222mok- zuJ4dqDYamX!TUhhYn-u!uE#oyRH2WFI1Q~Y0IM}u+bc>bjSFt;z zYbBS0Qs8WZuJ1Yop(;3Bt(am&D@{xh4U|%d(}=YWYYjfk$SF}uLv@-WT^I1)wG!-= zPuNSUNZnIy-N|Q)5$8Npi~!`Eu{#|pJCvGaYsPUxq@t9f)`q0_eqTC~c9=T|TwPre zoY`%jP)d<=CFP7UdfzAYwn7@qG)*|?n}UX>|4J#GGbp90Q^cCagg2!~DN}{JJ@EGr zKf*ar@RpP^&e?YIT(HLM63VR)^rP>bZAovhhjz-+`Xa>yyZwzZZF-#Dn-~%9PQjo4 z@>jm|((|kqbAX+W!!V45;4#J^B9vO8RmhrP4WD6YqWE_BUqA*2{V zDU{aK(vnQ6ojQcTG(}1&jN`aRU>pa!uBVjxX;b>@%P*oebUx5`JqpEQK1V4o2_f+B|NMv5V!=3#EEjXM(mYtN zG5Qm7{F5Jkj~p{8HusZj!DvfLiOuGcS>Mxl9m6m*OD%%$I)e9%(}Y4;Ef#z2(7Q%E zJXmpjc-Rc9RJ78J)2<}95m>L+oPOq*m?9$FA|1_>PdN^+4(t( z`HWKA{UA;gRiNtvo%ammNUens0@gbE*^F@*@A8229<41(Tci{~aeckT7)Qz(+1&wB zZobc1Llt&orr2JOh}tn`g({V+>k(@$DQBEDY_0}MsZ23ZYrb28;2a@1j5Z_$&>S5d z>=GGRt(F+w#;lgYxbuW5rDoZ3#%R-SL#Ev~wb58(Sg$wqoyS>2%7w3eGgpC0000< KMNUMnLSTY;0y@C} literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_box/gSkullTreasureChestFrontTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gSkullTreasureChestFrontTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..00f0b39e44bfeb54234336642b70db3e275bb799 GIT binary patch literal 4265 zcmV;a5LWMrP)ek z$**PCRmOj7H>bV#z5CuPm&@hYuGlGeiJiuVkjRM0A_yTAq5KOBiI@O?22v0M1VaXh z5i^7$MM{ED5{w80r3EK3?r`<*z1y5-cMSHvw`y<)=hdxy_F3y&-}=7q$fsU;Ejc$M z2z664364q86AD)`$opM69*U)5K=8MFhNe%=662$qDQAhLiw=wr#k)ToYp;rNl4{ z)OAe=`TF$q6cG>+AffKwy~}PlFikT6+s!uL)O%tKOw)u21RtoX3hzB2Xl(#t97n3E zqONOV40K(Gb2ClTl2T%^=!r1`i4Z*N^(9(sjL{6kfV}wPs|gXNX=Jmxq^@hOJ${{- zA|irwj%k{xs*0v*7>0q9lT$XE4Ruv9jAH>*q^=vraUdn=`;NMjeb08gWu7NK`J@FPgp61cC?z1EL=`kAVobycBINJKK#Gt^`Kd+F zN)bW;1W|(4iWn1b--$f)%+nYVA;yf}loB`36150@V;N^pN{JK`K17UG`A#AvgqRXS zK3GZ#r3|^V;C&$Yd{AvH)@roTRFy#~#oKqz2_a(IreT~WHk%Fp@_pXhX^c??B#=vx zZx&-INBQ%<3V9;rFc}~~EZ@mj?Ek0a@1joF?Y5Zni*t-NG;PC>1koBFA;p+WoFXa7 z0l5!9`yWe)5`^+Mq)bFUVA@{*S_zX6tcRH~ULX=N)>bSQ3sQ{SxPHcCs|BRoN2@gm z_z?3Grie8JKyYftgylgAiq4Z|={ zRTiZLr4V(_2%;& zt(IK5dd$_wS1gYf7-KBX&4|==ZOzfLBg8}qkq|sFLW&9J9r2NV?$IB{h|xNan93t6 z2e2t7A9(g=&!<0o#!D|fLDL^ocRh96W2+W(`__ka{Q|8tW}#6=kwPMoP}&}NAV!Px z;Q*|ZG7hZGpv?0f4>uMOw&TpnO7o>Ruk+lqCn#;u#-NQwsXUJ6m;d@_eBuj#&T4gv z5|7mitA!8*Q3|6Kg5TB;_YnqEJTBR28##)Yekh7GpFi<=;vvGK`MbKhyB*ub*aY zNPZxcLaB@w5X_6e`4>EX?Fr`D5p1T`nDUwpKH!}{Y(x>k`;Z|i8IXhaRMU)-jDCQN z^}w}j6EECsS*=<`6iO-70jLU5nKR8}SFbV84(~lYokz3IB9n&gkIZiXPb5!-nOV3}S>z5c?p-qLc6~-@15T4ij7 z;4sd4rfEb(vAx)`o#y@id1Ql>l`MrJ3+3)NsVb{BmJxmJ+Jb(0O4amKb%U)Nl&s{!g1rJr+M@DKL-T7pD>H%g3V@wHil)_uxM+#rXr?@)`l1YF=f7w zF(JTiobz}J%r2m{E~A!)fbdYj&V|ntK6Z||8<_XjH z9Wh0=+bz~4#wnm&U=TqUWGmTaAy6|P<=sF`yz|ZlPd{^th~oHU0ZK>_ zL@QqS+|xXD;{@-fi~(yaLWnFDOBgOm(c{aB-9v3vMlRu|IWLogICv=40BWQ7?)To~ zi@)&{b({Fi=ReMWe*c$DTgS;+$D3bym6OvNKko=`!hH162h?@V$;la}vK$>P4h*;7 zdLe|&etV%&RM1}hW!IDUM5Pt~_ST2of3Trlc6{k~U>}d%wKR=RSXfS6}-W!4G(! z>EHb9pZ}N_-uOCmKeMca5L20ZK`Fr)g;s)>h}Mz?ssfA7`af`B&PwMhbUyOk@4d_K zoz*Nw^TwMuncw(0Rh0-~!nqmmX1t&CF4)U#9~0jDgTxLY9^{xPAr+&MTOCoNv9&6D z0%A;r5V&$;`RR`?_|k9fP@-wuCC#xVc#ofEf_I0HdEt$(bN$JuNC`SS(zV$QJLe9w zul6^M#Ty8S7-AtOT~@0L0j2Wl{;!|h=G}Kc#&gdZ&L522x##%U^#x6xaPyc!IM0}? zk6)*<71KO%_ueJrFfzLUDPfGl7?brMxizkA$^Fx&1U>|;HCU}MWlL5G-g@i*s4B(( zynVqBesG(w{=v&U`)tFs+u-IA=NzVKYVJRH!0E{ezqox)ZS})NbhvTeM)~1WLHu4? z7c*AOiV~HtQ{o@LG4PFVy~C|rJ3@$j`O7y!T88b0adVz`!FrvufN7qvMpGAmu$Sec zPlg>Lx8Gjsk)wV+-76a z_X}1l!}iuWn_)sE6+bAYr}-%}7;_7(H5h9MDHfJ3vOK1|&_W<4n_CsZPa~HXTkhU# zAi{RG?B)ssiQR5X*L75t!J3TtT*eG2CHX1!{#vVp4G%G7AsYfpgt_Dbq7+85(}u*E z>G|M;OQs2wwp?yKZZH_7G+kFQO%tsstkI8*TxPgXNQoHmL7;eo15`y26p4tf>?3hp z#*fm9;Dx{ayLYK8LrRHv?>Ux>B_^eaNdDtE&1?r%SWq4MF9J%dgA0ai-}j2`V9QB4 z=-|=hB=3+#SJO3?5Hm>cPa&J$3?0dN&IX(-3Yc^V6GS8%;`orFMVmaD`3O9!)JcLd za?lI;usK7}N~o*>0@hX-r4;i#GYkW*f~wM`y!qGmhE$uP6;mXH{k-QAxey+4%p49G zQzC|V_>CA7D43@i(8L&WJ`h5nX&M%(;ds&JAKRZHrNg{yl}$?8ryqruYqFI~DW@g5 zT^gmp$A}h%D_hn3Sdims+cqbqN@Yb9*LUzCl42}oMnXy&R@$#Qo-sfDPSyjv-GE6setoELGN=0xaX#XM&t4%UWP)jS z1Rn`85aM3CmsB)?HHx;XP%1Ib4(~j5U1797RM$X`QL4)F-*4aMoe%FF^xl5VQpy05 z!&GqK{e3#N7czS>v)7A0Czl7nrUKIL^hxG!CaOk6MD@119?7;SV3l}C<`daj)vG0h&W1@9s; zhC@`RjmBzCOo>$qEoL_#l98OCP=btD4wsmHz-WW_d0$9)n4X(?o|&c$yqF@B^TZVK zF0a$Qqe&u+lglayFeP-BM}o|{vbnq^Gm^b?nK^A`fc#r#yV=rqE!I}tyE~!x zR-lws6+kJMM{++Q&gH~RisSJy!t6?LQTm(`mDOdjMXb#j+hd?<+U$<^*{>*}s%-&K zJOoalN*Ye-by?X(EyW0`999>FTxg6?Dj~oyxg3U+)ZIL~jD}$#l`kn#R7T-K#(+}s zk?1y~J)0DjeS;RJIUq(eyDaOC5wy}w&Swo#4(kG_GA;A$vN^3POI_E@g&>-`w46-EN?18cwdi#C-p4 zQc8!MKq&<&VvQokfY#X;HieFtiPBaxvp$>K}9je7!M`Y zS{2m{E=07-_#g?|5PUA@5cf9kfU+|-E{SmMJ_Fr-J>Q(CJK8yD* zTePZTnr57Hv|Y=C2M;(tJ|@OUC4$k0d-v}%jw5Z`uwJiO_Dk+vTyS)B%x1Hp?|YKU z?-9GY)2J3C_- z23D(+Y}srMyH=}X&dyGmrjcP7GLe1Zi(g711lH?woO7I=Jw{VEbbZI|+qYRP7J$$+ z4YzK6#A>x-nr1@qOyh(UNq2N~#BRGqX_e(&+c6BgL#)>{4ZGcrs;W!tJWbPJq$rNZ zAHU8tjfA4_#&P24==h;SOgTmQ@Pqf+?Y8ubC9BnGdASwYZMW2Q%{Yy8T~~q&&p7O; z>IRW4)0~?*Iyx?|ejREbuf`bY`$g6Ub&dB9V{*EEesRwE`8m@xvDvKo@wdOmI1aRJ zgQ#2`iWS>!cQ}{1VY}V%<8ObB_4)#9EhncZgb>)Q*Nnr6)|&qZJNyM$?Sz_t00000 LNkvXXu0mjfHpe@` literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_box/gSkullTreasureChestSideAndTopTex.rgb5a1.png b/OTRExporter/assets/objects/object_box/gSkullTreasureChestSideAndTopTex.rgb5a1.png new file mode 100644 index 0000000000000000000000000000000000000000..0a09e2b6f160a1971f004210f0dcd161e9e7bf4e GIT binary patch literal 1979 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 Jt=gUY{{wF#Q49b8 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL new file mode 100644 index 000000000..966bef206 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 new file mode 100644 index 000000000..dea47708c --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_0 @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 new file mode 100644 index 000000000..36be4333f --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_tri_1 @@ -0,0 +1,7 @@ + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 new file mode 100644 index 000000000..6ca96db30 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_0 @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 new file mode 100644 index 000000000..3a653966d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/gTriforcePieceCompletedDL_vtx_1 @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges new file mode 100644 index 000000000..52591dfc8 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_edges @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface new file mode 100644 index 000000000..06193ae61 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_completed/mat_gTriforcePieceCompletedDL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_completed/noise_tex b/OTRExporter/assets/objects/object_triforce_completed/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 zcma)-p;iK66oglkuRxQ?Q<%&agGb`VjYNupNF+ksxY6os*d%WB6=crmckY4&-2)5j zKXYg1-v6g*n&##4`RV3#|Na=h@;ZI|_uuVl<^Asq(Az`PoL+ZL(=FG>ZM*At{kGk< zoB8^f@B6V|^o#wtA9sD8b<1}4`pMPKy5;JSt6Sz1_+WFbkE_FGzU#q*&dPQML+cGs(0eCWBAFIAB+{qL)^;J{#(*^U779F9Cck7oMvyi7MiAz1X(~0sQ40!#}eYwJr z)w$3!#TWA08q`zmoX`3-iMlqu)Ut1KlNXALA#>#Hnt0flT_;zLs4+)eg_T-0z2Jv# z==wz%=q<_ft_t{={8V=eviFAWeF#3h{wM3p2TJs82JFS)hc4641%p29MHs>^6Wz%( z_P%H)*j`R&0VaIl)cQF&g7^Hu`S~RA&8l$%2Rru~ep=hPLVc9b!Em_yQawC50ZL_7 zkVWyC0UUWR_wL|gup?AW- + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 new file mode 100644 index 000000000..09e44f1b7 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_0 @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 new file mode 100644 index 000000000..48001e3c3 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_1 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 new file mode 100644 index 000000000..e35e34492 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_tri_2 @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 new file mode 100644 index 000000000..a86fa98bf --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_0 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 new file mode 100644 index 000000000..230fbb7f8 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_1 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 new file mode 100644 index 000000000..86d123825 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/gTriforcePiece0DL_vtx_2 @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge new file mode 100644 index 000000000..f62631793 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_shard_edge @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges new file mode 100644 index 000000000..9355e7094 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_edges @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface new file mode 100644 index 000000000..e863b31c5 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_0/mat_gTriforcePiece0DL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_0/noise_tex b/OTRExporter/assets/objects/object_triforce_piece_0/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 zcma)-p;iK66oglkuRxQ?Q<%&agGb`VjYNupNF+ksxY6os*d%WB6=crmckY4&-2)5j zKXYg1-v6g*n&##4`RV3#|Na=h@;ZI|_uuVl<^Asq(Az`PoL+ZL(=FG>ZM*At{kGk< zoB8^f@B6V|^o#wtA9sD8b<1}4`pMPKy5;JSt6Sz1_+WFbkE_FGzU#q*&dPQML+cGs(0eCWBAFIAB+{qL)^;J{#(*^U779F9Cck7oMvyi7MiAz1X(~0sQ40!#}eYwJr z)w$3!#TWA08q`zmoX`3-iMlqu)Ut1KlNXALA#>#Hnt0flT_;zLs4+)eg_T-0z2Jv# z==wz%=q<_ft_t{={8V=eviFAWeF#3h{wM3p2TJs82JFS)hc4641%p29MHs>^6Wz%( z_P%H)*j`R&0VaIl)cQF&g7^Hu`S~RA&8l$%2Rru~ep=hPLVc9b!Em_yQawC50ZL_7 zkVWyC0UUWR_wL|gup?AW- + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 new file mode 100644 index 000000000..5f33f7347 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_0 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 new file mode 100644 index 000000000..43df6492b --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_tri_1 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 new file mode 100644 index 000000000..e078b8246 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_0 @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 new file mode 100644 index 000000000..e0460194d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/gTriforcePiece1DL_vtx_1 @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge new file mode 100644 index 000000000..b9e61293d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_shard_edge @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface new file mode 100644 index 000000000..5f8dc51f9 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_1/mat_gTriforcePiece1DL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_1/noise_tex b/OTRExporter/assets/objects/object_triforce_piece_1/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 zcma)-p;iK66oglkuRxQ?Q<%&agGb`VjYNupNF+ksxY6os*d%WB6=crmckY4&-2)5j zKXYg1-v6g*n&##4`RV3#|Na=h@;ZI|_uuVl<^Asq(Az`PoL+ZL(=FG>ZM*At{kGk< zoB8^f@B6V|^o#wtA9sD8b<1}4`pMPKy5;JSt6Sz1_+WFbkE_FGzU#q*&dPQML+cGs(0eCWBAFIAB+{qL)^;J{#(*^U779F9Cck7oMvyi7MiAz1X(~0sQ40!#}eYwJr z)w$3!#TWA08q`zmoX`3-iMlqu)Ut1KlNXALA#>#Hnt0flT_;zLs4+)eg_T-0z2Jv# z==wz%=q<_ft_t{={8V=eviFAWeF#3h{wM3p2TJs82JFS)hc4641%p29MHs>^6Wz%( z_P%H)*j`R&0VaIl)cQF&g7^Hu`S~RA&8l$%2Rru~ep=hPLVc9b!Em_yQawC50ZL_7 zkVWyC0UUWR_wL|gup?AW- + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 new file mode 100644 index 000000000..b54e182d5 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_0 @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 new file mode 100644 index 000000000..00a32bfd8 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_1 @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 new file mode 100644 index 000000000..0993c1c1e --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_tri_2 @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 new file mode 100644 index 000000000..bf7dfcac6 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_0 @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 new file mode 100644 index 000000000..e3237ab21 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_1 @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 new file mode 100644 index 000000000..ec4e73700 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/gTriforcePiece2DL_vtx_2 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge new file mode 100644 index 000000000..c222fe68d --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_shard_edge @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges new file mode 100644 index 000000000..5968068f5 --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_edges @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface new file mode 100644 index 000000000..d903f00bb --- /dev/null +++ b/OTRExporter/assets/objects/object_triforce_piece_2/mat_gTriforcePiece2DL_f3dlite_triforce_surface @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/objects/object_triforce_piece_2/noise_tex b/OTRExporter/assets/objects/object_triforce_piece_2/noise_tex new file mode 100644 index 0000000000000000000000000000000000000000..a6d6cf945e1bd755148a4d4dcbb52a7547cff5fe GIT binary patch literal 2140 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 v@qKsBw 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 a-T&@2YJoFO%U-?#^v9T38ecYa-}N5>&$}J~ literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/buttons/CUp.png b/OTRExporter/assets/textures/buttons/CUp.png new file mode 100644 index 0000000000000000000000000000000000000000..6c0e29d2de653af5f5518cfd1c6a5d3b956b1e33 GIT binary patch literal 11219 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 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/buttons/ZBtn.png b/OTRExporter/assets/textures/buttons/ZBtn.png new file mode 100644 index 0000000000000000000000000000000000000000..def8d9a6da2fcab6486b6106064d9fcd11dd47ba GIT binary patch literal 1241 zcmd^7;fvb@6o0p#W-OllP&8n64z!-JE+(X)vKtE{9js;Vf8EX$%O3WC7%d^Ve9S=K|x zSrm&?(;VtXPt{sVp(e={K?-@n<#>zDYBVELR4$!iNs=OZji^&AwW>iQ@@gd~4(y6= zm0csWb>C7wv*74*QIpJqpvyT;;%lK5d0J=}JX0!aIYZ%8k(G0_A6Sm77j4zB6ipWu zH76;YAY~EV`9vRg<-M41`AlqNN;2VcfRhP!yBNpl`)HadNysn=umHFNFa{U^^Z?ob z4S*P+3=jag07U=;U^c^kADc~#B6MBUG!%ryJc&S(i9o_ms0l9wumLojPjKADevEA& z>jp+52K2s5&7JhLm>!$tKqvN8=qS*V0db7jMvM?`Laox7yY_tk=gIf%qo)9#`0M?v`Zx4T z><_y)E_`?O1>!Oi7k7B}p>t2ay!`d*t>=6=f9%hd^)>U;o%pVAmmWTUJM}lvza;hA z(cv38M|?_9rj`P~oKp8a)c Yb??E?_CD!fN=}5$jjjCd`nlKt0XEX>4Tx04R}tkv&MmKpe$iQ%j3f9IPPXkfA!+MMVUcT7@E12(?114kp)6Xws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;tBaGOiUPOWvu?>ZqU!3o%+XQcR?2KjGmYcKi~#6mnI- z$gzMLRLG7W{11M2YZfOb+@x><2)x+#$0!io1zHW;{yw(t)(PN$2ClS@zt#k1K1pwM zwCEAgzYSbmcQj=WxZDATo^;ue9m!8qC>DYDGy0|+FmMa>uDQLn_i_3FWT>mu4RCM> zjFl*R-Q(S%?%w`A)9&vFiqLYfsZ*;V0000pP)t-s006Ef7Y$Pw1wSksW-A6tIVG(x zFI-45gh38zS3R3h38z~MwPy^&cMp$`jIpz@$jZkcHAig#0004WQchC&G0sSOa5;;t1?OaI$n`0uc_7t@fqCKi>wt?=fQn;`f|DHMCO!vBaul3Pk}sJq z5th%iAQoQUf_j4%ei{I4*J%K9Zou$Q2`wEE$A4<gz4{Zfih2m+X4VSwB_Y-B;>A->{k zUP@R5gSxp-Y))!ydQM?+>%vXj>S`AsJ%8ckwu#a1MfGLAx{MNPdN!_3cFr#D(T&q) zO?L^Ick;}>$!Q)29)(T0zG{MU>RL7-p@B~3=AKEFHK}4&Jv$FB&NCKKbjc`4a?sQ@ zv$XMu&4_X~RMRnY_cUgd4egxXmTV>_Z6BW&?e6Fk9vbMMQkw0lDJ`p@C@;vuD(94v zo#>(}X&ROs7Z#hImm43HQdJhIBPuQ~%FoHlE~4WV74B`JW#k?i9i5bsnGqS1)!v=w zC?+f+$j!pY!l!EC=IiR>?BpKc=VD`K@9LY=I&F4Cu(E)d5E~N%6PJvhp|O=mSbS1i zYK)Vdkakr2?D=!*L$!qk1c34^d{VM9lDYw@IpsBN-R16z_W9FRubxrnttQ4Nz{SkO zAuK5)t7+vE7M>(#DBP}K4;^E@x5)_}76crj3S2AgCdw%=s!>3N}>I>G=Fx1yKxAP2*ODQVP zP7Lu6iOQckZ+3h4=F{g-Zs`ru(y;fkwv^HLPOfP0tSv}S&n|3TxMjoqIor=&yRdg! zl%9rDWTZKRj6-5+ePeA^ZFOzytWA4&ty{hK+TFVsSLa(;1jT0s2%^O|dU`;QZ}jwl z9^dHc0X@Fa(*t^Zqo)V-_-0|1M@bJT@h!m3#>yqGi<};i<6BgagPD<4NYxxEJ)p!l zP@aK>M_kJQkseUuTabr^k(FCSLK=}CP~)4QlZk_0SV9Jo9?;`kPC;H#9VtDa$2WR< UK#y + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_0 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_0 new file mode 100644 index 000000000..2986de72b --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_0 @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_1 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_1 new file mode 100644 index 000000000..2aa9bea6d --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_1 @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_2 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_2 new file mode 100644 index 000000000..2b8fe0d11 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_2 @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_3 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_3 new file mode 100644 index 000000000..53edaf74f --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_tri_3 @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_0 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_0 new file mode 100644 index 000000000..2c2ee793b --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_0 @@ -0,0 +1,335 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_1 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_1 new file mode 100644 index 000000000..ec462b114 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_1 @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_2 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_2 new file mode 100644 index 000000000..cc906e23e --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_2 @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_3 b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_3 new file mode 100644 index 000000000..68e4babcf --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/gShipLogoDL_vtx_3 @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material new file mode 100644 index 000000000..e48b335c0 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 new file mode 100644 index 000000000..e98640319 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_001 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 new file mode 100644 index 000000000..b2db72d79 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_002 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 new file mode 100644 index 000000000..be60fd46b --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_gShipLogoDL_f3d_material_003 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material new file mode 100644 index 000000000..3cda02a88 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_001 b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_001 new file mode 100644 index 000000000..3cda02a88 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_001 @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_002 b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_002 new file mode 100644 index 000000000..3cda02a88 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_002 @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_003 b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_003 new file mode 100644 index 000000000..3cda02a88 --- /dev/null +++ b/OTRExporter/assets/textures/nintendo_rogo_static/mat_revert_gShipLogoDL_f3d_material_003 @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/OTRExporter/assets/textures/nintendo_rogo_static/nintendo_rogo_static_Tex_LUS_000000 b/OTRExporter/assets/textures/nintendo_rogo_static/nintendo_rogo_static_Tex_LUS_000000 new file mode 100644 index 0000000000000000000000000000000000000000..2edcaacb3c9433e786162171be988e44cf246adb GIT binary patch literal 6291548 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 z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXFtaE|$ z&p&_r#9t*IxnS{#%;%D`dU=z3o5X__%&hkwxb&>tDsRkgzkqIkK1(yX2@oJaU_Ju* zeY*An{zldB`@QBPXcPhj2oNAZfB*pk>rWtmZy&s%{~dA2`g=9ac%=mL8M?!QevWRq zQl`cwK!5-N0t5&UAV7cs0RjXF5FkK+0D;97$iJ!YlUT&|yG-hUS-o<}eTl@`3kY{k z=5(DIx^12O$1R}S(`IERF98As2+U6)zfaFzz~88v(tGEpXe0sz2oNAZfB*pk>q{Vi zZy&Ru{~hs^_4P`c?Me#dGxU`O{Tv;>lCH)kK!5-N0t5&UAV7cs0RjXF5FkK+0D;96 z$iJ!IWkLU4{rj2MhFQI8$$ij*y8SZq+Hr<%yC?sd3ut%Ptjy#kK!5;&`3dCr=@Se1 z8&y;KyZI>^i2wlt1PBlyK!Cvd63E}%KU~oNj=1OgdL_+vr3CW%*(vdh1^pZyv{J6d zB|v}x0RjXF5FkK+009C72oNAZfB=ET70AD-KV?DxUHwzDeAQz1s0DTV=8WC0xS($P z&(ck90t5&Un2$hyqkgrZzf<>^kDyTq5FkK+009C72&^xG{P)AYiBlK!za#cpU$3Uw zu9QGNKi5nw>a}(+wNkFeB|v}x0RjXF5FkK+009C72oNAZfB=ET70AD~5$_JT$rK!5-N z0t5&UAh7-f^0)R)67@ItqP3GVq21TtD{96oA&~dltrzVBv}r!y$?Xm+VQNeQ1PBly zK!5-N0t5&UAV7cs0RjXF5Lj%1{QL8d7xdrOA3DodE@od`P`ATp?Dm`mbo*T9wAC!# zTyozgQETNFt(}+&&GNX3DgImOQm5FkK+009DPUm*YSuzTVU3-})q_3vF*T>Gz(8LS0?yw|RlShRmLuCIkUreWu` z@EWL*009C72oNAZfB*pk1PBlyK!5-N0tD8UK>q!CQLj&Z#4KNlxVln#oOo009E?5yH9yUch#nS-onp+9L6Z1vINE{pGCODsS??VFBHao28lD1PBlyFdu>ZKHXsff1_$j zKT6E(+CKAnb&NuQ009C72oNAZV08-QZ|z6dAKeSsKEFD}Rker$`TRU>0Y5`ErK1+{ z^)e>{1PBlyK!5-N0t5&UAV7cs0RjXF5Lj7({QL7BiF4~8q8F^4o%vj6R=UZ(bK*A( zW>(XwYn2}|L$^9^;-fXtg0+{-%1mAY1PBnApFn<_p1fecPrd1UW`2rBB0zuu0RjXF z5FoI=1oB7sJ9~e4FWBvK*4Ha(wks)+&(1Lm_W9{e=NT*MYHR`o2oNAZfB*pk1PBly zK!5-N0t5&USWJQZBlHb>{}8=kwQcAV7cs0RjXF5FkK+009C72oNAZV1Whl57CGB z|6}w5-T(S5U$!{ibAhb-r}EPoyIrDxum!q*&@A2LCP07yf%ypJcj-q9^xM=wm50tp z&?p255FkK+009C7)|Wv3=-xeXLjND%3v~aQ>+97t+m#Z?XJ+5TsSEVk=}qO2xpTlu zxf+)M0RjXF5FkK+009C72oNAZfB*pk1Qu5y|Ni`n-ake!*zK!l`Krb0i3{e{o6aX^ z>~`1QKnrwxcIL9@EZyWLK!5;&*$d>me3!&&3-tTcKb5P_UeE{x2oNAZfB*pk1lFHG z{^-7D;@ti}z8B>FnVH9>*WW8@#w#I^_u91<jq~<29svRb2oNAZfB*pk1PBlyK!5-N0t5&wwm|;vdCSDF5{uYw zIm=fsU_V?$w9M%tGj@B=0=j*0mS%DjAV7e?d<63Qbk7C+jjAbqJ+bwC6pccF009C7 z2oNAZV0{VXZ|!H*AKeSsKD54GNwZx^fqZ^mxPYIbn$o*h($&}m2oNAZfB*pk1PBly zK!5-N0t5&UAh4JM`S<6#K6RZFcG21&Gof8(^{OTJ?ukZKD~GWf1_$jZ=H{zQ3wzqK!5-N0t5)GKY{$c{gws&?}+EEzgN?Y zS3)44pACukE$HXy2`gc0OacT55FkK+009C72oNAZfB*pk1PBmVY=Qin`fV5V-_?)L zytbd!tC!q2PMn)qw01@&wA&2b_DKF;FB)3rbFW#M$xDC$0Rrwr#7VP%R zna?&ebh}~lpSNIUHJ_W!%1mAY1PBnAk3hc5w@WPQx}Dq4LNQ)@ChojoamUPO$60vE zNq_(W0t5&UAh37>`P*@;#5svYYxVD4H(0z^%#2o}K;CP+B^L2qJM}rb!fG^E69EDQ z2oNAZfB*pk1PBlyK!5-N0t5)GZGr#K-kreRHkA(?@80WP*F4Xnj0p)PQbaee;`=7r`WGtU7*)xD0kr9oYzE#-m1qt15Bd3K*x>>gZ3MimJc>&(b zmn1UI+qs*(HZCDZy}J|H$}J=`vxJv*DxiP@3Mim}0t)04VB2vEBF^!ht;97#XXNWK zGpeHiW9`{Q#`Wk{@|s>pbK9hV0tzUgfC36Apnw7jD4>7>3Mim}0tzV5Tmkky$N8zw zBQlnv*QW;T#EfX<^udxNn)(k<=ypX(GwW7B0R7>3Mim}0yzcP7JgN-?cq*3mvN)mwN-PD2D9o{fU&lBXW%9Kd%o*Y zzoWv}DWHG?3Mim}0tzUgfC36Apnw7jD4>7>3XCVfzUM`VICpur64%w;V!XTuy`Q@0 zC;pNRZB9PVAJk{O*HiyA;_C_BzA|3U-micH3MkMV0Y0DJknl4qQo5u>FHmoMu74*M zkgR;6gqL+Hpnw7jD4>7>3gi@ETR7`ALXXMWV`f!H0oJ20Ci`C5N#_%F^tMe3D4>7> z3Mim}0tzUgfC36Apnw7jD4>7>%@$xM^lF_Q(aHYaLO#ou>K*D{n7B8YS)_9h@x+2| zv#9?HB3pS+DKqO;Kmi357@hzhPDf_@fSQxfu_Y9#dp2=xLbC%(cv+_c3Mim}0tzUg zKu!U+htEm4O&lq`J7h>S2@Zt2^ zY#&f_@_B0sMe1IhxIdxU_9eWmQvn4OP(T3%6i^_a0NcU~5LvGgx^=!DGov~RunsLo zTuEdso7K_WHYuQh0tzUgfC36Apnw7jD4>7>3Mim}0tz%+fF0@wW!$MwPCqQ!Gt|Ft zQnx=9cH1(k+bSj9tXlyE6i}eN0H06OiR+SnPHkI45UXb<^!h(?TDgRmbt<5M0tzUg zfC38S6JXnMb>fbM+r@E>&^7Y)m>JbkfU&kFk@1`{@!sHiRL4PQn-ow$0ROS)ON0tzUg zKzRY)%O6MFnecNe&d*=ubBcQ3kkIR&7>3Mim}0tzUgfC36Apnw7jD4;;o1la$4 zMaCWLNFZUq!jK!NfCyq9lF#JS(Im8(f;p%Q}B z`{is{5jK zO92HGP(T3%6i`3`1r$&~0R{EXnoS)8ocHsu1xCoj8bk< zFJ>1cbo*LKGwW7B0ROu$2lZpnw7jD4>7>3Mim}0tzUg zfC36Apnw8R5@7%HQ;Ce{xxIcyFGb-b`>|rsEz`2)}u>0125U%z3OOgn-ow$0RCsC&!Kf|C8cntT>3=(Y~^-)q%7>`2^T@oK9rCwrcF}dRD$3 zGov~RFxKK)yIIe*vqc@vZIc2DD4>7>3Mim}0tzUgfC36Apnw7jD4;;I1=z2CG?B5K zP_kzLdrL+%ayp~1+m;F4V&3EOCEcuB0RLRY~@}OS|neOnNb}D7;767G3K(B$Z3H(n%gD? z6i`3`1r$&~0R0A_{Bt=D?eNLCkYk1wrbANU{?JKFxFl~WL%H_PG0l%>*DJaP(T3%6i`3`1r$&~ z0Rxpb7ek*v+SUFd5A9c?l&d!E5C!f8lFdDB8mhsty zZl{fxulFmUfC36MM}W_#mnZy;ij=M{)eF>pY(lfMOL7>3MkNQ0rso!(D^|p+20#W z_6$~!Br+a1aVL2#U&@ZUXA*Z1*~*@!%&b=d1r$(VcmjMloto_fYEC`}l~APaS;XB5 z&5B+NsYG8`rveHnpnw7jD4@X92(T?Yo%nUaZQ@92x2aKescjQrti^ZBi(=3vEIYNW zKh0MG1r$&~0RU+QBt?-3%flQ zw~WV4tY6a2x)o4B0R@I5z-QFol77Cwq=X<=8z=o-e_RPK>r_Ai1r$&~0RGzsE+2gNdW~EP(T3%6i`3`1r$&~0R{tIfk+B?JvS$F>FC!W`omSZGr3u~sOHM17bhB;+6i`5c@&deJbkfU&lA47!Bn z268HPt=&5IrEO9`0RsCMk1r#VRz7>3Mim} z0tzUgfC36Apnw7jG*^Iq>Pr#VCEd6FxRO1C*(FKcURl`f^n`9dDrsij3Mim}0>csD z^C{!G?Biqi=fsQ>iqv~{EFf7qy@Zu@DxiP@3Mim}0t)05U|aYD$+m|(>HIKfkC{~+ z1sHUPbOv6szu&2&w{22D0R`eq45>YYXW zITnzt6ulNwiN3H-1r$&~0RpMo zm*%U00tzUgfC36Apnw7jD4>7>3Mim}0tzTlUVwe+J0#nm-bv?TZp|ScDS+4>LD4@V_1b9QAPGp_8^Y#*oc&$d;6DLd+p<0i6|I1XgJQfAhxfC36A zFdPBi%jYNlmF+`{eEyGkUI|6&-6)|~e6DX^!pk}pP(T3%6i`3`1@Z~7?YI;X*CEYT z;(Np@`FhNZ>L|ciTamaU+e$PipJM0QsbgQ-CIu8wKmi35P(T3%6i`3`1r$&~0Ra!w;vaF+cTlt?c_A0q?>gspnw7jlo#N=d`TkaxMVAL zlh9fv1gUr5Y*^%T7csqrmvt(jfC36Apnw7jJbkfU)+> zr0Y>!Yq!`nitE^ywn+g66i`3`1r$&~0Ri3b;W~KUsx@QnqCUlFG zN*_1zql9jsDK*xtR{;eSP+)ihd_KJ~;b&B&^s5qz)Emb`o<(FU$CmK2P6ZTDKmi35 zP(Xp40&EN)mu-W1PCoAM$~k0fnH6L1*re;xF|`D@Q3@!afC36Apnw7jD4>7>3Mim} z0tzUgfC7ycV4wQt#Q!DSw|+Z06+5pEbW^Z+a|!EJFPSDlKp*q+xpUc6;MC{1r$&~0RB53k+JMs zLJ_DHi8yvJTiKz6mvt(jfC36Apnw7j7>3Mim}0tzV5YytMEe>CZS_2WwR3}$;Jb^8N( zm7M>-BlTxHZsIO-dQ8djX59)XpnwA91$Zxyc{^F>8*E%c5U*W`Y@h3KJi}v4cv+_c z3Mim}0tzUgKt2Js9akdmC$g2ew(8pXdd!UKD8N`-fynmVGTsy7TswZhtmB}wO$sQW zfC36Apnw7jD4>7>3Mim}0tzUgK*I&t|NM*C5uLCcSh8#II+(aFVSKo-+dc`$+TY1( zp^|Rat$+dwC{SL2_wo&hjMu1%7>3Mim}0tzUg zfC36Apnw7jG*^Iq&+`+P5gE%?C3^<2`G{Ewqv-Duc%49GE9aFQYu2rR0tzTF905L@ zw$JtfH7B2o`8&IWBK4k>(CpVGtgKT31r$&~0R7>3Mim}0tzUgfC36Apnw7jG+Tgu>T42l4cctwJ`!5J z*}apq_4G@|>rEV;v$xDj0R7> z3Mi0IfbHR96K)enN=2^`nr|eSkpc=Rpnw7jD4>7>3Mim}0tzUgfC36Apnw7jC@>rW ze#hA~>F-lllh+c%QFJNY3eYD@6LAgaY~{7x8u&Z~6i`5c`U>#jbbPiCs5$w3w1hf! zFGt*v(Cp16ysT3J1r$&~0RF-m&Xn5D8Y(8Ucb>jYHWA6XRXPcBAW>N|$pg@ZR zcrTwpT$b!Js*}#{CAyNz*COI`FjI+qo?pVtIu%er0R7>3Mim}0tzUgfC36Apnw7jD4>7>3MkNg0e;Updc7>3Mim} z0tzUgfC9}H;5VFU#Q8+V;`OO(I6N6>?@2h=E+?n?YRGMq6i`3`1$q$Ry?iqw+qvxH zWA_eXRu2?iKb3ml9}7rUZX%zVT_gBJ1r$&~0R7>3Mim}0tzUgfC36Apnw7j3@E^FIMa!Y*QbuTd(Rw@$Cnon zpkKa3WGmkwp=kxUtWp656i{G@0=$>MGTR5#oO~`T^*KeoCnhvIot&l*ap<`y1r$&~ z0R7>3Mim}0tzUgfC37n z72tQA)rtG!2c2Z)Uh-KYZO1h@G5Y22$;Miw^Wg?}iA`5P0RSXG^I-%LorM#?H0ReK22dnUMktD4>7>3Mim}0tzUg zfC36Apnw7jD4>7>3MkNg0e;8Xm56iYXDio{kmqf!*$|~q79j4(HrU8#r7>3Mile0&EA*CVrP} zd$^O%^Xy~=6i`3`1r$&~0R@LtdNY>oGG@Kmi35P(T3%6i`3`1r$&~0R`_#%t8X{QNab z^d|Mbg2+~GC80G+cv+_c3Mim}0tzUgKt2Js9Uqr%gLqCpvA=7Pd_87H3Mim}0tzUg zfC36Apnw7jD4>7>3Mim}0tzUgK=TFo9p{3izfrxS`Mr}b1IF4>3CG(X^YxS&DWHG? z?GxbhX`_UnQIXO=iKR;PDD{3i7Lcs`m3*d`@Ul(?6i`3`1r$&~fqVjNJMNQgd$^O1 z`@8ZDS2I#T0R3lm~N12cU3MkM*0p8EwobWR$Qu<}7?xgP16Ple{%FB8cP(T3%6i`3` z1@Z~7J$!h=ZQ@Aj_7>3Mim}0tzUgfC36Apg{8l_#J1R zq`yzyOkOKDzlU;VK%Xo`{3+p>dt0uaG8+XHP@tUxd_H|5;b&B&^pz4lO1<&9epxIa zSvjnPmvt(jfC36Apnw7j

0A_}paM!<}^Ap0mfyN&y8FP(T3%6i`3`1r$&~0RbQ>R?Z$XD+Lr# zKmi35P(T3%6i`3`1r$&~0R6O)d&WpnnJ zSt+1^0__pt^C{;4-;(fi>i@`ThY~$Wy_+TUij;w^Z&L5uh-~E^ z67qAs#DK9*1r$&~0R7>`2^T@%y_Pg*v3DFn3k`{ z%t!$R6i`3`1r$&~0RV7E;BRtT@!PoPo@z!CmLs+RQ73Ne~fH1iNvu$8xl_? zo=d!y_%QJ~;#lHKlSDk{7~&&Dyf*5Hx;7vd8@ViRE1-Y^brsN`ZnazqJppyng~ zlMw9C5?I867$UCwGr|D7j?!u z_e$bv#8ZeT5UUdlRGy?2&Le>H4B{*zW7%V#gd27Tof4l_8xx)FhqrS()|*%lyIBY0 zIq}@XiFXh$A+{toA=V&@TxYFe%r;knRtxYxFq4SS-A#!ti9Lw-5}ofU$8&q+=lQqz z|JO#{7u&tD%^Ta$uOps8Y)-_s_JXYjHhBdUP(T3%6i`3`1r$&~0R`TNV5y*_n!(kHRMI@|Ro{*RotoI2I+mrOgS6AKZm5qlEvBhDoLM%+N$ zO;lZ?zyC#CMx0E%lZayj7AIzP-+G^^fC37X72rK-8u2S4V|hghJ)mOFWr@#@sqTa4 zm+-Pq1r(TAfE7RH%`8I1yw$ylj}T`OeX&xvUEkixM%v zF}^$ON5uTcvx(TYzmm9#xPvIsFH}2eiSJ_ZeIt%VyNZah@^d1NVUG5`oLG;DakT(p zKac2VZ+0&bV~3bc#8_ID=p2LeZf>7MTtLM5i*a}}5##cHVk%0k7xxgi67k*{-}mD> zj3*EW5PK1u60v^Adg1X;-4BH^pNZkZM7$4WEAigG^_WT5>`vM_9}(;4hD2u@;+NdM zoVbaIb)xEWFBQgmb~SM^5!(}SY{B!0Wr^7}W3o94C@`P^>-GG^V#G6vw-R3_V*URI z5$^}_zFB37&-B>Vj?eQ;iO&1Xv$!4YaNqiXed@~mb?r0Swlopv3;H1OLn7uP)S<-hA-^ZS zK7>3Mim}0tzUgfC36Apg>9i z_6wd#WGrt>*NK!yivleW;C*LJB4hbCNj?2I=^LPD5pjI*4#azivxt8ZabD53l{go4yywTY0}mu(uJ;mz=R$AG3PGOqcU!EYbHp0s1JcV zomiaMnfNSGmu(1|(p8Cd>pJ2DVpn1*!q1zbjv^PPKz9PHoAG@mj*I;i5$`p1JGO`> zjag#by!7ajxBUxzAES0RO{;nJf8S95pxhN zV++K*sZ)vgUbQ*loabaK2YnjvM_UrFC%#4ejcDUMf70%{#CmZC5yuQ|LAYLwS}!n( z_l%6?N25|5`hP)7>3Mim}0vQC@_xDI5<~w97caYFJ zCHsi_S2K673W6GI1Xm^z|hlE@rvI?GnUhOg6Q(eK*jvII5%vz zaykjENMu`o+LX^CT@K@(p2nD1G|{+NsHf!ncrsSgh*gO=|Kv49UB7q6d9GLNBgNXj zgnVKf;3>q1iOY!ier)Mo;9g#GHStm6NyLJ+?Q|P9t^oZun}}mz-bBP)+nb2{2uptg zv0lWy$WIebC5nHZjN7?2dH{zX5E;usQ^5>m?9i}(l;$0*nJ`wC4PsKjpu@te#GiA6^mcv}Gl z6i`3`1r$&~0RqD!}BphP#Yx{*Gg$SlL}H z=;=gUALP!F3$5wxODju!3M;}kuI(4+_&<@T+cg$wQi>A$01hM8o5HSk(On48M=|F= z&f)tR;&()BLt4rR#4$#3jLgPFk?T!$(dASA6mtIyk+JMPEL`wn9&YTX{wnbwqKzfy z9>;#Kn4{ae>uiwoaYV*)RLS)Su)UL^on6u^>c%VHZ=O&5n7D^%Q~4XozMB}@aTcZf z)A|)qKmi35P(T3%6i`3`1r$&~0RZK_lBkH zsQb~qHmD)bU%Qmq$a=Ae^DJ*goIt}BtTtUAf6H}J!t z#k#u>aVgQH?FX7$ti=1*ABkOv`3FXBNCo8j8zN(QQ^_s>?5JdDhnMogX+dIlBHnM> zzMT-qS;aLlmnr3Ky$UFxfC36Apnw7jD4>7>3Mim}0tzTlR)GC`#}XOKktO8-+b|;< zImJ1{i~UYZ-7ib%7RP}sR(QNkXBp2Sev!~`(3DQ4f{jZ0H?3|wwjUI=rmw$36^I*a+Lwbg>w+!`dVU&>TAIjsY+uX;UALyU+crlV(oVcoP z$@bZvIF@R57pr;KPocgoi8G0K&uLoOzw1P6@I-K@5pkaFPZ3uVmZ=qp`D3pqJPu$A z)(_lg5Ze&P68|79^$_?g%{YK)#hgO&d@_-79%;-CEqUzqBdI^G9g+1IY*MSFoJTFs zBvM~rgZvgEVjDGMc~Z%bNWkLQt4!tfReOOmi`bL6BokCcxy0{52NG_}Rz2{nPXPrK zP(T3%6i`3`1r$&~0Ry~gu?YX$H+j}c&XPv5v z^U%*K;YPhN??29=dM=Uiw`9_)q{Mj!k0dHTk8O#5EnO!L2Unt6ze;{k+AdT5bl+p^ z*M*g|=u4&giPmjPWPGo^oxF;iGm?7WPgoSFo51gAQ03cZ)cGhP%k_1>OD>hnA)}r( zh!cn=pRaefQlBkV){AkR>;FYW%<*cnH_cC1;<%T;6R#nP9Zz57cxrlGI6s<*>sek# zWZZrutrX>EvWV}eOE-PYP1Jt!i}&V?CH6&CehfZ!u1$P`i0>(u?gZj<|Jy{72Nj!wk!zb6?W5 z4(_tOm7fQ$)88!rRb95BX-|uJJ2N_YR{58DPiq>6?QMm?kE zFBb7#@;yYH8@TCZ<$fK8@j7sqA;S9drbD-+{c((tolK2+! zU&7KHfw(rq+lWf9JyVylN?sppPFM8Q(i5vRX2~lNpCq#Gi|z@x&nL$*HE$)-&CeyL zxW+-o64wbTc3ySrUWdqZ9mBiGr;_7b=Ggq0zaA$DLYF0ukvN4|qssSx>ry}g1r$&~ z0RIK-3_aq|Yd37SEV*AyjC#XNp`JS=7x%!bdw?@FOPKfs2 zxfu?A3!i0)BZxRZl%;op_)X`tM6qks58lh=Y9Q~J6BBcevX%J#sS)SqE!Vrf)r{8% zvke0BiE*$)ZxY=Gm z+fn4$pSLpzfy=8Q*V`4ujUD6~_rZq0pGWEQ#U`dAwP4LMs67IAxnT}C$jl3qM`;dJ6UD#ro`ABcAeP7x)A$YeoJKB=R#VRQ35ef^CQIUVUM?}mPgy4F)F7l z|BcW6o{z!jH8GF-okYg#f09-$OT1rxg_yspaXsvMqU}V+dE0Sp@>5EAQg?iA#{RNQ zWv^bnNPcbNSD6rMm`m(q+>a=7PL5vt+~+HxfC36Apnw7jD4>7>3Mim}0tzV5b^-R) z%_g#53-$RW`xm$UiHzm@BvouK0(ECQA5n~fp5{-D9GATIk;F-a^BO=HLOFqDEIP6e zGTtV~j}5_cDi(2c#{Mdm6CJcIk#R2j#|oNlJrx6c2+NGQq($a|Q19o5(0&)8K(7LE z-q6aoXQ^{@BIfU8D4*%}y_95QF4mDmmfXkczo&QWQQ~QhCQVPVn6mzF4 zKTfH{_#9@Pm~24&fbjb<2rVq9lJg3~9CKAIi+1mmw7)Lu)d+`mh;x#9G$E}x&(C7L z_FEmEPrElJGQR)E@xu$2GN$f0-X^Ynnx(`T*tQohp1&J$a~6OG|5&pI+3yTb$#7#tihT_ zoSYD?XR|pL+wu3G2|enbQp~Mt{wziZtR9iTmL=*EcpmnY5`U^z{A>76uk zy>2|Jeh$lBfOs#F<+>82V(C&Kj?s)`;~RUv^D5T|92O)FC0rLkXnFYy`9G%0@imOP zX!Eg2`|Ts$`jLN}gM3dS>$QTK)WxyiYw>RTt}f4`&97<_au03&dr4#35$C%7bfC36Apnw7jD4>7>3Mim}0tzUgKp6q{ z(LHv!y9I}~wCh{ zx2XFw^DHsP9Ylj?=O=M0G}|XK)f$+(Xes+n=vzK{++My zrXNUiaU$z65KZmH(`fj@-Sl5woxv&Jc|3wZnCNtwX%Md?I zkZ+SyxQNEDGFe~wxB?0&pnw7jD4>7>3Mim}0tzUgfC37X5n%sZ*6UOMsf^fzwc-}P zYu!p@EBBGmQ%l)V_mV_h!#7*myjwF8Uyg|1;4(TitzXZj!Hqi~+GJymoE}ZYd2F+l z$Z3tq^7rw%0(ix7F4;<4&wb^&Syp?Cx-ZKHHYcBbs+D@MHtLS+Ncnk|bkH26U#Ujct7Hf=me7dznkL)*Cf}h$3HZ^8^1{c^Y=5Z zr{XoV^_-@IkJJ7GOM0@*_m8vOrrsUb4xToV8IP|;T-wwGlePPD@>zSL{_-&e6i`3` z1r$&~0R;R_`n*+28+=&$6ZLsQcN; zyds@AhksYsy5)Hr6Ith$l3F9nskFM-wL(g*OB2;gK2L09PfXtESa(`CFKVK`p6D^G z;yAUdCTDHjGcKJd??E0zBCeegpMx1ok!xoFwL?ZMb85UmY@1a+|A0CdB(j_f|M%oF z?p!b)dol4}qAlf9gM5DU@>)PwC1U&2qQGc@uW-lEuS3zx_^tA}Q6YAy2N_XYn`+)j*t8c zk+E#ot4A--2Q1sR7>3Mim}0tzUgfC36A zpnw7jC{Vos`{jOFpWTF&w&d;AYt5~ZmcFXe*4C%8E9Pcd{VmdbYkg9!tR-=dn+dO{ z&EwOF=Mx#P89`c&FDH`0xO3pDUlYpKNG`8$e2;be zxl{KH;u0cLdChoUB)JvQrsI^G)UR>gmX*fqJMULO0R>*6v@VKqWcjU&jpvM zFFzW6HBnzStyIk}7hH6ZL*ABbYfg zUf{eE?-QuRb)2%4caLR6a{Ce)j~$z=-<~>_M^*Q+@HiIubwtKFZIjvhcy9u648UGh zjlH_nLv9NY#}hFZ(xN~w0&!08)Rf$`OOyvok{6_vI!JUcQG9{WYm*0?6vGG1~ zJa}6H1r$&~0RW&KRLwlCcmz^b-&Hqvnl;hhjxN|K)=v6f8C2q z`I7gcHP5}@=ABT`?9cr+%GW7SP~b}?#ystPOTjh|v+9^%{H)PN+_yIo*9&P|iQ^p> z9qm!o|EYU6aRd?W76rNyh&g<=&5o^hLm;#}i#yh9t07i@Ze#=P7GNB3!6{!iPoY-^7mC+CzjW?_#Xy}*Y5 zKj!zZKH7}?b|SL=j@9rko2U6_a8GUDYetVP|F3`o3Mim}0tzUgfC36Apnw7jD4;-7 z1=tU_9g%S!!nNeJP|1Fw{;P?MWtUQJU@byqyxzo^*U%={nEU@t!qOap+iB`kO05IK zsF(J~{Qr#C!H&t&-IN$UL>5^He|hm7viy8NF$$+E3IdU$=Y zq%jM7&FBR-{Qq&imzg8Y`2Ws?=lyS1$9|6H&KTK`-c~>X1r$&~0R77R}y?r9dsq^*tf zoozi@iu;xzPM`GEKB0~R@jKHpqx-Ze|EKMbAu`V2yNkRY_WNH``edrwiPv9=Y~?>B zG`oZ;^~Nz#_Nuqz-vo7v&gv4RHpKa zkzORWJrU#9(ma7Tj_gZsE1-Y^3Mim}0tzUgfC36Apnw7jD4;;K0_-yUQuB5jcGcp~ zS1a}K+GxSnM9eM9R^lAK#m+HE-8*ChBcC|;&|)J;2>)M^aIOys4K8sG@J7xxr`<0g zGQK~>IZ!=s)W|oR32tMx0Fm)HwYY|Ok#Ru1F;6qjiJh&)@j#nRFkJe6hpPh^tIIrTt;;d+Yd99L!P#5KxF(5eg}EY>ezDo6-35!4Uksr$}h;c z*m-k0W2TqCv?s2kk@5WQ_mkK5y;$_}T_-L1r$&~0RjW>q9UjYRaP(T3%6i`3`1r$&~0RaNZ+0|zBd;OscOf#KgYxyIeDQkSBxA6t5C6HK*;kv2 zV!Kld{DABpn5$Oh*r(3t5n0|>Uq&t)5H}KSFEP)ovh%A`SA565u zj|trl4m0wK^~t$D`8xO%Qkq$6y{Nig+8Xaq=jZE&2Qu2WYOl=oA@-4^oq}C67Ri z(W2Mu?yM93RX_m+6i`3`1r$&~0R~nijo?VC!%jBOW`;a9qNZjW#TT89&#<%+hRuJ?4(o z>TW@DVzXHuYJI$yW;`bMp#*!paa9$ggk{FD&fo2E**;x?UIgAz#aJy}2MMi0T+>Tp zK39PO1mgSpGfI!^iRx$h^AT|#^^E12B=zx(iJ8+w0x@=rotu85b!YTrwBwYAT9xa@ zp`&xH^8Zl1DHqoR*Go7dSx%<&puo-ic~q6--?|h~Kmi35P(T3%6i`3`1r$&~0RM`M$g$b;{{jK%!RA8%3K##vx$qF(X~Cb@=BgvY;FQ|U)xh_n)Br6l^E}&|D@&+ z*wPjW#5}-_s~+>z8P~hGphb>tO7(|>ju)`v`b$T*3C&!mL?E_PYP~&7`xYU7IVCzS zw9Nv)BKczH)}HJ$f@MF7xFul+#5uU%K*YI1EO`Z@f1fkiI4}J;ZHR5D-zH^#aY;YS zzLm`Fu4MU`&cg+M#D5!eo!ye_u5~M*fC36Apnw7jD4>7>3Mim}0tzTFr~v!ge)#a+ zZ)yJb-a&09dOb>UUEPf3UlZj$=wpEGl@Kk?vwl{>yi93cfkU`!%H|YwyZ+I}m(APs z6!%=vt%1*b#3ShOWr>WxnVws!fALzIxShyYmM-B2)N3+gnN#Bh{zC?%wo%nJc(QH$CfqTht%B}&xvNudBd@Ffn zy!L6*?iwhqZ}aLY-QUIXS0v)x;MqzXo4j7B&)%q)`aSM0+o;K%PplI!D!EQrw*m?% zpnw7jD4>7>3Mim}0tzUgfC6n1U|-u`lkQG@Mae$IZ2zQgKP~Kbcv3ghYKK7l_Pu(^ zb)<^AY2*8ncE>SQ__U=C#=!bX$H50n^&wu{5b--?wsIW_6**Th^?o!PmieR*_%m5l zK7WZivtC~(MW+v##7hVHY+>2YATl0vHz2DoFC=gt_15y7skF=EU7>3Mim}0tzUgfC36Apnw7jv_XJzrY^m3gSehf>$Z_TPk|Z>ym_K=UivXyUR7h8L7MiPl6Ev~%|ROHYZaJ6fx}9! z8`iCW0tzUgfC36Apnw7jD4>7>3MimJb^-S7ZBE=bh5PtQEjrH8y=2M$qy9gYYWx(} zyGJQE@|sCpKE?UDcm)O(xRVMtDz%#nz7_a0~bMb?!-CH$U;yaq9JM1xgAWUE+PN z-#STjVdA2a30t=UT?)Lf-*MdU>uA?B;;b&)=hGCZv%s(VT@R{w9WaO08E1gDeWwba z@#>&mI}gw{U#h@V3jCLauQ}e>_I?EvP(T3%6i`3`1r$&~0RCm!)r z^C<0#_oq9mO0-7x#Ib4%PK|0amx|Y~YSe#KH6_OO!XX75fa5Z||p?H8VGJ!9EFPU`yh$#Ng$am?PA z)XlWoA#mIv7>3Mim}0tzUgfC36A&}IR4As(4@H{yvU`xUcIiTe}U z#knt+DP=duEg_pJwNT(*a$c|0x-v+;v};=;%$oE{-L;DFfiB9O5|9T{?M|_)GoY8nsOSt3Z7>!xmt_TI^TPx_|gRB|UL_5s|U{nxu-&uchucXGAln)(X_}{6zG| ztCIG}H_2;S$@lh5bz`+2ac9D@@YPcNgjHNSBV&pAc#kgOM!j#%2xd-=7l`@Tv#TES z)VVt0b*vgcJjP7mz+T4-wJbthJZ5d)slf0A4)1mBR{eZZjq4|!KD?A&k^)@`98&dI zpQt`sv@_wgV7lrWpVR<>6DAt(r5?j2j-$D|0Y1GpF^<=JWGT0(m-;6 zlf9PuKUzUM>sZCNC(C`1$4U6pbxhheDbT0De|X)a23aRcyp}c{+h;y?f7z7JZz|DW zrRt|9bj{qZRG;Fte^RrbmN3K0&-J7OCP7+X zC^Y8xPGDmS^9saw#|?TJGdzDo;;wnC@*V|-E^r@9T)LO>Tjg_E_AKI(p=a#^6_{Kg z){P~r9Pbm=#j;MEyh%Pofkp`|Khc;k@fbR}ln@MU$seYsG_>KD?3E|=# zOFI{In@Rn*By=;Sb_-ld`m;){H@((NTNWcSzCYbdUYqpl(dOsl^;IHUiMf^~kMH|_ zHn2JQe4xa-K;5xD6?C!4e?E4z0yj;nYMEk3uhR$Gw*^$>V*73)SXb&&Q6^+>()R(!G-hhCpcYhPJ; zi>*+BsS$|lSgh9TI^gpaP(T3%6i`3`1r$&~0R2GscJU7r3~VW!B>> z_bzFO+ZQqoJacKizz0jNFV?Mq0tzUgfC36Apnw7jD4>7>3MimJCIR*)9+z~F;*ll$ z3A1gIy8VZ|x<2=1ug289Yf?AUQs76ut`DW2PgiW8v^)M-$}D5OjDa{#E#}x|E0>h& zFQ8T+;`%`u%jzZEfZ8o1mN_+EAkK-rLDgfPI%A$|*7u0Uclv+{bUif95STFg1+PM^Q?Evkq^Glyp*zWW? zCvV#n=uzNGp1)|xb;i0CP(T3%6i`3`1r$&~0Rku*T{FB75iTjDRmwy1T&>*h8zLw?1d795|Df}hm z{Q=^s#Ef3;hWUKjw+a!*Cj60TV~K5oBHO_u`;lclzm2#<%=spk^)up|#K(xeiTE7Y zfp`w_bRyQrXU+M4+!qng*@<`;5wH6S@qOZAqV31VKrm^!Y~(mEeVf*-I%$iV`Gi|( zc9G-pqfe-NF~Vb0n>jXY_4op{xz29s_de@aKmi35P(T3%6i`3`1r$&~0RGSHuZ$=r*5&e#*Ze9mi{LW@kAge&p>v?yxj>U7p{Zdlkw|8sc^Bx!n zFG)HM-c+i8@p@5GvtN-{kvW;vdvZdvn9~>Yyq7i_Yp`UA_X)Re+}_PBIfz8 zLB#y#%8s?6uGz#AMAZFS;&>uHLt@#MxdL%6{`p3a_fr37S-Z?#bgEl_E@c+=A{Ez8 z@>s~SLneH0A^u6k{({&K@?qjV#QwxP=fu1Ddk}FX@m=EgM11bXHRSFgEZqy7U2?s# zZUq!jKmi35P(T3%6i`3`1r$&~0R=J$u)A%2;;!y@yEW#Su|IXCl6^$|7dA$tw}!s4 zuv_f+wkVKQ;9L@$R&pKcrf%ABSyJHVcay)*c~F2ca8S~5uvw`-#_NcrX5T4jc5y

+ec9-c9Mj`O7PIGdjUKw54D%AM#s7oL6~PH&#>eoM;>IG9u<>wXr;P znC<|7yd}sn=kEk!qhb1%S{8XMMSPxU(_GQ*OL=zc#q+C#WiWyGUU4w7DN*Fud3;dlbzec=)*os(X zq7>3Mim}0tzUgfC36Apnw7j zv`v70Xxogxi>=X*#N5|n=OU!;*k}8XMhlJK_$QWH4_M@i#Es)G+D8Tyi1XUTxrkzH z{)M=J_yG}f6+T16`G`94$vO9*#NS^Me;_U=t|$Ec0fatF%#G@5{#e2FEY9or?>^gK z=a!yTY2bb`&&gKg+)?>TM zl20J!_AENt2<15|5HSy~O(nh;e1%w-m_C_NT^^^c3lQ%o+V;KX4a4dKY~xyzeR0C(U|ey%@~M$h}RQw?u<5+_jKbj$a6q!+jSMH8aJ`;;&@^+qBh5u(UwJt*nbx1 z>~3?3_lPG9vTpS9T9$QUQtbEl;?V1JF^hB0TLu@1`Q{fA?;zG87U;Dvx_v%%$LDao z&%A*6GU4wfAdFFpe|~q{XFg8>1r$&~0RM5^)TB z_VUS+{Q+Ja57V~ejRqYvaSZW$h`!IKU9wL*bw~S%?-Fe-@!e?4jz{UgEGz4AG<%e^ z!|j;14$zA7zMOgvB-Sh0PZQP6vX>?HB7R3$<_g5QjcRjj*hGEkV+tssfC36Apnw7j zD4>7>3Mim}0t!rp0Q=95ox3|N)vcpSdLp%LDy_N6{E?eQ=2b?2Q1=VxF3MYd2*mv7 z(}>p*YZ3Dk(@Ku@IsG<++I&v<+vY0XoP8I~(_vGrjF|+wbWI1RUfc%F7(@z0}GU>~_T039kzP!l)A0hhL#e^Iqy= z+3{O?#`2k79NPX|2EZ0X#`y^!E7iAnJuTykLr!rVNRc_5)SK~h{WkLQSff%aT7&Cl zTs)b`_`XV7tuL>fY-l3%jut4z@x5`5<657Kd9uD5>v3{=JQ3UI*~^(ESo|}LWyGBE zza_@}9=X+KF5p;Wy2|^=HRc+gnz%nBwcim%wuhrHsQVSM5X*!DG1vA>#A;QJ-(l88 z+xMR^!@Lh}GYn_=o=#*ujwY@RQtWs#>Ryoe4`Jy}Ahvf;BVxQ2J1%U{b%wS@zrLNw zdQ1$dbzR;wsHd-2Kmi35P(T3%6i`3`1r$&~0R5-+}XhqBs_*PG?Y44Ncw}?14=Z(Y?L@j@hh;|Wc5}zhwzNBTYz;>n9 zrOE202`9~MMygwr&dr<5rcIC2^RGWW3%5X|=w@F&z_bxAFM=#5KvK;+WnK5XI+?Pt`jCxy7~UvLD0qXL6jc zv@y$AorvoTWh`-A!_uV<>sCKG$8|F9%-ErGa{4EKOV0m~eIl0e1>(KwnM9+vv1$Kw zB938bTN&2;|Cc3&jqjJmKAWj~QzGkUa@9lPLMnU)(U|RB+PXdw?QV0qv~Jr?wnqU4 z6i`3`1r$&~0Rz?kP{z;oB3>o>z&eL3 zz<58h8LPal>`@@*yDl@)XyWnRh|Jr#aZZ3;h(^!LoTv}uG4hMgt?v_=%Z(&Ab25uT zA7>f6Cq<0){CR`=H1+l5{)MFd8fnG(lS|IOJvFi2AgRUo_)if_G7>3MkNa0rs0^JvZ=`CHoe)IRE3_M79#wP1vlI9d$2E zcs`=>N6pW8XnUfu^KNK+9OJPU5x=Xnz5J!rx-?O}pvP}Q8Q-7&OI|BZl)I136~HRi z#UBvaN{oX==4M*$E$aStHn2JQyscWP2WyMIAmW&eY^8`-i9WE-;R!Iv<9Zk0$u`){ z=K+E1CK^dRBR*5ldf=Hk|M?GTZ8ecWrpL%Nj#Sp}*w@vz`QW5HX}O7KytL|ZT8H|`YgZ!kae<>dcUbfP zPaVDBB>&&PGhXk4v|3+&Fwy8lC-Z#R+sLmqbN@U2OwK<{ zJnG}PykX4$KRc;mYtQ{3>j!-r$GV-Bv~S~l7p3O^uSne)kBJ>*DEx^RZ_s()&Gwhv zV!JTobr;9$^WyXWS#TUjbNP4;X!!k4FS!n2<1xx(50E$pDXv#Eqon^Z>n4}@oIZre zxF3eJCM@6J(Z-%LX_({0WhtP50tzUgfC36Apnw7jD4>7>3baRneP(A(xL2*Y55BKt zFT%1%b7>}Qd7Q7T*nU*%e(i)yZ{LG4Kl~_Sv6AC;s_G`MrHNk>Z7uOz%+AC1XPNsZ z1)p$k-msnPzC6ajl0?Se1HW9#9vJK_xDN5t2OFAy>BN>`&Y8N@asYRVVjQRh|;Th{`wi4&s zS-+}rH@Y6`o=sew4XuKF&Y_N!-|NXMt^;sv1x8J);zk<0!02_r|0|$?0tzUgfC36A zpnw7jD4>7>3MkN40rr>8N5uJ{r0zTB{k$F@@dQxNmyAadH_CY)uq9-lAk* zc3U_3El&J`Xlwa+w^mg>59qA#Pw%P9s15Zn4&r(%8Os5s`VXw9X9OdszY+_VaHHPO zCp3%C=h-E^th3nyjEij(j`zBx6!Xwyo`1|2jQRVoCN>~eB4VELz}Lv4jyPB5qD0Is zdnBzM(1r$&~0R<8+P^G9YZ@78T7-TGCQ(wg%Ml51QCpsnYk zI;-S5HCEkNpHE~gFBvOe?;Bfy0T6Q z`TsY~2uDtD9md$J`!aBdxEew8>kY{pXTdd^@i zMqHT@t%scAcZj$?c(L<7l^XYh)=SRo5H}EQD{;QqB?tBE<@GG@nxx3f_u|m@=Q0Mq zmT(-zyn~fX^(}QDlh7W%Yg|2-CvtbDkHmvuH@fN>u4r7isn+{#O1j@&-P z%0wgP2hz^35ZTI~NNB0%ua$#kfWarSb?lschB5zt&!l!Q8f;YMc{TleT*5vsIsgCn z3B7tv>5IIejr+O|>lLbj&yRD?pt$YZz%f;#$^WL(?Mjc+y46q4 zdl0d&tS#l#y7htWQ9uC&6i`3`1r$&~0RcpK1 z;o|!4Yn8I2?$wA`MqA5GB>wzTzD=l?{9jDOZ|-d?s|~AvfL)ikBO!3CKZ^~^F~f53 zx+LK^h~sG%FVR2L8|QBPZ9=n1>6L}d;#_kXKiBsx;boo87ht?!6f2aaXMtbwia7pt z)uxRC+PpaN4E@G~Fm{L0Mmlx>OdUHX zmm>53se3vR^G@4bP9pgQhA}qlwhZ!qRtq{lt{wd7()KLlk4Y`}Eoq0@5lP*8Nh`M3 zpIp+pu62`ldI6#bw91)K7%y>T3%fITv;Q%>v$W3 zX~YSGKsNNXFBw*U;ImCbjV5dM**sy!upFDd9ITcg;#xS_%1=u54NxY*qoTz)b6M4PbadKBS~mlQ~RXK zcH{7DB0eXxm0`^P|6n$>Ir*$q9jyKZg2e5jPVV%Vs4%4*^m6U?U>V@0+cB zu~h#6wLX#Yn(23u*TN;-s5j&1di<8$*tyCj`q?@k6kuFzKxDk$1!+}U;y9{L6KiJ} zJLGXxr8-kzS)3bn=?r~SMGjazfylUC4`crSVN*Yxi?m>wD#liZI?!FGi15@_8gnB{ z%>REL^=AAY(M@l~{;riXjQM_ZApb>)>$|~f@N=%{*QeXpVY4ZbaUVwHv`DuOy*`h+ z=Ofy-kCl`kDpzppvAw!wd_E3edPpVzjaFY@Ipg>>Cj}HxKmi35P(T3%6i`3`1r$&~ zf!+n!4LF1Ncf#F)@tfO@y^D1J3hK``52P~;j_+Qf_h+(@&vX{lqQ6&=Yw>Gj_ud;* z`wC1BZqXQsRKGhcah7##QrkBT%P_-o7y~;b9S5&2)jxQ>JgM1Lp0*qLt-s}V)sbISJcytPLjM4az7Q+d_AnYQ;HIyUAv zXPj&3{Qv&f+RfX$Mm#)o2;==c+qr~ej%-vT>EEM$=;>@!-$M!kCJXps+-PT zhPW<;Qta=eLqMb(2nfM}K*f#&pTHVv&7+=TRm7qHYWi>1N$>m%E%fC36Apnw7jD4>7>3Mim}0tzUg zK$QaQBin_D-)pj!n@MPTl~yC`qVCw2dTTZ`@`m~JPo&SGyoyY4i+eRP9G5ePjnaX!brqcO>)HjQ`AQMuTxg5juitT$H z=2)*{Sy-IkrCU><_O>dVCaMFNZ#ETYl6HS&qPz!vjAg%blI3T7IF56C_MmndUysYv zh&VS^_Hs1|7T?dFag3Og0tzUgfC36Apnw7jD4>7>3MimJ9s%}|y(#IAz?i>R>F;?X zd!4%1B{J?ujhq%4nQN7|sr!jZ-AHSmau4_HRHa|Wx-eRm_%G3h5@X^CrTdX(JSr*X z^`-4H)z281N&F?@Sh%)SKVdb4xFn%jq_lGhH|pJ($hbYUO$jgSY_DZOBP??T z;vCAww{50sN0#-xIv*X>W}?^Q_F^LTHD@c=kWl5UqxKI_;@}F%Z{*$T%w3-ripn*3pcx0CvH9YEkR^DPIcdjGj8Xj^zk}G#&tW6 zSDLS6uTp>3`TwN$zgQ^<`7Bq$ zje0+j%qr5kidd+GmvuHGUu>7z3|P zIu44RixscElbW61!_evJsd)V`q1lB!A^7Ch2r$-Pm2kZGno^t#zu5b5-3Al69?&aL zji3Lyy7|_nJ@nZQ#QinyyMCMYh0?|}?EU>Vt|6~GytH8x>Zh+hMx34`enK8ksM?)6 zv#$%J_)ximTZ>g~n4vyg&VNWL-;JM=QnBmBM*ma)zWLZ5$mq`{$2Ut@J*nGRY2C=3 zYnJrPQa4UBiC>I`nC!lTvh7>3Mim}0tzUgfC36Apnw7jDA20_`})=*GM>XS z=B2FKt4Fubr|#H)dJ&Payr5f$UY`e6%<=wRMzj$*#dWHm)~k)rf8ck%V@EhWP$Gm@4Zpvr9X4&b{zz=k?pAsn zW~v{r_ceUo>8APL$<$}&qJRPlD4>7>3Mim}0tzUgfC36Apg=DI>_FSGo1JJ4d`?`a zz1TU4sCxw>jxoqq;+%(%?_~`2@?7d(p13_5+NgX!(2I%BohyLj3rAUI{qMnhuM3Rx{Xfj zYuqudq-D0c@!E=r<0dkf3$pc_`6!@(0tzUgfC36Apnw7jD4>7>3MkO606YG&?jJt0 zTZ4X{hg)1XA!CVa=&sn0NjI;d?)5UFjmRmkZLml;7CvWQ0qoW!?i}Hn_2!u*R_ymG z>fV%ybMj>?7m|?k4CY;R#=HlsxZcX&vJC?AiTMCU<}XIQM0^frE5**y3Di2-u;%3B zIkLxmXKDAHjEx0|za$;=lcg2M`8=a-BY>QrNW`_hGL-kXt^c~sm(HC{WW7e+Yr1vl z_j%Z5J*ULz^*)XPh+`d!ooAxoKJax@FTl8XG?DR`Skih}i8-X3PQ7@2TYyj2ZN>Ob zxm{^_mXY;1nrD@?!|ik;d%2th7c1$Qv2M&_-(Vcaow;0^vG2@D0R5f;kN)z*t{{Xv1-eqgU&A57})ny=K0eyk9&Tqrv}wLNj}ze5>ix4T+4$&c<-v zs$bO7}ICe^mljDgcOS@Wsvji9uPbXrZQ1)^H2^Kq-L9=>&?AGEFbCffd*thVQ zv9qq`Ug}?;$hfT?pB+n8qcnCc)PDt$xx{{&UB=GddnXgXF0R{>x!gc7>3Mim}0tzUgfC36AFqH!A7n?@>Bk9h+mzA`|?69P6-!1HRXi~RG>vM#^ zgAO==FwA;PQjC#KiSsTkF(B2rFQ@J!I}1wo_g&*A?E~`&(DzSD*8iP!-ab#V72UxC zb|T`p`D`WLbBdg+mU<7#hDAPc-3+faQqg*qsT#&bTwkYcCEgR?Nwjhsm%LXZo|Ygs zCx@At*2gMy!R8x8#u9UppID{USaq?CRf#stdnV0?%9Y%DHc{l7RAV`NU%dqw3wu5^ zI`ZC#dD)MvcTY@x>oCf?pZ&Vhy4XD<>FN_>V&PJL)V&&UOH#W|THoSEE5DCaF>bIs zxC5E=ZycYrRuxv(p@0GkD4>7>3Mim}0tzUgfC36Apg@xZ*e|v)k@dVfMSqjR?JOc= zd0WXb4A{>yqLI^$#4082+EO=;`z9SDBh!la-K9(PDfPy2UEd$Muq3x*{^3p~%(B*r z*M3R*eqW{yZwk2`ji2Y<)ZpPf& zqUUX;{@C}Nu@t?QE?_rhL?fpI$6Cj{uXh0~<9uJ4f47QNAkL4uZ0|agzk+&a5!Vvg zO3cgJU~&PU(GkGw)7b_A`NTEfAJxgR>c7;T_4*?puiC0pA7&Z1hd$iN&%X+c6kxm` zO|+?8O0tWLH1M_pnFJW$%M*V|yi#9HZVQ&~GnNtGKYp9oakA9z<_TXV)+=po{q+=} z@23-|5}8Zv!`P&rJy4%kVYDF;?+F>pp{1okJ3b>EIeo6A9reFDsaq$l{Y(0_p>7~o zCbI8q-?$BZX^sjgpnw7jD4>7>3Mim}0tzUgfC37P6kxyKOFKJKll{Gfd}fTywaVMn zy$W$xGP6i0_5&|hrPau~sCx|}j?Ktc{!T*0uH!t?uno7dT$IRoT+--qUu0clAxjYP z+kCbX*D{~phIOEd9K(vZAlo1ypE#Fd$@Av@BB5ENv~LyTYIGgcyBd*kd+2$ib8W-_ z>B}vM!xO}ZO7s^laebZN5N#>3&AxdFOY2mi4g!qtWr*{Wuh4%OmNky2CAaJ*z1SxD zHt{r~*!JqM`qbrgFF=1UMEr}$Tw)zBwx6|o?Rq~Gqo)uV_s7K8-lwEK_0LCS{XYJR z!gd+Y{Sad=$`yqB+y@*$7%om+Lu4*R&uKMa7x;1o6i`3`1r$&~0Ryy~sYE_|7g<4DS}C40iR&{Bl5uh= zHUIxv<1DY~cVE=fRidx7>FgQAZB6g=G1?#V0gBA0qTZMXn04OHMtu#WNnV6kyx(No z9*S#?JYka5Q$9@IA0;xBYfE$xFmYT}#`o3n`ux2m9IaD7f$2?KE}5%~>)|&wNNN-;JWz zIDur$f_ulSDgql9jqlwMrQu5I-KdPWB|>EEKqD79_8n6Cl~D4>7> z3Mim}0tzUgfC36Apg=PO*w=S@(*1o0mh6H5XYV|~B`J%x{jtlk3kyrmIf;@)kRVYI zMMMDu8Bq|BAWBdWBuEe>sepn3Nn!vAA_6K%5K%$JfaIJcBRQ!*|6RD_&Y7J#XF}JR z>8GE%uIHSYp6Tkh?y8>9^9GphF3Rl+;!2xeTa?=a#5FOq-m2z1Y7kw6)a@M^xdqxD zqTo)*;HB!58dwUc^~;XZPOAfaFOC%Z?L;4n#WHFgzlyInPY~Cv8N3GFCvpD~DcD6B ztO2tus>1q2Q3HQM{(PHJNcP!R0@QVkkNgCx!dPR%eEq1=0?x6vi38fEBJea@&O@7` z@uQUwA7^w4klT%svc+|;=|`7pIgbXX14IiU);F0WGb{LCM3h~OA5NM7Bl3ao@hHB- zD%uSM<@e5lPCQtiuUI5?F&$D6D!VZO6EFc2FaZ-V0TVC*6EFc2FaZ-V0TVERQV`(U z*o^3|x@%-gH`C_TiR1$62THK&VmT$VctyGGnaQlGzNwXO*VU!!&G%&F7g*mH1?F?D zgMy(3RQq6kssjuiJr!orFH6yGBdD<*!D#cz`2C(Bu-n=UU+QV(F~;8gLnNO^WF!YR zTO-9WLXBzv7aQK0!O%WUpq~V&>(x-qds>C_m@wb4QJV*qqrDZy>xZp5fvCG!XX+bh zT{Lq)&j%m7SOVnpb4b~|CQzFtLlK-JS_rYG$%HmBboDg+e@v9!!(9Psb0?V1h7>>B z#C#T;w28q_Yl8qhk3h7_6caE36EFc2FaZ-V0TVC*6EFc2FaZ-V0TYlRz{S1= z&})Jh{UWC2vl4~x=pXfKL2eP!PKn%Nu0^#u-5(_;|8Ad9G;lo@sasb6{~JZYt(;J5 zvM#BC14Zj##jK>k>x-hyex1P#R`Gf0YC&daW$+qwpXBi^=p~VSZkfRwHV29D87Yjj zGx*u33G{;i#Xc=s8(oYPuYC~Ku=d{3;!Z23K~D|a8b4T1;6d)hyb@cY>Cu?c%9W2( zJOT1|4pOxk*Ryc()bu>UDP9LJ5G{nCWM>R+s^7;i%wz|*kBHXX^~B}(#sZ56o~rYN z)J}o4^+F3wgI$?`37CKhn1BhGfC-p@37CKhn1BhGfC-pDVFb7^wtDU2nAraZh-Zev zKs7kRcg(A!xUO!@74(jVa0|PO?^y)79aR`0I~qcOTG;f_3mbF)cD|;5|Hu?;i>>i;%MBLXY?L#{`+}-9s6bc31Ma9#WY%a`B8r zz~?f-dT$KrX*Mj9!OuQTU^EF($BUxf(I1h@Ya5|-Y;%kV9GSezAIK7EVV0|7AH)%+IH0w-C_uRX;D< zjUQGq@8F-+ihy{c4#u_1q0{H#`#wQt5z-EwvS{b|_?=gj*GBEcul5{r`B{;C#u(kX zip~!{$DzN7Fd8n5ql;!|eF=EBw+fmhi* z`VW5{ZH#6@p8t3>UP!t;2Kk9G_=;3N+QR6Dezx z)$#$i^92j;W@1X4+o{H`YX4!nDpI^=Tss9$saj4QuL+od37CKhn1BhGfC-p@37CKh zn1BhGK(z^QVJv=6RJ3nrW)@(ZDMBNrd(5q)%>Fc!SJi!!iPbvFMme9H(f|LwiRdLYV8j!1yDi@di*}!j-%SO1MM#s{WuWvt z;{L8kKCdT^!uv_$bAA+KWtA=F{-0B+t{sau0qQlzp>2SUL%&1+LAEIbMt#n14!s0= zZAue-s{jHq@4!W9Z?rP<+5iLg(nd~JL8|>JWBs*-1`PSYHV0PAAw`S&UM&+m{$t%9 z)vde6c`m-+iT)i25wrMhce+kkwEJBAs=cpA>K{a-I#+VLyfdFi z%wy3NNWsp}UyN}ZkXf^sRoyRaV_c!Cx&MFF3*7OWfC-p@37CKh zn1BhGfC-p@37CKhm_UUHaFK5eQk_@lU74wb+fPI^bcg~ zjKHTlhF`!AH1VL$n(1fvKp@5gT!~@~_+VPp1fu6fK0? zvl9!qokiJQOI(GoO;*bXzTbxaBgXEeT73RrJOOG&<$Hg#vUmUg;!DOJO~3?9zywUd z1WdpLOuz(8zywUd1WdpL29y97$W}nFHoH((@@;X=d&W%c_>cM46m5rOZubd6J3f<@ zegEJ8Q7dmjFALR7vrr1{|IYzpT-R;Q4pj1O|EB%#@OjZb0eC6)L5cVGx3*8AvuEP_ zB&2Akbw;B7X9MdVL12~qy_h`CC6ddSGj!ATtGNAV;#cu?wLa};VEw`;8Odv!UnJpiPcv@nWlNiZ}en9F8%ssDedbhBc-{=UW*BU_2jsN}*te ziJ|EJ|I>0T#c^FR4rE+LovHeS*JZ=hU0EM`iH4`lV;^;Z@1K_y%n0p1IHS%v=1fTC zeZ3yx))~A8-6sG(To0kdx8pO~0vWvVdv%GqjMVfPU-kA3ZuVJi0@PLX^Islqgko)? zn9Kh{bPbBWM{4uc64Jof3$$d3epDdGod#V#FeIwmkT}2ogic1AqFMV<59-H+>vZTY zq-Zx2)b#2(EGj-&#r2rG6$^ow4k(IKGe7u`&kL7|vP)T;ti};$M~iX0fq2rc`_#Bq z`9FN`6swu~aNPgD%Ej&2O~3?9zywUd1WdpLOuz(8zywUd1Wce}1h_DEaQ($Gq1zV_ z%QRW(#P@nA=Au!v=pQgKk=xq%SK)SF7AO0iLV$Xi8^!#E%2ss$|22W7*pKQ~;!1m6 zpHg)iw}KT?Ja_&^Tw^n`gx`ti4y0%sBr#KZU$5moKBugm!E4Zc68F9}2qpg?;|9lM z@W!w5n8e>^am&bseGV1@YG+ooAX*iD3LSx>4_}PSe-6diRK?Gygf-CiKtC%5GST1M zHaG-g?vJ?Ub1m8rErVw2XN{;F8;IkvzaV{ML;~b}PNcXF#CJywzRM@TE7lu%4XIhw!CSk; zGVE^n-AoM@@x*!ta}67~=!3-lW08FRf;b8vFNV*BkmBoVE$tr{Ep3NYAOVVHEP6ZI z62+LIxSkMwi62HPkVt0Pt0 z&S`F+07bBxXd%QHzLfQ~YIN}**Dw^v3dZ+`#cFXD{U6`4=EQ?Y#aLbUln9_Uk0l(_#~G%i{$gT4BoI&eO-;T$GQgd zXK=I6auT2(Vm|*R&<5y>=nQlRdJ)AO*NUGp2uses)b9#}=x%aUH3N!sI{K*Ih~k`I z6;0}Q{gmbSu-XMFTGYe}Wy!-Jv=d(YC>8=SMgQ_yG8yClL!#`SPUbcP+?3WIiS?rj zAIqJYgEIPtvHSm*F>!9Q37CKhn1BhGfC-p@37CKhn1BhGfC-p@3DgnbI^XQ*d8B4B z@8CP?LM`+rzGGe8OVprEj^}_vVbwXC%Q)CT`Sx{_%1qKNx!$AX`EJ$xb+Qu10rzTk(2{TeOCq!LOU|ZQ^DoTeHOPJe5q*6h zYX+o*+jhuc4F}cN)kyJ08O-c+@ClIn_^dr0S_AEjzKyO!(a$fwSKG=>V8?z}A^4_8 zca|GtWm|tQk;mFVu{OXQ$o(-}oKUd&8d9`ai_v*+k@ses;iWoX%vH_eEpi*|RL4$S zTO_AOUvOI=DUHia`7YPUt?Kuw8TbFMdJ#B&6EFc2FaZ-V0TVC*6EFc2FaZ-V0TVER z(i7l1-;zkto+hY5`_tAZ8{e^pz0$h6^);}5eXK>^<{dq7sTQ>SVg-lGk*Y^9VzyYh<+a_{iPxw_?`tR%pn=SeSb8w z#_W3v0a#B&s^e`4?Ty!xxu!(i+b#Spie3uGViseECiOMdM zfV@tN;=AkS=xFqN^e}QiY#>y?;@lpH&(E|Y)|mKR1(;VQ#2A}>Gx{=G3r*YqSG@kU zfcT@nRpz=Nlp%I#|Hzi_$dDU{o*zO`=T7>1GN4lZKE6|CHN)+@I%W3y^_i^fdkO(+ z=2)S+X$<9rDUte?37CKhn1BhGfC-p@37CKhn1BhGfC-p@3Fs2w!q^dw7so{I|0FYO zFnYHLr^YZIWkcHUw)kGV@qi`0|0?ZXIJ3s=dkO(q&yAiaX|1%=?3mkX_9+qfb_-u` zZ|9&Te9rHBBU7!tqo&X!CCu|kP5xt6COPm}6zc~m+Ikt>0QC+gSQ45Zg3GGqbL~h++q4M8c%tb0emq(iO+Wgte*HZnF)xK;yp5u5)?d2iIywcg2vQtl7jxijn3)ay z$F;x5k)kb^%nWUuJ!@@=eXS|9>ku-fjXWU;-v!0w!PrCSU?4U;-v!0w!Pr zCSU?3A;5LMUkP0wt`Fsa%oK2-&($AH==P1t+&&}1Ds_E{%p8=_H#MW)|37};oTaGT z;A>Bj^QixiGArU^!!bqKyw4S4H7fXO-#@Myl&^G9FbO(AF*-Rh^{Giy1>&+YO znFhr=6gFJ~(eL>l^feUYL1D89R@S*!;!f6=^Z{_s5$x)&)}yoan*J>23NS=l#lnMoKpXOHFH`TDGU{mX%FFd;rDo z&1&`#0Zqu@hF`_kn;08g_}uXs{EPHS%+sPX2-A5zmUxYnh`i?(9RheIWziev&MRxB!RyZ)q!Z~L94|g+e3Vxm(1Sl zuRlp#a~8=;-Is7JJeHk1*li|y9{riPGS}LDGyYXR3&vRdN{k!JKA+n059?ndRXd~| z27ZnSn1BhGfC-p@37CKhn1BhGfC-p@37CKhl$ih*_ZC7gh+gEIH#0XdyG4}Sr;@p; z{La5^CMWwIN`P~1Nu>H(6)z=Mp+fqa;wR?R{5?{$=##zP(8}<=;RwL%AhjZx9M3w# z#ofze`2T_^vkQr9nqJtn+84h!3NkyhRlLgE$z${y$9%}P>JWH|P@|vW=4fJZ)ifBM zi0PY1(SA!%g!8|D80 zI}eT4_e{V9Ouz(8zywUd1WdpLOuz(8zywUd1WZ7W0N425jTHM0JVjhHW@QZD6H&~8 zrD!W9a+?ldj|f2fH!&=p#mRoBCIIiH(aQq0(<+2ZrjEM1EgT^FhdtZsKqcNDbD5>R zu6IX!u=j%3*(J_*tER{6Z^}IOkxhK7u6_QsjB16~IHd5tP8dUM*9=~R?h^tlB8BxY z;=4`C=QRBO6e-#ngI=FiZ6C!p7up(~hn_(;6X=9MtnKl(K|OzH+jb~E+u5o|;2uK$ z5=wc$9_^YZj1ydhlQH31Va0TVC*6EFc2FaZ-V0TVC*6EFc2FabFNT;tnG^t$@h#5Fc6 zVfbDJy(Y*mVwyFPTg>qneGtlS@muwzEKc@2H34`>pG4)pA?HsWb$46f^d_XT?l+Z0Hk1H$Fh6=4DcZ+|MqBJX zfW{z@-`(t>CERvp zW_{WB)C8!J7;~d+-<&$??zRAFGZeoss@eAmXkhy~NjMf|73M}Smw0lXU_#HfA`d~4X zs^JA*%OO=;Ix~wvn}`(W+KJb<<+Iq~JI2!8iWKZ&g7CVZS;f-CFTC$mD4Z5C4D1?M zO;X`aCSU?4U;-v!0w!PrCSU?4U;-v!0w!PrCeSefu8WO9m$kS)R`$(%cMPP19e|@c zN8JS-VCeX%a67o{g*CbTM|T{&cj$=#+}9APomOFdz9&F+J9B{jTOFk2+rL~Frf-fw zfLyPI;&)~>J9dQ7yM7o@i>Sex98b(0Jzv*ghS?Fnimx}14imEUL*(<ycugH^Q>@k-$l1IA7p1J^EQ6Ir-3oL*QRD z<)&Sb51pl!bsj`0wCsn zj(*=Z69@vYpzokktWQHb4?>FeR5#~ymG_|tj#VrKV)_#*e0{C@TEYKLqU>%WuC&kE z_+AOA%>D4C`kL_Vd;-+YKarw6OHhmC6Z8>GzywUd1WdpLOuz(8zywUd1WdpLOuz(8 zpnn9o1~wN`oL492yjd!XEWXDhmHttLGQ`sQ*K(AYkLb@sj#kb)G4JcjS@mVV%?ZF< z_4o8R3*vS71I;7vY%6}oq8pH!#c!D_bVj4v&xY5DYDGXiF&}8!>o2|!6J!=4{Vd~b z!0)u^x;TKC9hSjs(0!7_g;D%Qu3#yz6M*@a0-Po>95?9osnR~!$2gmn(T`B{ZMB&| zHi1>kbp8M%)&aOU8;bpxia^XczAGB&b!uqeC8eU;eq*mG(`!h-+X~ic(53C;E!8tG z$>s}<(@I68{l=b{$&O}h*M3t5{+S=kWM$t|6QClV8~Cc|u7MA++sso(>@6l>0w!Pr zCSU?4U;-v!0w!PrCSU?4U;-wfLx5{wn{;>iY~c5LG&4&?AL}N>8rW(U>!POh55RZ) zmaniLarD(0msw->Jv9NCEB0k3tT!yqy_Kepy7MjgnHwq2-Sh}?&E7eY>h1wA#Xcyp zX3?x!S;hCSkfMDdgBhS^KraXa+b)CGp!+0`D~jaudE!{SXx{jY{#=)dFlr2=kTHlw zGgtCAxqjZk|BFzJWwV*UXb_0+7;}{ET%yf!-5~ma#u#av3DgK&&5gGVYCY4onBQB` zju}+ugV-(#AjbJAuCp7peLyyIj-fcVJlO4%$qxU=$3aAF(M(qMJv9O9=0%a(X$<40 zsiXB46EFc2FaZ-V0TVC*6EFc2FaZ-V0TVC*6VM{SMX}E{UKkU(|GUgg!RYNGoEpQ3 zKEpF)vBUS98xL60`}ff9)HScOa!_W!Fh8TD_0mqWqyKCna}wca$#xD{!sq<0%=J6k zZ707I(H$ks^GHp;HIpR=kNLqAZL17!fLcTm7BMZB!E4Zc68DZG`Mi@j#um*RpEIEc zMHtnG5&eTx)=MZl2W9mK-&N30QLJTTGl9`05T6CpzTan_le9bf(8l_2@fu|_ff|8W zr)URM`1^b>^-OyfK#F7V3G4NpdSO$wec?3+QXJJ`ALW*--#VK(^COi8!0TVC*6EFc2FaZ-V0TVC*6EFc2FaZ-Vfk7a^ zb+B{dVwj*EmB|-gy9+XlknT+8_Hhwz=Mu*>nXK%4C;@nnMSl>fpJrh^IW*$#@8Nvw zW(N@1_T%oURBuo69qT|S_B%K?E1U3I7{$EdiZ*WsH$ZJC2rK$-m2R%&BJxYzUlGaY zHAV1f^Z}bW(SJl3)rau}cP2H)>-)VEfa~JuM@Y3#4WZcvoWM_eujKxDfNiY1@)4xk zuX@1CXJDHbW2w$W6Z==4Lyt^bmLFJJy*F&Rq0#!@a0I};F;ZN+B+k1fhl{<}$HG5S zyoM2L$fmKw_XKn^Qm`8d!t3q!dNOseKfKpMs$-Nd=m3SEY62!;0w!PrCSU?4U;-v! z0w!PrCSU?4U;?Ei!1cY`kfQC9nGk@Ts0fXizLU)DFcEHF$>e0;Qxl+W#-XP~>Ze&4 zf1Nt&?zZ6kh-L>6*!G$3h*Wbg^1Zr1&YOj>PgX`b*t$ruZ*TOOn<;}Eeh)@!_FDoP zs5xaba@XjSn4^#7r6S2ZyfMBa?!)FiB77RdxUC32_A!M3fMf38_}&xiJK0R2p9Cbn zw*oNcru|btLHgJON#JT)HQ$t1-~QdA9g1UOBk9kf1^Y*~d`E_t>U=TrnI`o+68_gm zigpJK z;=5Bv-Q5;^sr26rF7>u?cSNeW7vT1+hbMGO;APJ1KoN@cYa91Bu)| zKdZ(D*>BVcdQ2pT+h?$b&AuXh8pCiun}P}kuyG#6T;+E+LiT+V=o5i03M%iuyMfe^&(Z|k{*gX7@%Ojh3q|O5C2PLu8lfH*LFXam&#OXZUx`rSHGR#Z zYrWt8h-o8qPbD(wAloGnpGOz%S8a0axYqU4b{ku==Uvt>u4O$g`Tc##a@=lnPt48< z4f#&HjVamlQrW@cGbQ6OTys;#ac9?Vnf;Rt)iLf-XCEnZZrpYgFaZ-V0TVC*6EFc2 zFaZ-V0TVC*6EFc27>NMaz&4EwVS@HoV#-`scNzSvtgBm}{^(yj&q#IC?c?~4J~7YM zAF#x?m+zK?_v?-TwW2yNckMLTt~)YA_rlrSNb&XQapD?3G+N&qjsU#ky->{_C7>CG zi@V#$@H+-wss?OwJbQJ^Ww`zDy%2gvj9K*C-E_FPRUaen_aTLT%P$f~%KHU=Z$paq ze$~1jjkqX?c>mZAJ)u|}j;YiHo}=|CuZN>?ZVh(~;+hSeg1i<05NvM{xQD-s3|BW< zk8#|1A5d9C=?$25e-6pwrsx-Lb0Ed}7@{On*FnX9jH9_d4kTzhX4X5rl-3_vFO!vh zPfdXOIYaQAi;&{l#*$M<>@6l>0w!PrCSU?4U;-v!0w!PrCSU?4U;-wfLx5{wN5{o5 zLAxL`OYmA(kXeNE0GgD=4&Slfgo3?75O2!jWWUV`!2C-J#WPF{OEr(Yv#t2w0L54y zHM^LAQrDXAjPzhX8&=bzr__poc%pxPN*&;L2Bg?mBYyMTAfsaOy9!c!y?K?GmdM~W z=st;iH>6Og`)TUkt%CJ+Q}K%@Si#xbMMx5^qygfhgoQGXpNGCs&Hf#Um@qODORt43b{i)$E) zb6G?^tkoEAk@xZc2c%%}I=X6+Z0u`f0xqG5jK#+b5eGvy7V_r_pm~ zva;`~3BdfSG8RjlZI0hK=bbw0&bQ!ar#6pSwx^}8m)<$)!QO-9E{|fYlA3)pi#(ua zRf9D-o|pOmmJDwA-A<6%(;2MF;FDaQE|SmFGI+ygHKaJt^h3loF@u@9Pq@7u-HL4G zBoO1*qQBGpNHvdVsGo-sANiYrc14dNn+co)f|T*Xtx-aa(} zcx{XnExuEx&Vz#g_^cf@r)H^rkbxEJCaA2n`#iBMI`x_5Em;KM9pi3d?3|+AM^NLk z*x9cMn1BhGfC-p@37CKhn1BhGfC-p@37CKh^qTG-;{Q9M+)hpA zc9ICU+lZs^d1W)%m)$qK$D!b^YBv$sgd*~UpYMu7+O7ybRq;U$ykE2qKAx3Bczr;W zS&UhmCW9M(PZVVKn+#TE@JTMO6Upa(8N6Y$sR*COFk)Sml-G!iJPd-*;DtVhUO={T z5{PjxF*ZH=Jk5s`*GwX;*S|0b=gerefq3UeN22GDt-1uFAO0SrRd;b5j<>rg?s%UY z)v*+#nYWB%!Rt`bTDXh2rq5tj^NIhhMcKs~Lt|@k7X2UJOQF|9*gZ-dp0B;=A{oXX zyk|#GiqubI7#Fg^-*bn_zaKII6EFc2FaZ-V0TVC*6EFc2FaZ-V0TU=U0j__|h!oeS zj%(g&YsKO_`Ut43t4k|t*Uo~=nn&LG zRvcblb1L*d`f#k?IgRS>0k4CF@?Q%j=ILE0E35e4w{{@0|G&>*2BYZzdXX5b2vNFW2frATdOT8`pSDSP$ZdLth>kcnnLzywUd1WdpL zOuz(8zywUd1WdpLOuz&NlK>a?))2k87r*IGmqi`lv9|n+g6yIX%le7jqOXj~yo$fe z;$*+g2}ErQo^$mf71G}nhtYrYfqKUxxBp2T^Ea;!Z*4{ZUOy7aeSH|QrhQr+h%yU) zg}lBr)){`|ef^O*fS7%`uoA>9I;xjg|mGXYP^UT0T$l_&#i&; z|56dSpVl6N7Hq5qwtWIAT8x`qpfTPm-6#Gxp_9?e$W}c9*AVik%p=Wlej|$atit5PlQv1aci$Uynkl|VE%5Q`e_Vh zm(FPT*(P8DCSU?4U;-v!0w!PrCSU?4U;-v!0wz!{0$dQ=weg~u$o)GrGXbOMAEju~ zUuw22cKDtPMIQtO`$ZNf`)y8uqB%mLaE6IttLBk+wiW;L40DV!Ka~2q-Wlb=e>RDp z4k@lv^FStjnB7w5lVq5-e=36;$D0YMysy{eTQ-B&p!)>Cn)MJ$eEUJ#KCWoq_>6wv zx0jeptER^}yk60~6#cyzye9$o+?txqVQCQ-pDLCM)|MN&w!`XXDZUFKYiHuD1@2y1RS$d9Ns= zpLa*3ntM?LGo$G9re>FCWfV}sOtnwg@)_LVwUJtZ5zlidZH0>?t&HNgc{O{5 zfac8LhTl)C!6Kf&qUkbt4Z2U_{)R|CkIP^Un|O^-w1+ZyDfuMnG4KC1ipdv=Df-;r zgU&SL)Zx`eiA$>TDTlmGlO24vTwMQl^`yNUF-UkG9VLNJQ z#NFS6(OsgP7U-Tz_4cF&4i~M1g|e~X?zB!icNPe4sT7c~O&{ZS_nbMwdh&6z01H!h3Xp5K3@TEy43NYP?U!+S@H z-;Ylr0IxXbH%GrfFCd#1fsahdcCoj>bgCAr|3&kgVoB+HgWNCwM^EcUjJZplM;rfN z)k5~aXwJ&4e2${F=0w_>kC}NWt8eOOZ4~Q!D4M_LmbEb5ZWAy86EFc2FaZ-V0TVC* z6EFc2FaZ-Vfwl;6QEyGf3wy-$Aey-?2wgl0--{sCc~KVc0!drDz%14yj`{Rd%>Dn{ z%6u_TqG+yGDV!E@Y*Yj(e8;>^O6z{taK&rx#3C3~#Rt5uslgNaA9XNGRu1ufwh*iO zP>#)D7P&wV)gMUUcFg&^Wd^T7_etE*pH|^_|5u2k>v_P3%M*ST$GZ^L>)Q+$cU2#Q z%NX>n0Aw3A0?}XnWpp=+zO?(Gx1;${Vg3CZYZ~8&B1MbW!&w{S_5G0vz%9naEsSD) ziN7L+_ea90y!{FSDgBr68?Rf6_SKQk7(b&%fC88a-J@6t#PsD_oLT=l#towEK9j{P z{KD)oF>dbvpII;szNwv^#p_7#~h;S_xk6zy?>nlpee~p;32t4?nA+*N~dU_>ZyOQK{x$@KWoS9W{L|nl>Y&_?-^jffOxeu2Vp%^cSzs z`M)LB@}CG#d?N*T>wH!Exsqe zsr~G?_e^}ox)CaCC^e>b$Mz`rSwR4JaqUWVywJwkS%zJ#_oHa<%VYf3L8Z&?p6iuvwG{l>VW80-2Jx*SCx>dnz2X!d^B z8*P{t-HH_Lf`0Q=J3d7+9xaUyK{0ML`jgwrMc{y<>yq$SM=^#}&EoYt<$FZYIT#dw z@LEHy2qwq#FaEzRlQaHfYU>i-)1zC&>ZcaY-P{;o z7p`xbfC-p@37CKhn1BhGfC-p@37CKhn1BhGfD{3)e~m@Itz8fk`@d6WvS4+B7_VA5 z=O%NDz8*^E{{NW;Q|FtaIZdf>n#A&>x^R2CiSJ{Z_*dApM^9iXwlfKTCsLf7bliyvRT_SuM z!-(&LDeJ1#^DEPBc+86)Xawc^9TBMY_5Uh+MPL7cT~~#+Erwn|igrYq&b>0Wm7-Z0 z?T@ZO(LdHU_yn${iG_~!#NSqfA9hzQjPWgnuc=hVbG_X*!)w2;NDux_v0l#{?Q-bm zdH9ccKNQv?Ys}^&Y)Ctg@I60LTYtp;|GPPfy4Z)>nGY#mJ0z^v&+Gz;cQFAIFaZ-V z0TVC*6EFc2FaZ-V0TVC*6EK0&5a1fvbV%`c?zm>TVOApWJqBHl6zwxv+`=!w&Ju&> z{{NW;Gubya6LWGtDONj;;e4VflsLYi(Ye6=_zn17MKP*^A9(F9koU$Aq7F97${)T} z*Y>$9k=f~uNfNk!VivDK_DkO4^X=~i@)sebd=J3y0U~@F!#HPQ2AufZsWKL( zG1po5qtEjb=n}LKS^>?9N->s-cCLg}ZJW_PhpKu!3Ml5+h(2^Xql?kgNUMZJfCu%1?x2Rg`?_8bJ_8ul+0w!PrCSU?4U;-v!0w!PrCSU?4U;-vkECDX; zEs7L>k9?W9W+|4|6mR&BJ|X`^Y8JKd!6}gzdkf#uuk1cCXzu@CY?(CmM$K%5)PBpV zKNbGXoh6zE-{D65Oh7l+I~TbfbD6!VLmZWUI=oI7$$fno@&3MKR$}peeEq?MZht3( z8GPb>{Wc*^wNUoS;5Fzz34U&*@cH&t;#e_*H-5j16m90c!hB=^*Ju;cGSv0P<$@`2$}^I^7*yj^+FuqK#CUYr_IxEzCQjN>mbHw zrUlTh=pq#7QuIr+^^d?YMb#!D#2Ea)_b-&xBOg{2rz-d%6R{4+v#A;BpYy#lb4Xb6 zSzGn{_}0nn&g@@Vq#fD)|1*nbvTw2%pHCi3%VK{WM_dy}0izFL0w!PrCSU?4U;-v! z0w!PrCSU?4U;-v!0(~REHL!#Fdl@e6=oe)|rPUuoigUL`|A5T(h~J5SwQ&=l%B(f} zext9%Y}CyoNZGz#RIY*ac2Q8*7R9IveyD-ylc(6H==WLqgIE0Sc`s75cV}<|)XGTh zeLd#0UOa=>p!+25&5(lKl))M>)1#;lHG74C-jczrY(C)-efG{%OK<`4#CODRqW7Tb z%a+%X+DlBamQBpfu4p$9RND9Zk=Xk2)&$5)%#E-*+7Dffo;P|eCSU?4U;-v!0w!PrCSU?4U;-v!0w!Prk_5O8c4=G;6SN~U69%u( z3Nnk3;`f5I{sH*@L>xrS_Qogm~U8dEgQmm{X*s3sl-N} zqVMJmXbp4#`VD#%`Sl10i56on#uZUV_*p)&o&9tj>k2Mk1f#0>fY*onnXtrT$LrB- znR&$jl8No_r|YLV_`F%{@I48|dxo0*D2ti>P9Z?i9H&+|b@8OGSu%y9Z<&Azn1BhG zfC-p@37CKhn1BhGfC-p@37A0t2yi{@=DKTQN;k9D)xA&&R$VN|W>zD|`Fh>4RBj%e z$;`gr=>LC(O0l$vnkmeQ})fDD2h=P{E+OJr&rN-&B`CJ)>H&WOb?)0 zGPvRQXhCMb%3xInpXBl|1n}d5-nY} z9Cq7YVqFQTtz|<LMNOMvXf8h9}tAl3(nbxETCsK)~Up+JlIj@K+A)A;#R zf$bV3R}a&enTlXk6(8_AVvrJ8U|XKTSL%2`{HuP}zB9A_;WlT1c^)KJ=Vo%V?>DZu z#~PS_9i*fe+?L9GpK(2#fC-p@37CKhn1BhGfC-p@37CKhn1BgXlK|J#7DUnSRMD2q z%qqZcRD?!MJ0x?vQjpuXGMQD~H}&#n^on5J)Q5Ckk$J}P_4Uq$Zoj2SPRhQ>`84Pb zq1@MpvP@R)ILJQr2NJq{RU$LR_w^&Pcn!K=YF=gD$Tc!p!$-A0*YO#=%I1^&?ke=U z-%Tj@uv4sYSGj(k#JpWMz)G;s|D%l`F59{2_D)TJJjQ4Hap-L*)_S`L-HoFEx~(?? zn@>%=^DQ*t*xs5kuzUZda~jp&gG{Q<7jqf0jmgX|{#8F~UzE)4g9G~t(p$rRlgZ7# z-{}9pOm8U~=)Lcwbu&26CSU?4U;-v!0w!PrCSU?4U;-v!0w!PrCZJ4!YhZ6h@td%k z#hgU*WaSRubEC)ApiPb^ZC!8t$DEqC3v#b|g=VN_<=aAy{J;F-0FBXwiTBte(KjnKw5zGhC2XzpiCsgOs z{$@6F*j<4X?SM>n0NY;?8ZmkNQD))b8^*_r)=q@={bYQ0ZvrM@0w!PrCSU?4U;-v! z0w!PrCSU?4U;=$7z;&=~MX&Mwk+{<4B*OR7qTHS(t_8CS6W@y>#r~+Ck0_~VsF#`1 zgGkw;k49nZ5r;4Ik|3~nU7s$g)a2a4D(3H1v_BKn_>6?%H^$EVMiCk@eKLvJBKTH& zy@}W4wK8}Ox=-T%2vX=b7;B#tKF=RMWA5cUk)o|Q==GW1K9c%r#l(&fQ;emq}m*DAG-JjZ)fjLUpKit((sAcgmI!brDsil`M@`E+_q`*VQG zj}h#oK1k6VNMSr}vufL^6BgY(mxHRTMb^k~i*9(dyAKEZVdDWs?%&%k13#~G0@O&1 zBaAunRc-6esdx_)FaZ-V0TVC*6EFc2FaZ-V0TVC*6EFc2C>H@P?y1i4@srF` zR?VW1+WdoFpPB6={z>RwR3(e?bgO1^EsJkB#CYTTMU#IuaZOW}T)3UZ5g_j|2YGx> zI~qlO$LB#?a{@o_l{{|Wj8yx4zq?mHRob6AShjhYm40iCCt5o*m-t^8#n?nen=hH& z-HOnN>ETRnRrd|w4Upu!yh zk;?iP$7N&+KC2)_i_cnxua}UKgDjuK|53%nwumXlIGu#1&*E7&zpz*asrG&PO4;(^ z_7+co`i^UV??XR8u_lCVD1qy_JFXY<>>ukcD87!wcgjkCM(DII;I%tawC4zF(N1}E z@_hWi2Pt01h_yk|t{LNdRutc16)o7!(McWox$O~vYkW31N3m!o$8>#rB>XHBFaZ-V z0TVC*6EFc2FaZ-V0TVC*6EFc2C@%r7g~j~9j|pDqi{IQf%ghY^&llttAsw8|?W>~P zVosUGGTBwpH%zxiiogBUp9awftI)Yu@pnf30fcUEUd8jEo0!S@!9sa&3}uUMn6$GG z2a4AmMSGN>Qs(}}?{R{_3SE<>o!T3O=a9#_(aR$F{7?o{*z6|4r!kDw@xK|lDAFe| z;ydy08li~Ze~GxZLSu?#Sax6V__S!gBdp!Zo*%crSOV0<0_XtrFjDNVPFNk=1MD%W z7t-v%Psc|s#}2V}#Q0v=RBc~)DSkhSYP~-@mmFi)IFO+IikJ#p^RqDz_+A73OORWH z6muCY-x#y+_f7z=@p<4?q-y*2PRSljzywUd1WdpLOuz(8zywUd1WdpLOuz(8V6X{r zaWB@nSG+zP{b`rVqL1&GA0>V(RkV+0aSOiyi#~O#=HDX(nMy_mqE>9 z4!o4{5?OgD!Y?szDw4?P!|=8um^AvpZ_JslXesL@H1ZqN`y_NVRE0jUPZ8&OgPMna zwhJy7AXQtbpKN{X!6iV!&xrOy_o)_t2XQ^ib9dp59LU-985po0SPhhi#3|=<9UL#{0D<09VzqN>O5Q z4SDg_(fbh-FaZ-V0TVC*6EFc2FaZ-V0TVC*6EFc2&>+Bdu=gXyz7DY_aoU`Q_@0Cm z=SPWaqi@ctZhXhMiO7wrrOY*(m5)L9OOb4>S||;19ady6@I3>H{tIgMA^|N>Br8>4 za9S5BzPCqBKaVo!&AT)XB4|ft)H|Hwef<$ZUZ-U68g!rJ@f49 zq^cHkh|JS1arTWd_^NXkoX{qi2wIS0H!g2`Av0f zRc}Tg?WKF8HFR(MEg}l*$)S<@UNZvZKKf(F9F3}WbF;XM*aom*_cu{+DQo*oW~SIz z?D!&Ts!Tq}W%OHnRHXhd%U}!`+nrh$IK%@tt^jzkE&?#8Zd9SblEq?P{cSZMcb@fE>+x* z0(di0{C*U*y+dYB@jnxKJPssg@%oaw{u2Jr7h~2KPNDOrWY%$6d;?hZH^Ex%99WiI zxSb|o0w!PrCSU?4U;-v!0w!PrCSU?4U;-x43jr>OeXn*=OzeN^{8X^oNsL!5oIi47 zOfNMx+`jn!2vY4^5e&~kY2!|YW2^QU9JfcxwL*Ar*l+neZISuI-?smCE%*1PA~~u0 zBI}DI<#ATu%*q|y=0{H>MSDvIH$W|e{v`i=rR6f*p{-8Zh%B#qqz< zN3GD$A{qR%d=mS{NZI1^^ln-F6#T;HLg50qQXxl55sb0%UTQ4?h1#WXAMT@z1mdrXI@I5`cM-g0IOlM{BtD;}H z#=5b}bN|Ph-EXa8ZXL4;n1BhGfC-p@37CKhn1BhGfC-p@37A0j2yiXzI;3cOW@Zy$ zXDC7=rf(#3J5ZEcEv$cVBW?UdW^M+_Hw>fCf%?RrRa~G ze@iUyjp6K@l{XHwEK=;N5n~$W%ixCJ=r^m#Y}X84gY1($ehsPEDjBSS^I;W04RPF= z!K(~D;SlS-sE&uPrTJWLRCavFNOcIC=xg|F4VK`4&zCz=5%}?@1UMHygJSKFDp@c6 z|6?5yMT<2J=4@Id-q@4?y!KNp1Y(NuqLZ2iUC>7S$DA>W_HaRnnz{klB&0YdzrGgY z_bY#%ZCU}HZ-nLN>fs9B_WUuOmxcE*0TVC*6EFc2FaZ-V0TVC*6EFc2FaZ-NApx%K z&4wNlyv7$Ht(=(>{9h}|ZSQ1mKN00t3oCwaSum4f6@0_+%Qc|l|D&(b!bRs4pT8F6 zR=N4OJFN}!9`jB=?nubE4h*r2Z_O^bd*T58Rus zT@Kwm5B~=YIik*;tGnUR?ml2zM+a5QNaFMQ4DB-U^I8*tFXkj#JS$W9o&~A&ks_2Kwniei zX*p8-UZiX>2l6&q46EQ5es4j_{jlom|Hnn=6z5~nef0+sy8YRrd8zp$*T)FuyICl& zv9a(mGqpMp=67)|#-jhjY{zDa64 z|6g^EkbRq1hPO5&z)3O_dS3Ko`C_w}i`)h`)iHTH7Rjm67u-%3t+~dqc5aN__d6p1 z(8bV;jbO#@|D1SI<^$-Awx4YRCSU?4U;-v!0w!PrCSU?4U;-v!0w!Pr`2@HYc97Wh zy;?ZG&ZNgd)~_8%?0@{uk~wZ-2{B%+!ucByxSvHACn4NE-U_PV_NbG>&o_Loj#THd z-LQ+Ys?09r{S2gTS7s#?aAVM;f&k<7V9P{qXA3fmkXFrNHfVl{RcYK*6VRVjlRr7GKl6WVx18BG`mr8`P7>AL z#5JZ{K2_QePVYp@V`T{M4eP4^KYPYp7tj9Jh1}l{RjJl{jg{;#ELQ=CSU?4 zU;-v!0w!PrCSU?4U;-v!0wz!`0$dL}RqPsHEu61qW|D&(SUZr||KBEa`JF zVZ0}$^*6xx1xV4h>!g0l{9KZEq)-AM%gP0emKEYuAIgK=nKg^sAp0fmnEz7sz4KLK zTVRm+_q%QI`4CdKZ}-9(IB!9(2t&H07d}xD#oZ$6&V! zQk^e`(B7~ZuRn1Z41UN2Ouz(8zywUd1WdpLOuz(8zywUd1WdpLWC(CAY#J2TO%*MB zUER|ap%K&ZnFWsj!$r9@3u~WDmTJBM7;_N)v>8azZ810Cd>MJfZ_LrDYVOC@q-aQX zyq{mMn)HUaKG6h0A)7ePhDh$!hNf{X88Aw^58wUL;P=Hn!8R`j?~0>7EX z4@Rov>r%)6X62&@zrs0YWJPevDY$kFtZ;0C*iDl>horp5)H79iZs=srcWj{(wTaZy=U& z9Rq1|2Yj!9VthswEXE`*-zFMAt&RZPVx5|YD^MiI)`jhxCSU?4U;-v!0w!PrCSU?4 zU;-v!0w!PrCLm3K>wVLqn1e>mqVL}7naRWd?P|~_$MeZ#ZqW~=Dt1FAOEupV#U4np z-)*aUKP@YV9Pm4>4l1yH!z^xP_Df#(MpZa}{zIJa%E}eKKZz7A`WuW%pVUX_s-%vQ@dMWg@aL(&Pi{J9bHy|kNF22vGKbX+%ZxjZpi6c1u zT8LGBC`UI5S85Z%&V!Ubo5eZ?b7mw7zw!Qh0aCPGGnkdZC&~PiVnT^2zT>;^ON%0e z&79~7q;B!Ksi*H8>>p#amCgPCTbw7YHd74B>t@9KD08)nz14R1iO&eCYZ-pGRovd* zoB*|OFH*EqnnzsBR)8sg);_oxO3l5%?qJ1&8z!bpdCcRInwLY^ymzVI7!I^C!StS+99)nfJk zH^x*=fX&r)eAI8=M}TR+<>C8UQGlPzJYR&lrS@^j%t^bx$=w&lYW}=-WAF5Na2kXD zA;v4hiGCm(b;?8G`NXX}raRs*XHv*h(J{a!`WK#!s$fwEJ$=7t|1XJy+NtOoQ};(b zTr6A@4-wnUo#v2zW!a@ny{9#wumHLxIhMzj`E*G7X`T-&%`kX?M%{y^q=f&ViF zxeXUm%=@%JCQ~Kfuv!UKVa}a6*FL0_JI7)ICSU?4U;-v!0w!PrCSU?4U;-v!0w!Pr zBN5=5nCiN^r;QYQFOS1*OQdMG5me^5i4E`{>#0}C;y2(~dZ{<%ePJ^msm_zvs?K9R z*7s!P72na9O|gGsjpt{x*pel`L-I7htO6 zCVok5vhxZO+oDLRA3(eoZk1K*_?{WXSXniTG0n4PaT{d6#2sV071zRx8oh3i+4r+; zu-OzTj^&TMH`)!ovc};_jZVexr@e3V%6?GyC;#6OtAYA(e$SnbuZgkGtVr=%SH!g5 zpw{9bwhLZsA=U5aYh^Nr+a#oT9z|dEw`Q@!_c(MDs+y(Fagdd-(e_JG#QRfK=G?i1 zSjUYvV;|21Ouz(8zywUd1WdpLOuz(8zywUd1WdpL`bmImVwWLBJ0g=Sz>ZUdMoecW zbK6;z+el$~zVa4@1-PR}f+zTRYJV(v#h8j01-aEiI;=%GRcnYoY*?<1D46UDBr|223?4hEXFd-SQMj1KloN1$NDI-rL0NR z$Z*i^6Zh6g)xI-mya%B@BztZYbK+OUPVALCpuQyzYrkF@Rc(LjVNcYV&KJmB|xcs^eH= z9j3HCNtrq6)i-f}8L8T*d*!3HKZzcT&Q(o#i@0{{C6D-w?`%J7;ag?%k-adg;=UyQ zM3v+>iR096a*CgQn)p)K^l&#ks=Nr!7|o-k!^;-?C386qhc0)mET+>%X(kfs$pcc|j-K2q?Rr^?8(G8EP z?n52KYvyIOViNn`)!G=H)B?Ot6Ju5j=Y&o;boyKlqPpfpjo)zpf5C&59LGFdodgq9 zwe@z78;*$|GXWDY0TVC*6EFc2FaZ-V0TVC*6EFc2FabpZTnAhMDcU~>YL={=;d>sW zIJfp|#FaU2ViNuzLRGcf34Erkx-64lIJ^tJSiwSwzRMrZ&K1W{@BbgQvp_b-LG@4U zF$V2dsEYO|LC=+y8+@NC3iT^l%xZq&rriI(t9f8+YzFN=xtb1LELiJz5>xh^q}K^@ zi;zA#XmvUo?IG^y7o+;x6@A<{9Sx3UIR-q|Mz5eM*}n*OVlO#j|0?wVzpNKVRos`l z_`T?f6yMjoniGTlZW3j3NEK_m+nA|=xEAw-Xf2%FEthupgWKOk+3ndbgKnM&voDKs zYbUID-F{a$+*J30$5?a(3N}@-xK8Z3|5c0HahZS#n1BhGfC-p@37CKhn1BhGfC-p@ z3A7}@b+O$=F9QChWzYpY0J9ZExy3rElL|o8)CIohLRFh@v9VMRXbPu78v(K=da4mj zMehHBILBnCn`6Xp&dTRn#I{*B$3gTzIk!LIe^u><%)9{gHc_xUW^xPPa8vC=8zpfU z>Z$)^Hvdli6U%`Sl%O5a35QOf3$Izw1A^Qlq~$y1q40d-o)sysgAqB6&q^yPWbYGFGr6EFc2FaZ-V0TVC*6EFc2FaZ-V0TVERmISyic3fN_ z6SN<;47z{^;I)?^vk2*L1>kAv0{aL6B|qKUpKYmN|xcp96_G1|KjN1YT$_HX;jHIb%=XDq&gpby!Nk{c}|qYHymb1H=-)q_j}C~(5le? zKVI7_`8wE5eZXoZq&lbY13fTNTqBfJzrI*EOscvMygnGv1T9`CQ@;=4Kjzwr&-7{* zuMP8LofG(u^{nnvgVsenJ7qC0i(i<KLR}W$`QzEK!y`xt%6p0w!PrCSU?4 zU;-v!0w!PrCSU?4U;-vkBmu6C{kGMGvyyM$yGTxrzF>1=$@#QuerkVO4!VD<16OSO zmBicAJdBMsS%&+>yeW$D4^_2a5cssCkt>cM}i0P)^cc6#x%bvi}RrS zCH7czIeu@bqJ4AF^W*lJfC-p@37CKhn1BhGfC-p@37CKhn1BhGKo$Wm2>w&_qTmOz zn1x@Msm>p`XEL`lMC+}aunx>*J!rlGv>Q^LU$ve5NB`T^Mk_BIs|x-9gZsxui)lZP zL!9%Vt5DVL{{djrtX$xGD^b8P7hw9>BaRVcoRsa~1h;Bd{bl>5?a}xDIne~gYr}@4 z!0;eZewSsl8&v<~^Btl&jj*Esf91v-*Vh{H&w$QFRkof#HvupD|6fr7h?msYk8d|8 zKpn(-S$7K8!_LhkAAT!54ijZ^9dV_9j~JeAxgLer1){a^V0I?pcBm-3OEcNQYz$`jabJQ!E8`{5Z|)sW@LN)_2_?{_Wq9VV?`lU&^zyU6KwNY!Fo%;MP$2h~5Z zPmj)1O>UcU#rWe@ipUXuzS-t6%lx#MJ9)_>cq#jU(H1CPUlc9AYjpLx$$n=l0wJbz zlaU+)fu|cS3!fMc;K1k})~gEk>Cxa>#4$+5H0Z-fy+6P% zMerKQ2OL$$v2Ha|?5dBG)4h?Z#b=~N0pyX0nCZwJFB97F*COSi}UDK zMR3HlWiq>}yhpcN9x?yS7txqZ?)~;ne2b$?km7H9gw>YC>+QV#&V`R}0w!PrCSU?4 zU;-v!0w!PrCSU?4U;-v!0{tSu^|6>AMA0rKsI+Um_*NY^@gT8v{99+c^^5P-QOrST z8%p3_?k>?T$1*&Rc%r}II;g5^4-@(jJAAbB#?hnye^t)6=-VW<)-&-$-ii2PbutGnmYP6hCjpX9W2*E4#^gt9{_L5{h*s6m5%EaTl>2U~vsg zaju=XF1JPzjGFntcg+9p&pkkBus9FnbAHP8qh`4nt!=~<=SqxW_FN&azrNpSarbdd zzywUd1WdpLOuz(8zywUd1WdpLOuz(8U=Rs#o$wr_XeVd#2H5_J(1__Gly+Sl-yas` zRzz5L<7COqx*aXw#1ZQ}#BVHB>3dG7!`f{;nl`ScRbDw}^kr7;gU||g{N6dS6^}A) zCyuqyUt0xQ@$FyD$|YRqMK25Tjo-aj%;Hw>m*Z?B$gfpMvDVbv>*F7!+q7*_6mtPq z!9G74>;btZQtZ$5JaJ7o8Vm>V7{vZ1q->Sy6GI}GLaJl=x~bD26m{Nl@IAU|KuPyG zpLVBR7bz+yqwNPK8zAMe$uVE_dfjkkzvYmk#r1@xx?xh)ec<&G#X^`IQ|j0#j30{Zp%C-y_)4{;o&c=SJT`iha-ts}S3vRn7Oh zoz|^TNDBwg$)hxB)?WFvvp>gq4~pN|t71jG4?CgVTg56s$fyybGB2T zy*`KIY%L1(H^eozS3YIkpV*HQ&1)^J%gdUZPTNhu>!62ff&2d(1U}m-$5zkBTdd_* z6^rk1+qYs-#qID|7X3rK03Ig3X}ifU`|Tjg=BaLYRCyojK=rz3e7BfAGq?DU?=XsX zS2DK+fmWsO;N8^7&Y%#}wQ$QKYO9qG zTBJH|qAS=fc6yfWTeV_1sM~4tJm>`UDzZ%>a6UKt(l3wN?DE23aesgJX2x5;UY9aOgXY%wZhWcpP*#1~_b_e35)34{iF zUr~9%?{cV0>$^Qeyek*QugrenusnJYsoSyL<_R$ISy0h#=$1(p_oD#f{pcLULLjE! zqA{6y#sBf5>`qJOwv{NiqQi=D<2#|!%o##kV{HGH=xSseO5h6aroJX+p11aG0w!Pr zCSU?4U;-v!0w!PrCSU?4U;-v!0!0wu8rcFvFPq8Si~iFK7s05R4}8yxV(nfvi}_<# zZx(YA+weUB-KGXDDW2$8aSEED2=JX zHJ|WT!iu%y`!e=oxV+RqM*C()yP$iJZDa!fR?o-ZtSIJ3uaZSy!R1>qsls;RUlTo0fuzSZ zn6Aeev+uQ{jE=5Cofj1&H4t+!+%H-SKQ4+>D?f0H?=y;aK&zOG*bcD$6`>`?6mv@a z4!swRD}sN~K4@3Gjx3H2LoxO)+H4z2;3e*^P&9k{GXWDY0TVC*6EFc2FaZ-V0TVC* z6EFc2FoCiW;5wP={4n=s<^pbU4OY>jU*+6c?C>4^TwhfLml)G6_}(NdC%yQkUD5A< zcNF~{ZBrA7K1e(D!nlZiX~U^gH?1;T;`gBAP~XO2w8|lES`rzds#QQ3#QUW(K$`lSx^n8=&QQ?h=KxTQ^Lqx(_uFpBGif+C7oz7N0zwP>Iotc*&x$sDsYUy$wD}o={R&zdO`G{TH=J*dJr~**U4>$7 zf~|7`KOc^9uaD83r#f#^Dfak4uWUNmA0l5X1(p0pzRV$_zxbC?%+DY1pD}lGM}5n9 ze$4+j2a55h+oSKGM^M%K1Vo?T(VY8#K8~k2jy3vqzPXcgT@;Qz14)*XSU z?O40@EEM0_=RxV$4LQb)D6S90XN#|+ST{uRyCz{3Zuj7EAjWKTaGLt5CSU?4U;-v! z0w!PrCSU?4U;-v!0w!PrCXhmaYh^zz{0iJ)I6O23*kq@Kc;sM&S$HchOy(AI#oM|k z@EW^bgx-TjbsPZ4n}9xo&O|!s(nHTZPe_*ZgSDiK18r2ivHSa7QbiD(GG*6p2P7zqXx0Scw+2a ztTXWo^g|T=DC2WwqhicbJRW1oeve{}CfiT~TNhQc?fBu)D-Auo%)NVwB)yMIJB<7F z9OA!JCY!?tat9BWzOM{1?2H~9K2$$CnZPMUpI7*eacEVw_)K*=nyYB8gW`|aWO-Uiq93c&IrVH-3`0Vhxao9 z6EFc2FaZ-V0TVC*6EFc2FaZ-V0TVC*H3D2OQ~lliz;_fO__Y-vag zc0=P@_q{0kFvoo6UH?wqD&P2wxrP@)o1^cc=y&MfC4kU|#oS&ywqiTl+iA-?kaIV6 z;^I7d4n@D(ucE!sJ5kKtxG=kV)WLoVR>^ZdRAQZsU0SWCqkn-@ogXP1*N{ zeO3DZN16Qym2SKTZ61T-b6R}9Qhcu_tQ7lL(dQ?voDaqKohn;=w%QKOP&D6u`6HIu z(H`g#R7Kmn7xn;Co$Dgz6d&IUo2u;#uTw=&ptvSj=z21ZwSoV=McG}`7;BODVfI#0 zZi5n5eCCOH`JYGe{(mRB9K{?T-$HTyFBIeVFGI2Y&*&Z$=S_^Git#g5|Lg>ULhNk3 zjOv_7MV_zrWdbH(0w!PrCSU?4U;-v!0w!PrCSU?4U;;%F;9A*SDEcNU+G0hrYV-xL zm>W#dHfxNv$ol}hUJ+WKV~YMSx1w`V%!RoQnuwy0%&@=B#p9C|&z%vy3w;vB97lgd zuOM6J1kPudf%z?hwk(XEMYi?{#BY1?JKcTgCiL4D`U~6dN3kY``=SA16l|d)-a8k%sDT><>mWk9E<1-DBVLOX?Tk!z z0NYLxnqxA7CIlX4+uWJwlYN_j37CKhn1BhGfC-p@37CKhn1BhGfC-pDnFw&bY(1nn zFLBJrGGkUQ@Vyv{zMhIUPa?PIW3D=GVvuT~HZM`kNpvGR2StCin1?9(`h>oQPDC*; zQS@z&e$JkY2nYq(UHF(i>pbe#FKvqXsca@tb^U@yeE+cQfLgTwlx|-;dfAV* z#Qf|w6UZQNYA?0f?Y;yO^EfKnF5NOw*^i`FssDeihEL(f#6is=ZHqqUYom*i`~3r< z3yaT9^JU~1zwuqt_Fn=qrsf~$aI_2>)iIZy=9l(Ohn7O0Low!G@tPoEb!3Nh%CY|W z94W4KUD?qQ%k$(f*T?GH!xJE_GohG6K+%pJKKkAs1z4O%u{NWkeX=(?!|x5SqZOe! zCKG5N5Z?zj9{z0bqb6ViCSU?4U;-v!0w!PrCSU?4U;-v!0wz!`0$elOw&6OO%H5b_ zeq2^I@f~xd$8Wr97W4GZl*sKZ_G0^tOR~d z!^ie=F0|S={GzMN3TA|M@7yZx%oJbI0(vz+#8E#&^ii83i(6Uz!r;hJCqFm`jPa|-q6PbvFOEF} zivHxWCc!hvRt$klvT{oECZdOmX{M}~_zd(ciZv|0gf>KPMRO(QWx>hEcrX7CL*GM@ zC(rQ@geDd_>Uo?jKITP=pLt`P$lJ5(RnafJ;=N4OR>;g5(3U`oTu@+$K4bu_U*faxTYH}_9{FEgF{f&Eylo| zj#fjdpK0+w7R`hdG9SbepFGtXZl`*vTWE9b$ZKlNki7W6xG4mt$ISnb`> zwrI=AT95y$qWE9i7IXOThW0>vp}016DY_Bej$)mfeQHFmDZkwK7f`& zJ@s2-|C!NB=!574^c&>9YCuS~Z8Fa@+P+G1`|aKYG6}?3jMa-cNAUC6%!c;S_uq@) zQzakdO7*(u--&H}W-jrsI(Fj6$=u!#JCC6Rf=!mdUYX~ReVc#@n1BhGfC-p@37CKh zn1BhGfC-p@379}x32;R)=7&*SSNCP&S~e>$_>OTCPa`#pT6lLNw-`6^Yc*(&#{?ko zBw9M_+$qX0ZHzTv&P6s8C@X=ObMVbYofD0I@O@xe!Hm%ED;i@R&HJ?NLnE}$51T+c z1mbh*_M=(5jmP5nvCfa89nlzT74MVSm}A@4oIt$)-iNM7-$nbPJ<&(ec4!;4#pK(u zE#@Wv96AnNhGI_gnD5;E{edvbHh<>%Of2J2%wcacfie>K3XSe*Y*X$0!q53-1kgsC zPpQS||05HC=NJ_IZ;2Z6=^ZAm$9*JnOtE+AnSO+_p;9SG`!G@8OuD&x@Krd|xDr^w3(2{rC^l_}mop zw%bgg7Xsh!NB!20NyDc@f9NHE0p54xTAWq>Pkt6eo;!7b3)M~Qm_W=~FiZA%%`tZ9 zxMkkK1o}_lPMT2I`hEENOaDVkX~BNkuOUkQ$w2h~erEcOcQ3$Jb&!l1WdpLOuz(8zywUd1WdpLOuz(8zywUd1jt}oS z|1w@m3;vRs7h1e|N~`6V+oR0OVJOZt4N93$38d~m9xdxHrKE`fOOTc@b^JbcZKRL8om zQ52^t_@NF~Lvfu*(LR=$Lx9CN%x4tA5z{is?5-2#=CG#qW!Xws_I6Bc1vK>bl7DYAaS(eoQfk>RO%FT+z>` z4NK;S+Lt3lf5n-K=2cdIu(+x(S@1zkAcw%Ht(!vg_st2xUQM8I0`YnDNHk4R?^XC( z4k`Bej}>Np{-?o_?Ap=W%ErEPcCaB-EI?TLf|u_bq;duN$9>NZSW=& z7&HREra|M2JU8&S>7WH!PO4gT`g%5C1z3TF?cx=TFJp z7N?yaXJ1gV>}FsH97IbB8>g3f4%)W~n1BhGfC-p@37CKhn1BhGfC-p@37CKh^os!3 z&?cbA2Ii_>u?@+dlj!zhn>#Sxw$H3bqP%QiB6UMdpf>{7vir=L=S!w<+CDFOxi)arpTbCrKoi_6Zc1Z23(QZy?$L^gr-p{W*fwLem5bNyFj-y1gaYno6 za21|Mj$^$twf9cZ(sfuP5QyV<#?1=Jr0tf^$rjCU6GP55zTq%yZYiO~3?9zywUd1WdpL zOuz(8zywUd1WdpLOrTc+Tt{0Gy()M$G1jM^y;nZ1_Q&^J=qW*N5z> z2QF|CE=orbQ7(uY3m7#jD#57OTTCpmMMYy`Y-ltZL{S5lC<*o+u^TlSV~GuWi^1?$ zV?+f-QLqLqh@isr|NfB0<+6A0IlD7w=DhoPpXYmb@9sJ8%}x zICj;vyui8n^)+VsILq=bn|xvU)$&^cg9-d1OFp>k$3*(9zpdXoSXKQl_@!NYwcKAe z#Wu-T`rRah^?qER+b&hr?LnF@R$OPkAU?e_T`sc!-V z2oNAZfB*pk1PBlyK!5-N0!s;GU)mjJy7z8&3#;o>Z?{y9y8=o`QDk z!f2^J()V7;nXRFzXUz$GDHUI3sWCopKT92VPJXlIw&2<-_~?{dX{FWUub0mO({If; zcxNk68`W{-n4X`qZhybg`uQ(`r3Ai}AMP`TXLtxL9P9qXwP zn0J96Z)%M9`SYONGO1%wH3DN2sC{@3?qlqy-z}5V##C&F>(Yx^AKPIJea3x5a!!Z- zW9sUueHy3G`g^>^ea3Z7oO|?a_xdpnl#bTLwS+o?oU?9D$olTm=i^1+1PBlyK!5-N z0t5&UAV7cs0RjYC63G6vqvmWM;H0bj&OWzOR^rblU6DDvUF~D-WnN`%kMw<`q{ZVV z=4>-t-Bk#jn)_a}&*zs}_MKYRC0|RlHU<7QWz{x*PebLsv9HHSjJ8X@-H_EnDfPMe zsOI)&eRfKIxUho05?Dguto-bTeLdb+(r?yhzogC^+*+&Wh@Dqr((-*`^3>Kgw0hR0 zz^8_IoJP9laK3U)iYt1o0`*$g+uY^!U4KWkc-@%Ve|PgCo=4_&nen<|(rP=T&+QVl zD<;2c5UfQp zt}navy*_E}niDk+Zk)U^(bk4Qy=UCFug8gg2@oJafB*pk1PBlyK!5-N0t5)OD3E<> zmrees(Ormt*4H+RSDnl59gVs@r7ySYH)h8+=2o9a+&^jYbKb@_)vC++a(Zayy#I?I z=c#Llq{VZduX>};aR;Yl-S@iadG`wWzUI=l((bf){C4?$fNtFi(nfXsDF5$mzSBy# zLDs3R0dQ)f5m+ox=ls0yO2<{7_se=ceDR7l^!H%tK+8XI7E%WkOD8u!h5^UWj)5y|3il?u*YSen!ZQ(cf^p-Ynxkkyq?$N_87A3 zI$pHb>qVviM-N$GkIOzjefRb_O5X=3tsS#k$4u;9i=V~TKJP!eWVPM>FMV&5)PBj?wWMaTD)s46VjN&8=;%uEsXgs>}IOdg%39-Isi{%>I9=?{AWN4$$Ubpgxzs#=

zFYD?t-C94{*sab(YjGT_X=#B$uU(c^s(k=QC;rY@di(fk{{k<`B6^+sU$1>xMjd0& zVtZq2ukC%;lR@yGEe?d#bixem-rA^L-`6O{jMdF5d1n zuX6wCG$y}w`rbM@eenVt`2G_mkUDOfl%Lj~z^78uo|BLC`MhOntmB$aX>aqbxr*vM z?*}d(h57%38&ddTYT0ISeO-Q^zF*mp)#6fK(4|wCePt!AzWuQ_)(U$+@BhN% zD*&GuE5a(*>+ct8^mQ_Uc-KDZ7Taqk6Ij`;0#?V1eY!=s)YR*>c+&q18g%>6!bnPO zwZHNU4c4fn-=1H~dH)v%yRQTY5FkK+009C72oNAZfB*pk1f~?oPR55cx}))1Q&LQI z^O)VfQMaF@u0F3vT*pncc0J;kEDWC1_TEOv#ab<^&QV$0wAbsKFN~itd`+$QTB{qo zk-BSs;%zn_OZnL=8mX_!_}`7|>+=8U|BDS-EiUD;T{?B$S5|cYb?0byMA1^)L(r5v9*n^o;4#-bLDzFZgQH(dDdf(UBc?VeXv2dBNv;cz5|nU8mx0Im-LMkf3?Ni+4lqp5FkK+009C72oNAZfB*pk1PDwg zkbP{=YIG;#o2HYP?$$ATRHJTRm`-B4TgR-9o2WU}Eww*iP)JdAJq_0YXlLubJf&>A zuzlCU*VJ3*=6ruUn{Z84Qgi?BwA47w&p*_V#7mc|v5TL@XnpeihO8Esa+5BM`s^bs z+vPNA&r+a{YkNY{+qo?^eiUU{S4~=+^IewL*|=W+-?Tuy?vb>%-!mm$ zSW~aZf@B%{)`A*r^6m^^^PDc5!<{qdRAh>DlcOjk(olq1Rt9 z%lCDy>gP9B<611IKDR$S*=k|iuBET3``U?fY6W4ATFptk{ZivJKd++1#o= zW~n;6_*oWwz2w}@t-u`qzRs(Dg)WTx>?7+~#~;0G4t=fi>ICXB_uyo-=lx$8_gTk3 zTV1K6z4vz)YV>u}0@-@rNDK!5-N0t5&UAV7cs0RjXF5FkKcI)UtG+cNoP z(qg;obQ;s$I$$TZh?bfjJ)OjKw@&}BZp^JdTfW3Olhp{pPO$@uJu`|dXwkj%kQq3)Uisfv|IG`hzro|TS3~Wj+68M%k|~fWxrX^n!E6= zjn?0P+ZCu|m+su<80xdHtZS>UjqmHzt(CsB{pxvtM`lm4I9sNIKq zcDqS~Zof!P+b!jmpYNaiq`{ikN=a`{p*t+KZT*Y@0RjXF5FkK+009C72oNAZfB=E% z1hTJfzoh0bx7e;SoyO$14%UuItJkc*;p7t2-!g8WZ)~vD^)oJYUb+0N)$4sXw)rkq zPR)_M-t;58y?ex=&Z+guO9}bG*aUu%6*_Ra5t!fKVr(nDiuI{;4(_^Ky{q|M`hLnP z)@{spea~uKW_{m4-Yog~m_e;uCjzIZmmAG`e641AS>N||Qu+8gdFpCh*3x~m>Hadg zV0@d<8m&U0&O?8fY?!MIaT&q8e$JLXX z6Qzw)pw4f3z~;thZu58JT>TZTRn^?vdF<2oYge>Z!dnL_IppJ8SX0q*&V?E3N^6W~U>5?8br0cvbJ6mRtq^@t(Vmr3)?Gd!^ zvthe`s@#B5&PfXvpYt-$e)V1Wbb-Nh<>HAtqYsXF8Z&Nm@bL z>qMlqW?EfSy5>iZdTz|7##!G#kIUVY)*iPhY3|#JsbHexwQ{>x%zd@LE8ySz8uMAi z*2x!tx2UGR+vICiU!JS&H~L#7_1t_xQ(HnYbq$YyNp@K67@76`v(7c2tJU*V?>%eJ zd0t+|K^-62>R3o=Yo;BaO7~b8i~0Wb4Oaf8h5E+)HSPx_-)_)fN&O4dvB9@W2Axls z<=6QiTD*2uy$`?3n4dq!wr=sN_Xw@F2leS4v^r+&1*P%UmW#N42oA z8>*qMRWb23ZgShNG*tTw`g%;pXnpd_hO8D!sdMQ6QD1I-_M7#t&-0&`I0hgH4Yc!8 z!()@J`y4lY^_}%TD5>{;t+krRzH?u0t@azU2PAdQX>I-l>bTZtCF>W)XTGnwyC){C zwYj!gHmsn-_@Jp-GFa9&PS=MnI zEw7dT_IfSnAU(AesEg`2JzqCF2RFBQTvDHvY2y*7&lF#snC~{h(_oy|C2vZ!VFhaL z?6Z@J=g#J~we8MIT50#4V92iMCIPx;D@YsFQP)|Y+j%=#_nOa8#{jfA?>c3znf8rT zdh-=MI7f1)tp9x)-CCbcUF$}|JXfm`w__U}6D^l@MvA*@a*1<==l^xQ+4qyy+7ssK zK^6Qu_w(x#ulcQw#BS?3E#2KCxy0jm$g!SvxmVKSKA#_?u8IHl9C90sW!dq1X3}D- zeF|6U(>eV=phdXURL2+g_Fg-E-yo@dMJ=^DkIr=_9dEhqhUAn)8;?NkhdMm*8ZzlX zb~^$D2oNAZfB*pk1PBlyK!5-N0#gcP|J!AgmgWSeoC{lh=62U9IVQe&`mT9sCttXv z_Wrrw#1JdEVYq6(bxZBDlQ5*zpCqH1FEGhC8u~Wzx_okcq75TZ z=X1MzGS#@v?QfLSd2U;1b=*#G^M}*-Ls~#uqK4Cke*ReP@>5gI%RDA&abDFDo2;oH z)i!%`GK%x_u4XKzhUYi6Wqw!8TUO(;mhT(FZIXBVj?#A{7O3Z zcnzSNE>-WCf0pe~=c%aoWOcq|jlg1o`di?I$>`owWSySActsof{@+r|RDXYt`T1oQ z>lUw8&$IK@zWrj?>VDH#^=TKkyCtptom}VJnd+E{+Efle@y!b-9cL+bee!`e%AVUQ>T&z(WOVZdCLKpZ-!67_48VsIZMFjc zmBn3aCEGvutIvX4X+KLLyu~J#eIG&kO8rC^2rtlMng6IIJG|SncVv($td4bWWDP6q88gf&Fgc>Q0tvRP}fXov3;m-)1b9_>_o5cwd3}* z1_#`KuVlQ%;eh0n20iAbq}uQPvSj;}Y(MuQK!5-N0t5&UAV7cs0RjXF5FkKcI)Utq zyXU;@Je+bpe>|PUbhpmB9F(+p+(eyU@3PZrOn&S1y)LQi_qNm?G`YZq$K33Y;y>CF zz#P@`<*e%=$(GY>Gq=68Kn!siU=FTEuu`$_J5^bFQ>R*N`wvZ_~h zs}%9^Srszlz_)Xo`y|^8H4Zno+^kz&r|Q3x7F(~^2;H3Ry!}1{U_;XC^VO63+#YeO{iWYZ zT5PxK+3xKPqOJJYqgZT6zTTk8*h~6qR`7O-{qB7};Pp*_009C72oNAZfB*pk1PBly zKwyzT_QyS8>^lxuxPHekDzo!%vrcs_`WD;CDXO<~sip55B{c`5l~(gZt~2cb%YEuI ztybrlj&a-mUsmd=$>pZqR&Kv(fp~78{70hoCs1>??zri#FxhX?$J_c_#hLf~=EU00pQ^&rw^gcG_T-eUdzbm@F z%($N=X1{iE+$}l&LRJ5aL7@J&z4xReA-8RD{{P1&t$*yd%l4^#37<{0wIgtPN_|Q) z`oH6|j#o_TI%Tc3df#)yu|GeIY5n4L$E3ygi#4|I-=}r@ugB4EYC(;)Iv!;IzU z9i!ah@kftZjaf{tl+<-&wEhLo&LWOUc3jOib6)}k2oNAZfB*pk1PBlyK!5-N0tBWP z$bPvxFK3JGIn!%Qevg2?phdLQ^oq$9roUzSe_&&7KTSPbPcL!tbpSrtn8(=4`Bhfv zb;&*}*+%ZOd4bq&oBVUq>gU@jZID)D_oc~ho8Jn{-y(g~T(%RFR@xEESG@Ay2I#6u zi}QAB+wZe-nepB;b=6$kI@h7+pdgH()jaa&B)g7x)U15XQdiCAYO$TUauxU7SRe!B z@=3k_Z0$Vq^ECEr4${+-spcT%_ASo;fAPj+be{j8ZB&1I)VUaboM`O|)b@R1a)o&r zuUXe?G`czJTnc;5%XS#UddBPFjgEyXYo9)i)Bmj-wfj!$TGy9d`raj}_dcz)RUdm4 zke!mZCf=9#GdgNN%*T@7?`wSPmjD3*1PBlyK!5-N0t5&UAV7csf#n6Vf9{L@?EV|P z*S}i6^40w|)qQgC3j0}sC$COs`Fm%TpViN557z64v*Z<@+cx*zE_v->Yv1;Q-%y+EC}t+Q*xr>~upI^SF?t+(^o2dK^`-(ve=idyxtBwajT z^wCFZtLLDHB|l2EVFc=P|9?%Uz9v#1W1_D|g=)Ui7%+YAYFgxnIryZ|%Iphpxn8?Dxrb+CBNlq}Ag#Q`(wrr>5$sCcBJ%3~s!B zS?7l)Ew+<3uGRmS7sw!}V*p!hzerK*m#=22lUqbfO@~Y`GWk8y|M88v9XomTOW!gcTPKfC>YTaS>I7=O z&gYWbC0i{m?x%eT#JtYc_MJpqnLwRev5uqIuFGwd{#yGi_{v@SEbc2-4^LWbpIBUH ztKVnC?VHrGU#-pE9P^kg>rW@QOqze)O^t6#T5Qj4^?6~)x-w8|uF_O7Ms^|Pe~G63qezs?iVVypN3y`96pMAQGPT7*kY&tH1Pbo0~n|CbHA zeJwR@+l|nLW2+vUcSufe&}!Bt)xOKeCEItkee^?s009C72oNAZfB*pk1PBlyK!Cu? z1hSv*x=DQ|+*+%1!1Z>0>hyiJq@{TQ3){cWt#kF2ji$}tD}C2|iZ3rLtdXzx-hAbg z-69^hNxs#H%MfLJG7G(Hvf?>ElWi%tT23JTS5E32(Awkz=j5i(PkNcxo_-%X`KpZm zmOscE?!4R>T+Q#&_lrg^u(B0=(`sDi>AvCFGP!qB^98hi1wNAQ@0e^cPh(^?>zZ1> z&@bs=y+3j_F7CU$K(=ZfbN`0qg2A?My9=&)|8GqWT7D}`_S^Kcb#g}1N~`nSc6wat zWRkPLRqDKU^77>8i8dbsH9r3>srM4S&MBV7h)fNw&iOuk7JjWPBjaGF-6MS;I6zIaSmbG0NT2g6H>CfwXHm$E zmQlw`TyL^bGuf@uU(FACYf|UH(>5<~Zf^2{-uYeYTkG~pS0Kx>)LDV{beI=pB$0Yx#_g-1Zu9{ z`;$YGHTAlidUs4(nirgME^ap%`(t|E*Duob-l)zw`t^&|eX|;YI^O@dWdEeIV}H|E zZKuy9t+Z29NM}A{e;lUPI=65AJ^k$DdxeeHQ%Mz8+n1s&oFHnY7mGeA9R9%WdraruIFO z`mDM>v({EFP;+hT`TWLX-{yU-Uuv)Su`RxyotwI@)R&unFDVe~9g@S7R_C{+v|igO z>HWxL!zE?A`(OHgWv>;?w(~c3Co^9?g}IK|IyCus;@TTQ7`WBk_?IWQPMSM@Fg0yR z>TkRjTQAqBo3Ce-$=5am;7?l|1Q*qG$m9~!-!jX%N%E@({i>$RPcJh0J<@-R$0*-( za)FiIGH!b&FHBk-cPVSt?WFYZH_5Im6Lv2G1PBlyK!5-N0t5&UAV7cs0Rja27Rdg) zH?Mk6;5a)ts!zL=T>FMwY{&KN_K3#NPUzXI<}CbU@{`8gR$5NYx%+Z*Sh91{+xgP_ z+^%b_?`)?!r}~YP4<>cK$F1*^ta^{AO9-Op zO<#8D`x;4YpO#wv9ePk-W{dmHa<)jWo78*F+IOh#ke}A;?jI)W z=5agsvCgqLI%%=>dfl`>wna;QWNWqf$)l6~X1UF?ym~*-O1r}> z?Z&>WY}Db;XZCvSj`ZKs@pp@d!CU$q`&m#u-?p|NqmDh=zE8KM`pzZ<_4@f#`roE8 zvxPCxx7*f@xy@`&T-@rolMTtSNu5Wp^;YL+eqB=M3Ut1qalVACXsxn6w@MC7-jcL> zZHxNs^&83alIthEKDREnA+qdxO}tT3=JT36WZi4N_C1r!Em!YqewV&$Y%aVWw)E9G zJC_(37j5s~%hzWmCna@`!L{5@OYv_^W^#V*B{s?Ee-xY6-p4LH&v~CUg3*`pRj=1~ zPd=Q~`R>Ry-3`}4(8>%S(q=V}I2#z8%|EM+=z(((up zAV7cs0RjXF5FkK+009C72oNAZfWR09vQK`uKPNFJAbDVg{4Yt{Onnk}z}Km%zLWpS&q~Y*NPKmbXg0hL2&OSqB0H2oNAZfB*pk1PBlyK!5-N0t5&USd#+T zProov;VJq4Ka+Ekd1*D5>8r`xlfOtdB!iseb4?DOkriFrKDmBU=i&ZBa$YhIt>y-O zG5@FHg_`u1PBlyK!5-N0t5&UAV7cs0RjXF5FjvX zf$YcMFS&2>(&W^nKC`cRHbb^re$AV#b9>fjBz2zQ`y{`YY&&Zs%NxHy#zoD&t@FSB zW%Bx@&M{fXiwrrIYuWX9sySNBUWBsF*7Ey>>`_fBfA&z6aEdk!@q z=6Ja^ZjzdRQlG6oJ$Zlf`Q%4Q9b3|6tIzCe9?yx%hm)5lbsWN-lO2;S=a|x}6Clum zK%NKcIjLULuAba0d35r+(iTfJ}kUQ+LyK9am9c|=mrjn__U{X7?q zc@SB*6$@N6zU%!!JqB-<)ZZ#K{$G`RAgRB-zn0YNbsa}M>sEiC{~|d(srM)Ke&OB8 zi;_BqsQymBX;Q}(RUICyD;^E*NPqx=VFdEreWj$uw#P7{(=Rpn^?rAkI>%1E@2_L`4oz;8TrTl?KK*8N4*~=T5FkK+009C72oNAZfB*pk z1PBlyFc$*(9AM{U@8oJpeKt^Yn-5Q(k^Dn)%%aq}*Ni>{OWN%Dwf;eG$J^8Lc|&2nGs?}K{Y zdP-97wQ6qv4U@(1n_OFD^drIw4k3`oNzKEn*Xs?*!ux`HEdE7OukH2sNIkylHGlEr zu>O{*YxsQdqP%RQ-`8#HF_SN-=Sx^-n&l?J0%PI1#X@1ADS$@53l`_i(mJb`u>@@&C$u@k_RMzl+^3{ zhGf^oJpbOeq6H8jK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV8oSfy@CvB5AE1pMuP7?`DYTg#ZBp1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t7}Nka_a&ZEf;@>N#!%v{r!t0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72y`Zpx$>tpIRF1)odM~H z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBZ) zka_=mC1*7@|Ns2dbGw0QEuH`Y0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXFbS98_@&_d~U%sVwPKvooXGl6CK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjUHWS;!}TAKKOQ7u0hnAPG5 z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfIz1L znJ@o>2Iv2Ouu~+R5g*&i_ApKvs(;K!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0Ro*0WZwTa$@dzZ|No#)p>#%o009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1O^nyy#Jk&^BbK1 zfAs-bEt&uU0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXFbSjW}|95C`?*C6y(;l5d>5KpY0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF3@DI!@`p7z|Nrx;Y2ARV7EOQv0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7Iu*z~`PVl%|Nq}RMba4o z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5Exh> zbN|2C;Qaq12WGW+0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FpT*K<54LkeuGw{QnD5&pkUs(h&gy1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U7+4_liw zvAO?M&d-u9J44bD0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oM-pAoJvpstNOrwJ#0KYViaJ5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZpi_a&`+rSibN{QHV>?CC836(W2oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlqP$2W<*CpSm3G(i{Z}dHCpYgLONRsq5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZU?73almEk-FyB}^FZFCQ5UIryAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D;a0GEe^Tjm`b9a=x8x z**TUD2@oJafB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!CtN0+}cOqM9(@SbM`joEA%f009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1UeVUoc}G7&o?&rzsfnfb1WSaAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D*x7GEaWXk}*xdgr=fuvTbVz^z z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C71{BC# z`S&$A|NpH6l3FwY0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5a?7O^ZwT*Uubat|6@Ca(is5)1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&U7*HVd{;!<;tik#Je>xzmMH3)EfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5;&P6aaW|9}SP{-2+k zcIgyKX9NfkAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkKcK!MDY|EmV)|NmcV+Im1%izYyT009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pkoeE^0{Bs(d|Np+!v}LDIIwL@U009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk0}5oG{D&Ky|Np`PNiCWH z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72y`xx zx&LQ2IRF2lokQu6009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBZyka_=mC+9Ub|Nny2bK`+XEtUWQ0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXFbS{v2@^?*YzI;pVrzvKi&arezfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5yU$UOPS zv^4SmqFPSQ*DVGjwO9fK2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBly(78b7$^T1(^Z!5EIg}0w5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfWSZknfJeC@~H;r{~t9Fsl^f?K!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7csfzAao?|;|i2Mx~u zKeTf!9TFfwfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5;&fdn$|fA0q8{#QxY7>Lwj2@oJafB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!8B!0+}a&&j#oJ|1veL?;J~q1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV6Rsfy|SCT7&cdKbM-;4Mb|O z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV8pV zfy|SCM}zbKU(-314haw-K!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7e?fC8EOe_Dg{|DQY{sYMeYK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7csfldW7?|-M{>;~um-?mdIoe>~FfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5;&0R=Me|B&Q2jm`i6 zW$HO#Kvs(;K!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0Ro*0WS;y38k_rH<(!dh(eSo^|&q!vwp009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1UeVUy#Mz%Hut~Ed0FRB zIwU}V009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z0|{iF{PxLdHDSK7cGy6q7E6Et0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C7Iv2>C|J{@G8=L!IU5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfI#O0nJ0ftgY*AClA5;c97~4;2oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyKwuz&%#%N%!TJBM z8;H|l2@oJafB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!8B!0-5)}Rr1{i=l?&lb1WSaAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+0D*x7GVlM|$vKV9|35$V++iS6izPsS009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pkoeN~1{2`6a{jYLkZETKYAceizPsS009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pkoeN~%|31l28=L=sLF&0@=U6%< zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjUF zWS;zijm`b9a(5u>c0t5&UAV7csfo25$KQz1G Axc~qF literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/parameter_static/gArrowDown.ia16.png b/OTRExporter/assets/textures/parameter_static/gArrowDown.ia16.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb51d79ceb47ccc3fde8107e3206157c95bfb38 GIT binary patch literal 17227 zcmeI4TWB0r7=R~&MWYpIOBEk<*rM?^JGY&g-C?t(W*ZZ9YfPg_P_T13lZ@S+8D}QR zCLn0r2U|t4MHChFLBtDKwLS>q4XYwzTkuk%0oPYi^53_w}!;Qp9fa$rA%Z5-j+Vj5tOwX( z5V%vm0oG5$p{(gN`XC$zt=YkF1=Tw`3TP$y*kH6lBxjP1#aJsR6N4Nt-WlU3QJBXxl;8afUKo1G;Zr zmZ|HYVCsOe^1QoS|D<_yTCkr_gqJx_Sp$6;#uGZPqDu!#hks>#Od*-($9aX!L z>O9R4K(vv2Gs#Xyxe=wl$fl7HRLzKzWXXuO2QZ85yw=ubAL455XAmd=8MOe znFP+swpZ+`p|!Y^z_r2|%?Ily;0nRn`LWmeX6^2aY;;qp+W*BwxXv{2PiHFNglq7I z4E&&E`EHFyyO<7LTJyZBZD`RPv(t{*yuG2Vk<53>$NxCiK$~9TlH*N_%L9=SWQk{V zF()y305_pzig`nj6!44%mevL#u9VpxSjGQNK_bM5z0GIPyDzvTRKij)#%9h8ES zDLgaz#RR^6IvFN)l}3VzZsgRnXUt2^sd!acyq(C-jO(Jf9++R5C6(*KN-jDBt7*{`El$w|#!_G*-K?e! zD=K<csbkI~`MGmXOMwL|Ji|m57 zl($CTSX5b8ry7;BB5?4-^3mU>X}Y`Jbo}I|>F##ZmgQ}Z9hQVlwKun04B2&19V^@LOcg|D172?0w0Cqg9AC^Z)zLJJw@vCR`?_c zK7?Y?Q>fIOp|i-pFSJJV@B2m`Nf3krMWSAsvDXp}-LcE*wfohu}gea0G%2hZ52uxDW~)f#AZS zgmeflgaSt(xNs;T9fAv?z!3;897;%s;6f;H1cD2P64D{K5DFZD;KHGVbOW8{}2g~b5>fb!S>!ypgau=4L-Syp; zJ?|erwDsZ@`Uk7`J?dQc+2^|-+p+3!{u%8DUU=9z@<F0i)2mif%Kkl< ze?4+!`FFqn`Nfs@DcgTLa`dHb*WEdA?cJ*{|K!$vgWYr23O#$jJb08I?ed|R!1PKA?eU-|UecfW$; Pp$6A)D8G8crY(O1i2dBp literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/parameter_static/gArrowUp.ia16.png b/OTRExporter/assets/textures/parameter_static/gArrowUp.ia16.png new file mode 100644 index 0000000000000000000000000000000000000000..1ed3f1cdd9596322cc34c09509428082949304a8 GIT binary patch literal 17217 zcmeI4PmI)57{H4}ql+X4Vnkv>GlT?$Zu{D4XQs{$vOB;oGRwNGVV4A?e_nSRX4;|c z?#!-62#ASBFC_kn!FUj(2Sbb=OeCV4pk9mzVq(yEkwnFV9EcLb0bi%nH{ID;cnC^J z-Xyd0?d$ia@Atj;^=l7(acFSs(yon^ux4?HYG7W4~o%G6$ zQ!@=7mSF`tfsgD!N4x5T8@T?2yFf^Mc)`FZC#4dO-SK=Ym5!nPVDl70&_vqN(jS?w zLVg(f?xbhH%~NP9t2@o!4~AhYI|QzfdPhfrZ7tv@JhC7JShp823y(x*jWrdeI**{K zeIC>T*Bf!&av@r-Lvb$HzP`rtWY_Mp9n-D(>+*cNNo(P>yuqRtz=9~CwFpE`P)0yj z6=YS+trvhQ2ni>VW}A_g%NnG?Z?D5`0oieSAUVjl_3b{}XXnp&U{c}eOf z{FsJnxn4yJ3U);shkVr;&-1OpgeG2|YM)znJ#<>IAm#Z^!wIX#l2r#BKhPWl4ipP$ zpxd@h)lxvtl!3t+Izp(Y(}2?rN#-O8Xu2hunjqyG5KE<``8?M}*O&~q9daF2DAP5N z`_^fh0+0k?iJX-M84dtwa(Xr=ah4^^8BG_htP*7l1xh%Z7ifoPqXMdx6FT8sOcc!2 zv!ZUH5I|A@r|D^n({-qE8DOPFQxOy~jYD*BGcQ!X?W5H?omj!XjBP!tDXgJU?f&bz0c6X=cw)CmDjsf5Lp> z7_|xLTx)wpt{PekJMmpBsA(SDG>%pX$i(7TaTpoDAa zf($*sV0mtZ#k-gdTvn@A%eJ9~Ip(Jvvw3;LTO-bQ!pHwOS6`dD$t4F&OUOcr6BQX1 z4=F8kS%@~FY)V-}R5D6NPRCQcp(PhrV~|m0pn_~%jc?zQYwcKLLUYEUS>WTD#RGRz z=AdMnCaRE}EGiNQL0aQbxrCf$rZb`;T8as@cz*vXbM4nrnz?47zv=pNostq(9bAIq z6yBctVnWZpkqVPIOC!d_HfrjfGv}MGsbpPQw4LbAjQXOeADG*j#hvT?PATpve{#^(<<2qf<>d>lDkq*@ilb(!otDTAYUj?nK*`4{tP8)RCj^u+b$IK%SpB zmhjd%HX3%;<;h0poDl`|;X(MfX_`FTZaQ{t)8yfH)0X8^jvW-lX0*29gjIMq9Nntz zL{ETVL6K!i&hzcV?G{6J9hOInHoB+s!+QguG;P`sbTk}W$S`rBX&7yr#Iu@4Vxm#> zBnUl(;_*|cM9%QN$gLMzGrIM@u}2yNqaYC|E)q&uhvLF0NCb+Dgc8=FxG)M5f#M>e zgmow`jDkd3f<&OW zNGM?)iVLG45hyMaN?3>D!YD`tii?C2)}gpC3KD_hBB6wJC@zeGM4-4xC}ACn3!@+r zC@vC8Scl@mC`bf~i-Z!^p|~`p3(p@elPE{uXiptwjVVI7JKqaYC|E)q&uhvLF0 zNCb+Dgc8=FxG)M5f#M>egmow`jDkeCCa$j5dmW&IUgl6kZ*7=)J3WZr_`n+d!zG59 zS;H`gjxfyMSJCeUhM5!?=J$OJqrSl~54p#l{&EY_{dAzXX{7$c@nf@HXW09TUw?S; z$=M_IwMSF+%jd7~&u#tdqixD7Q|FGK39P;EO@8z8x$kygIlJe3SUA7o$nN#bZ$}q1 zmEv;1u+JY_aXxTYK5Ly=(tBx-{lXugt-8GKU4Hq1wygZ4a`v&=(-%jVZGY+1!*QT w_2JhJ{(QE7HWIsT@LQeSvfxsRl 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 D?WQyR literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsENGTex.ia8.png b/OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsENGTex.ia8.png new file mode 100644 index 0000000000000000000000000000000000000000..e7a7aaa88ad66b0f28c94559bfb4030518c8bf17 GIT binary patch literal 4004 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 R^pmb8q_UP$`4j7~{{a|frUw83 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsFRATex.ia8.png b/OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsFRATex.ia8.png new file mode 100644 index 0000000000000000000000000000000000000000..8cbf6339cb3380fa37e0f7f6c2374b4bd232e7ad GIT binary patch literal 3942 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 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsGERTex.ia8.png b/OTRExporter/assets/textures/title_static/gFileSelBossRushSettingsGERTex.ia8.png new file mode 100644 index 0000000000000000000000000000000000000000..455390170f0ab2abc2bbb0937250548aa66ddb23 GIT binary patch literal 3884 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 D4SR)& literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/title_static/gFileSelMQButtonTex.ia16.png b/OTRExporter/assets/textures/title_static/gFileSelMQButtonTex.ia16.png new file mode 100644 index 0000000000000000000000000000000000000000..076b9365f2c790d9e9bac24124591ca71894fe29 GIT binary patch literal 883 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 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/title_static/gFileSelPleaseChooseAQuestFRATex.ia8.png b/OTRExporter/assets/textures/title_static/gFileSelPleaseChooseAQuestFRATex.ia8.png new file mode 100644 index 0000000000000000000000000000000000000000..8bc17292b26f703123ee26b2ade96d2acfacaa13 GIT binary patch literal 3852 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 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/title_static/gFileSelPleaseChooseAQuestGERTex.ia8.png b/OTRExporter/assets/textures/title_static/gFileSelPleaseChooseAQuestGERTex.ia8.png new file mode 100644 index 0000000000000000000000000000000000000000..25f96cc2bd9398e4e5dcbe64e8bf8a7fd46018dd GIT binary patch literal 3666 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 literal 0 HcmV?d00001 diff --git a/OTRExporter/assets/textures/title_static/gFileSelRANDButtonTex.ia16.png b/OTRExporter/assets/textures/title_static/gFileSelRANDButtonTex.ia16.png new file mode 100644 index 0000000000000000000000000000000000000000..f43114e81968d6937e6c17c9589a00b3b26c2780 GIT binary patch literal 1045 zcmV+w1nT>VP)lW`cszSvotJ>Tb0--V}EYdwee>Hq%U|MM;ZpslU#kFKt+KCjoy zSG3d9Q(9VDzE4k2|0&kg)O;Hl82C#RMIIg=z9it;*;!IjQhpNz0paNA=t-l|;PH4E z85u#TR1y#nAU`jCrBW&TqA23=czAet@Ogja`(^B9?(y-Fpr9avgM$eU4n~qB1VO;* zbh5U#hX3W|C2qHynVA_B3dK)%X|Y(a+wCY63jF>35k(P=Mng(U$}eZsX0wr;oGkll zwHkxLfXn5=AK?D}{>=sl2M07ZHvTYIP*A|==qQJWhra~u)YKGKtCjirdCX?B9Cx`~ z0RIAh&n_-508pt^6c!c&;PrYj8jYAtCRSHhiI0zGWo3n(ogF+L4^dH3=xBL;Z*MOum5Rp3Mpjo>IX*td>2#t}sTdp_#B4Ux+1dFfV88zdlu9KL5fP-O zrVS`)0D}6Mbl$1nMQp49=rL?q^p`jrpNqU7vSYKa%T3lS@`udvi@bEABYaSmTmp3b< zr>B1_DJl6Iz+^Jp{kNy85}Sb4q9e0K@ViY5)KL literal 0 HcmV?d00001 diff --git a/OTRExporter/extract_assets.py b/OTRExporter/extract_assets.py new file mode 100755 index 000000000..b3551bff5 --- /dev/null +++ b/OTRExporter/extract_assets.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +import os, sys, shutil +import shutil +from rom_info import Z64Rom +import rom_chooser +import struct +import subprocess +import argparse + +def BuildOTR(xmlRoot, xmlVersion, rom, isMM, zapd_exe=None, genHeaders=None, customAssetsPath=None, customOtrFile=None, portVer=None): + if not zapd_exe: + zapd_exe = "x64\\Release\\ZAPD.exe" if sys.platform == "win32" else "../ZAPDTR/ZAPD.out" + xmlPath = os.path.join(xmlRoot, xmlVersion) + exec_cmd = [zapd_exe, "ed", "-i", xmlPath, "-b", rom, "-fl", "assets/extractor/filelists", + "-o", "placeholder", "-osf", "placeholder", "-rconf"] + configFileStr = "assets/extractor/Config_" + xmlVersion + ".xml" + exec_cmd.extend([configFileStr]) + + otrFileName = "oot.o2r" + if isMM: + otrFileName = "mm.o2r" + + + # generate headers, but not otrs by excluding the otr exporter + if genHeaders: + exec_cmd.extend(["-gsf", "1"]) + else: + # generate otrs, but not headers + exec_cmd.extend(["-gsf", "0", "-se", "OTR", "--customAssetsPath", customAssetsPath, + "--customOtrFile", customOtrFile, "--otrfile", otrFileName]) + + if portVer: + exec_cmd.extend(["--portVer", portVer]) + + print(exec_cmd) + print(os.getcwd()) + exitValue = subprocess.call(exec_cmd) + if exitValue != 0: + print("\n") + print("Error when building the OTR file...", file=os.sys.stderr) + print("Aborting...", file=os.sys.stderr) + print("\n") + +def BuildCustomOtr(zapd_exe=None, assets_path=None, otrfile=None, portVer=None): + if not zapd_exe: + zapd_exe = "x64\\Release\\ZAPD.exe" if sys.platform == "win32" else "../ZAPDTR/ZAPD.out" + + if not assets_path or not otrfile: + print("\n") + print("Assets path or otrfile name not provided. Exiting...", file=os.sys.stderr) + print("\n") + return + + exec_cmd = [zapd_exe, "botr", "-se", "OTR", "--norom", "--customAssetsPath", assets_path, "--customOtrFile", otrfile] + + if portVer: + exec_cmd.extend(["--portVer", portVer]) + + print(exec_cmd) + exitValue = subprocess.call(exec_cmd) + if exitValue != 0: + print("\n") + print("Error when building custom otr file...", file=os.sys.stderr) + print("Aborting...", file=os.sys.stderr) + print("\n") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-z", "--zapd", help="Path to ZAPD executable", dest="zapd_exe", type=str) + parser.add_argument("rom", help="Path to the rom", type=str, nargs="?") + parser.add_argument("--non-interactive", help="Runs the script non-interactively for use in build scripts.", dest="non_interactive", action="store_true") + parser.add_argument("-v", "--verbose", help="Display rom's header checksums and their corresponding xml folder", dest="verbose", action="store_true") + parser.add_argument("--gen-headers", help="Generate source headers to be checked in", dest="gen_headers", action="store_true") + parser.add_argument("--norom", help="Generate only custom otr to be bundled to the game", dest="norom", action="store_true") + parser.add_argument("--xml-root", help="Root path for the rom xmls", dest="xml_root", type=str) + parser.add_argument("--custom-assets-path", help="Path to custom assets for the custom otr file", dest="custom_assets_path", type=str) + parser.add_argument("--custom-otr-file", help="Name for custom otr file", dest="custom_otr_file", type=str) + parser.add_argument("--port-ver", help="Store the port version in the otr", dest="port_ver", type=str) + + args = parser.parse_args() + + if args.norom: + BuildCustomOtr(args.zapd_exe, args.custom_assets_path, args.custom_otr_file, portVer=args.port_ver) + return + + roms = [ Z64Rom(args.rom) ] if args.rom else rom_chooser.chooseROM(args.verbose, args.non_interactive) + for rom in roms: + BuildOTR(args.xml_root, rom.version.xml_ver, rom.file_path, rom.version.is_mm, zapd_exe=args.zapd_exe, genHeaders=args.gen_headers, + customAssetsPath=args.custom_assets_path, customOtrFile=args.custom_otr_file, portVer=args.port_ver) + +if __name__ == "__main__": + main() diff --git a/OTRExporter/extract_baserom.py b/OTRExporter/extract_baserom.py new file mode 100644 index 000000000..f387df57a --- /dev/null +++ b/OTRExporter/extract_baserom.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 + +import os +import sys +import struct +from multiprocessing import Pool, cpu_count +from rom_info import Z64Rom +import rom_chooser + + +rom = None + +def initialize_worker(input_rom): + global rom + rom = input_rom + +def ExtractFunc(i): + + dma_file = rom.getDmaEntryByIndex(i) + dma_data = rom.readDmaEntry(dma_file) + + filename = '../soh/baserom/' + rom.version.file_table[i] + print('extracting ' + filename + " (0x%08X, 0x%08X)" % (dma_file.virtStart, dma_file.virtEnd)) + + try: + with open(filename, 'wb') as f: + f.write(dma_data) + except IOError: + print('failed to write file ' + filename) + + # TODO: handle this better + if dma_file.compressed: + os.system('tools/yaz0 -d ' + filename + ' ' + filename) + +##################################################################### + +def main(): + try: + os.mkdir('../soh/baserom') + except: + pass + + rom_path = rom_chooser.chooseROM() + input_rom = Z64Rom(rom_path) + + # extract files + num_cores = cpu_count() + print("Extracting baserom with " + str(num_cores) + " CPU cores.") + with Pool(num_cores, initialize_worker, (input_rom,)) as p: + p.map(ExtractFunc, range(len(input_rom.version.file_table))) + +if __name__ == "__main__": + main() diff --git a/OTRExporter/offsets.json b/OTRExporter/offsets.json new file mode 100644 index 000000000..e03311620 --- /dev/null +++ b/OTRExporter/offsets.json @@ -0,0 +1,8 @@ +{ + "MQDebug": { + "bankdefs": [1281424, 128], + "fontdefs": [1278576, 624], + "fontmaps": [1279200, 448], + "seqdefs": [1279648, 1776] + } +} \ No newline at end of file diff --git a/OTRExporter/rom_chooser.py b/OTRExporter/rom_chooser.py new file mode 100644 index 000000000..a79eb837a --- /dev/null +++ b/OTRExporter/rom_chooser.py @@ -0,0 +1,54 @@ +import os, sys, glob + +from rom_info import Z64Rom + +def chooseROM(verbose=False, non_interactive=False): + roms = [] + + for file in glob.glob("../OTRExporter/*.z64"): + rom = Z64Rom(file) + if rom.is_valid: + roms.append(rom) + + if not (roms): + print("Error: No roms located, place one in the OTRExporter directory", file=os.sys.stderr) + sys.exit(1) + + if (len(roms) == 1): + return roms + + if non_interactive: + mq_rom = None + non_mq_rom = None + for rom in roms: + if rom.isMq and mq_rom is None: + mq_rom = rom + elif not rom.isMq and non_mq_rom is None: + non_mq_rom = rom + return [rom for rom in [non_mq_rom, mq_rom] if rom is not None] + + print(f"{len(roms)} roms found, please select one by pressing 1-{len(roms)}") + print() + + for i in range(len(roms)): + print(f"[{i+1:>2d}] {roms[i].file_path}") + if verbose: + print(f" Checksum: {roms[i].checksum.value}, Version XML: {roms[i].version.xml_ver}") + print() + + while(1): + try: + selection = int(input()) + except KeyboardInterrupt: + sys.exit(1) + except: + print("Bad input. Try again with the number keys.") + continue + + if (selection < 1 or selection > len(roms)): + print("Bad input. Try again.") + continue + + else: break + + return [ roms[selection - 1] ] diff --git a/OTRExporter/rom_info.py b/OTRExporter/rom_info.py new file mode 100644 index 000000000..04f73f055 --- /dev/null +++ b/OTRExporter/rom_info.py @@ -0,0 +1,129 @@ +from enum import Enum +from tabnanny import check +import struct + +class Checksums(Enum): + OOT_NTSC_10 = "EC7011B7" + OOT_NTSC_11 = "D43DA81F" + OOT_NTSC_12 = "693BA2AE" + OOT_PAL_10 = "B044B569" + OOT_PAL_11 = "B2055FBD" + OOT_NTSC_JP_GC_CE = "F7F52DB8" + OOT_NTSC_JP_GC = "F611F4BA" + OOT_NTSC_US_GC = "F3DD35BA" + OOT_PAL_GC = "09465AC3" + OOT_NTSC_JP_MQ = "F43B45BA" + OOT_NTSC_US_MQ = "F034001A" + OOT_PAL_MQ = "1D4136F3" + OOT_PAL_GC_DBG1 = "871E1C92" + OOT_PAL_GC_DBG2 = "87121EFE" + OOT_PAL_GC_MQ_DBG = "917D18F6" + OOT_IQUE_TW = "3D81FB3E" + OOT_IQUE_CN = "B1E1E07B" + + MM_US_10 = "5354631C" + MM_US_10_UNCOMPRESSED = "DA6983E7" + MM_US_GC = "B443EB08" + MM_JP_GC = "8473D0C1" + + UNKNOWN = "FFFFFFFF" + + @classmethod + def has_value(self, value): + return value in self._value2member_map_ + +class RomVersion: + def __init__(self, file_table_path, file_table_off, xml_ver, is_mm=False): + self.file_table_off = file_table_off + self.xml_ver = xml_ver + self.is_mm = is_mm + try: + with open(file_table_path, 'r') as f: + self.file_table = [line.strip('\n') for line in f] + except FileNotFoundError as e: + pass + # Catch files for the other game not being found + + +ROM_INFO_TABLE = dict() +ROM_INFO_TABLE[Checksums.OOT_PAL_GC] = RomVersion("assets/extractor/filelists/gamecube_pal.txt", 0x7170, "GC_NMQ_PAL_F") +ROM_INFO_TABLE[Checksums.OOT_PAL_MQ] = RomVersion("assets/extractor/filelists/gamecube_pal.txt", 0x7170, "GC_MQ_PAL_F") +ROM_INFO_TABLE[Checksums.OOT_PAL_GC_DBG1] = RomVersion("assets/extractor/filelists/dbg.txt", 0x12F70, "GC_NMQ_D") +ROM_INFO_TABLE[Checksums.OOT_PAL_GC_MQ_DBG] = RomVersion("assets/extractor/filelists/dbg.txt", 0x12F70, "GC_MQ_D") +ROM_INFO_TABLE[Checksums.OOT_PAL_10] = RomVersion("assets/extractor/filelists/pal_oot.txt", 0x7950, "N64_PAL_10") +ROM_INFO_TABLE[Checksums.OOT_PAL_11] = RomVersion("assets/extractor/filelists/pal_oot.txt", 0x7950, "N64_PAL_11") +ROM_INFO_TABLE[Checksums.OOT_NTSC_US_GC] = RomVersion("assets/extractor/filelists/gamecube.txt", 0x7170, "GC_NMQ_NTSC_U") +ROM_INFO_TABLE[Checksums.OOT_NTSC_JP_GC] = RomVersion("assets/extractor/filelists/gamecube.txt", 0x7170, "GC_NMQ_NTSC_J") +ROM_INFO_TABLE[Checksums.OOT_NTSC_JP_GC_CE] = RomVersion("assets/extractor/filelists/gamecube.txt", 0x7170, "GC_NMQ_NTSC_J_CE") +ROM_INFO_TABLE[Checksums.OOT_NTSC_US_MQ] = RomVersion("assets/extractor/filelists/gamecube.txt", 0x7170, "GC_MQ_NTSC_U") +ROM_INFO_TABLE[Checksums.OOT_NTSC_JP_MQ] = RomVersion("assets/extractor/filelists/gamecube.txt", 0x7170, "GC_MQ_NTSC_J") +ROM_INFO_TABLE[Checksums.OOT_NTSC_10] = RomVersion("assets/extractor/filelists/ntsc_oot.txt", 0x7430, "N64_NTSC_10") +ROM_INFO_TABLE[Checksums.OOT_NTSC_11] = RomVersion("assets/extractor/filelists/ntsc_oot.txt", 0x7430, "N64_NTSC_11") +ROM_INFO_TABLE[Checksums.OOT_NTSC_12] = RomVersion("assets/extractor/filelists/ntsc_12_oot.txt", 0x7430, "N64_NTSC_12") + +ROM_INFO_TABLE[Checksums.MM_US_10] = RomVersion("assets/extractor/filelists/mm.txt", 0x1A500, "N64_US", is_mm=True) +ROM_INFO_TABLE[Checksums.MM_US_10_UNCOMPRESSED] = RomVersion("assets/extractor/filelists/mm.txt", 0x1A500, "N64_US", is_mm=True) +ROM_INFO_TABLE[Checksums.MM_US_GC] = RomVersion("assets/extractor/filelists/mm_gc.txt", 0x1AE90, "GC_US", is_mm=True) +ROM_INFO_TABLE[Checksums.MM_JP_GC] = RomVersion("assets/extractor/filelists/mm_gc_jp.txt", 0x1AE90, "GC_JP", is_mm=True) + +class RomDmaEntry: + def __init__(self, rom, i): + + off = rom.version.file_table_off + 16 * i + + (self.virtStart, \ + self.virtEnd, \ + self.physStart, \ + self.physEnd) = struct.unpack('>IIII', rom.rom_data[off:off+4*4]) + + self.compressed = self.physEnd != 0 + self.size = self.physEnd - self.physStart \ + if self.compressed \ + else self.virtEnd - self.virtStart + self.name = rom.version.file_table[i] + + +class Z64Rom: + def __init__(self, file_path): + self.file_path = file_path + with open(file_path, 'rb') as f: + self.rom_data = f.read() + + self.is_valid = len(self.rom_data) > 20 * 1024 * 1024 + + if not self.is_valid: + return + + # get checkum + checksum_str = self.rom_data[16:16+4].hex().upper() + self.checksum = Checksums(checksum_str) if Checksums.has_value(checksum_str) else Checksums.UNKNOWN + + if self.checksum == Checksums.UNKNOWN: + self.is_valid = False + return + + if self.checksum in [Checksums.OOT_NTSC_JP_MQ, Checksums.OOT_NTSC_US_MQ, Checksums.OOT_PAL_GC_MQ_DBG, Checksums.OOT_PAL_MQ]: + self.isMq = True + else: + self.isMq = False + + # get rom version + self.version = ROM_INFO_TABLE[self.checksum] + + def getDmaEntryByIndex(self, i): + return RomDmaEntry(self, i) + + def readDmaEntry(self, entry): + return self.rom_data[entry.physStart:entry.physStart + entry.size] + + @staticmethod + def isValidRom(rom_path): + return Z64Rom(rom_path).is_valid + + @staticmethod + def isMqRom(rom_path): + return Z64Rom(rom_path).isMq + + @staticmethod + def isMMRom(rom_path): + return Z64Rom(rom_path).version.is_mm diff --git a/ZAPDTR/.clang-format b/ZAPDTR/.clang-format new file mode 100644 index 000000000..5ba1c4a95 --- /dev/null +++ b/ZAPDTR/.clang-format @@ -0,0 +1,84 @@ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +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 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +TabWidth: 4 +UseTab: AlignWithSpaces +... + diff --git a/ZAPDTR/.github/workflows/main.yml b/ZAPDTR/.github/workflows/main.yml new file mode 100644 index 000000000..4c35dfac9 --- /dev/null +++ b/ZAPDTR/.github/workflows/main.yml @@ -0,0 +1,83 @@ +name: Build ZAPD + +on: + push: + pull_request: + branches: + - master + +jobs: + build: + runs-on: self-hosted-runner + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Build ZAPD + run: make -j WERROR=1 + + - name: Checkout Repos + run: echo "Checkout Repos" + + - name: Checkout oot + run: | + git clone https://github.com/zeldaret/oot.git + cd oot + git submodule update --init --recursive + + - name: Checkout mm + run: | + git clone https://github.com/zeldaret/mm.git + cd mm + + - name: Set up repos + run: echo "Set up repos" + + - name: Setup OOT + run: | + cd oot + mkdir -p baseroms/gc-eu-mq-dbg + cp ~/baserom_original.z64 ./baseroms/gc-eu-mq-dbg/baserom.z64 + make venv + make -C tools -j + cp ../ZAPD.out tools/ZAPD/ + .venv/bin/python3 tools/decompress_baserom.py gc-eu-mq-dbg + .venv/bin/python3 extract_baserom.py + .venv/bin/python3 extract_assets.py -j 4 + + - name: Install Python dependencies + run: | + cd mm + python3 -m venv .mm-env + source .mm-env/bin/activate + python3 -m pip install -r requirements.txt + + - name: Setup MM + run: | + cd mm + cp ~/baserom.mm.us.rev1.z64 ./baserom.mm.us.rev1.z64 + make -C tools -j + cp ../ZAPD.out tools/ZAPD/ + python3 tools/fixbaserom.py + python3 tools/extract_baserom.py + python3 tools/decompress_yars.py + python3 extract_assets.py -j $(nproc) + + - name: Build Repos + run: echo "Build Repos" + + - name: Build oot + run: | + cd oot + make venv + make -j + + - name: Build mm + run: | + cd mm + make -j disasm + make -j + + - name: Clean workspace + run: git clean -fdX diff --git a/ZAPDTR/.gitignore b/ZAPDTR/.gitignore new file mode 100644 index 000000000..68c502e36 --- /dev/null +++ b/ZAPDTR/.gitignore @@ -0,0 +1,341 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +*.out +*.o +*.d +lib/libgfxd/libgfxd.a +ExporterTest/ExporterTest.a +ZAPDUtils/ZAPDUtils.a +.vscode/ +build/ +ZAPDUtils/build/ +ZAPD/BuildInfo.h diff --git a/ZAPDTR/ExporterTest/CollisionExporter.cpp b/ZAPDTR/ExporterTest/CollisionExporter.cpp new file mode 100644 index 000000000..f7501b2d6 --- /dev/null +++ b/ZAPDTR/ExporterTest/CollisionExporter.cpp @@ -0,0 +1,76 @@ +#include "CollisionExporter.h" + +void ExporterExample_Collision::Save(ZResource* res, [[maybe_unused]] const fs::path& outPath, + BinaryWriter* writer) +{ + ZCollisionHeader* col = (ZCollisionHeader*)res; + + writer->Write(col->absMinX); + writer->Write(col->absMinY); + writer->Write(col->absMinZ); + + writer->Write(col->absMaxX); + writer->Write(col->absMaxY); + writer->Write(col->absMaxZ); + + writer->Write(col->numVerts); + writer->Write(col->vtxAddress); + + writer->Write(col->numPolygons); + writer->Write(col->polyAddress); + writer->Write(col->polyTypeDefAddress); + writer->Write(col->camDataAddress); + + writer->Write(col->numWaterBoxes); + writer->Write(col->waterBoxAddress); + + writer->Write(col->vtxSegmentOffset); + writer->Write(col->polySegmentOffset); + writer->Write(col->polyTypeDefSegmentOffset); + writer->Write(col->camDataSegmentOffset); + writer->Write(col->waterBoxSegmentOffset); + + uint32_t oldOffset = writer->GetBaseAddress(); + + writer->Seek(col->vtxSegmentOffset, SeekOffsetType::Start); + + for (uint16_t i = 0; i < col->vertices.size(); i++) + { + for (uint32_t j = 0; j < col->vertices[i].dimensions; j++) + { + writer->Write(col->vertices[i].scalars[j].scalarData.s16); + } + } + + writer->Seek(col->polySegmentOffset, SeekOffsetType::Start); + + for (uint16_t i = 0; i < col->polygons.size(); i++) + { + writer->Write(col->polygons[i].type); + writer->Write(col->polygons[i].vtxA); + writer->Write(col->polygons[i].vtxB); + writer->Write(col->polygons[i].vtxC); + writer->Write(col->polygons[i].normX); + writer->Write(col->polygons[i].normY); + writer->Write(col->polygons[i].normZ); + writer->Write(col->polygons[i].dist); + } + + writer->Seek(col->polyTypeDefSegmentOffset, SeekOffsetType::Start); + + for (const auto& poly : col->polygonTypes) + { + writer->Write(poly.data[0]); + writer->Write(poly.data[1]); + } + writer->Seek(col->camDataSegmentOffset, SeekOffsetType::Start); + + for (auto entry : col->camData->entries) + { + writer->Write(entry.cameraSType); + writer->Write(entry.numData); + writer->Write(entry.cameraPosDataSeg); + } + + writer->Seek(oldOffset, SeekOffsetType::Start); +} diff --git a/ZAPDTR/ExporterTest/CollisionExporter.h b/ZAPDTR/ExporterTest/CollisionExporter.h new file mode 100644 index 000000000..1dc50634e --- /dev/null +++ b/ZAPDTR/ExporterTest/CollisionExporter.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ZCollision.h" +#include "ZResource.h" + +class ExporterExample_Collision : public ZResourceExporter +{ +public: + void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/ZAPDTR/ExporterTest/Main.cpp b/ZAPDTR/ExporterTest/Main.cpp new file mode 100644 index 000000000..07fdbeece --- /dev/null +++ b/ZAPDTR/ExporterTest/Main.cpp @@ -0,0 +1,79 @@ +#include "CollisionExporter.h" +#include "Globals.h" +#include "RoomExporter.h" +#include "TextureExporter.h" + +enum class ExporterFileMode +{ + ModeExample1 = (int)ZFileMode::Custom + 1, + ModeExample2 = (int)ZFileMode::Custom + 2, + ModeExample3 = (int)ZFileMode::Custom + 3, +}; + +static void ExporterParseFileMode(const std::string& buildMode, ZFileMode& fileMode) +{ + if (buildMode == "me1") + fileMode = (ZFileMode)ExporterFileMode::ModeExample1; + else if (buildMode == "me2") + fileMode = (ZFileMode)ExporterFileMode::ModeExample2; + else if (buildMode == "me3") + fileMode = (ZFileMode)ExporterFileMode::ModeExample3; +} + +static void ExporterParseArgs([[maybe_unused]] int argc, char* argv[], int& i) +{ + std::string arg = argv[i]; + + if (arg == "--do-x") + { + } + else if (arg == "--do-y") + { + } +} + +static bool ExporterProcessFileMode(ZFileMode fileMode) +{ + // Do whatever work is associated with these custom file modes... + // Return true to indicate one of our own file modes is being processed + if (fileMode == (ZFileMode)ExporterFileMode::ModeExample1) + return true; + else if (fileMode == (ZFileMode)ExporterFileMode::ModeExample2) + return true; + else if (fileMode == (ZFileMode)ExporterFileMode::ModeExample3) + return true; + + return false; +} + +static void ExporterFileBegin(ZFile* file) +{ + printf("ExporterFileBegin() called on ZFile %s.\n", file->GetName().c_str()); +} + +static void ExporterFileEnd(ZFile* file) +{ + printf("ExporterFileEnd() called on ZFile %s.\n", file->GetName().c_str()); +} + +static void ImportExporters() +{ + // In this example we set up a new exporter called "EXAMPLE". + // By running ZAPD with the argument -se EXAMPLE, we tell it that we want to use this exporter + // for our resources. + ExporterSet* exporterSet = new ExporterSet(); + exporterSet->processFileModeFunc = ExporterProcessFileMode; + exporterSet->parseFileModeFunc = ExporterParseFileMode; + exporterSet->parseArgsFunc = ExporterParseArgs; + exporterSet->beginFileFunc = ExporterFileBegin; + exporterSet->endFileFunc = ExporterFileEnd; + exporterSet->exporters[ZResourceType::Texture] = new ExporterExample_Texture(); + exporterSet->exporters[ZResourceType::Room] = new ExporterExample_Room(); + exporterSet->exporters[ZResourceType::CollisionHeader] = new ExporterExample_Collision(); + + Globals::AddExporter("EXAMPLE", exporterSet); +} + +// When ZAPD starts up, it will automatically call the below function, which in turn sets up our +// exporters. +REGISTER_EXPORTER(ImportExporters); diff --git a/ZAPDTR/ExporterTest/RoomExporter.cpp b/ZAPDTR/ExporterTest/RoomExporter.cpp new file mode 100644 index 000000000..56b315388 --- /dev/null +++ b/ZAPDTR/ExporterTest/RoomExporter.cpp @@ -0,0 +1,372 @@ +#include "RoomExporter.h" +#include "CollisionExporter.h" +#include "Utils/BinaryWriter.h" +#include +#include "Utils/MemoryStream.h" +#include "ZRoom/Commands/SetCameraSettings.h" +#include "ZRoom/Commands/SetCollisionHeader.h" +#include "ZRoom/Commands/SetCsCamera.h" +#include "ZRoom/Commands/SetEchoSettings.h" +#include "ZRoom/Commands/SetEntranceList.h" +#include "ZRoom/Commands/SetLightingSettings.h" +#include "ZRoom/Commands/SetMesh.h" +#include "ZRoom/Commands/SetRoomBehavior.h" +#include "ZRoom/Commands/SetRoomList.h" +#include "ZRoom/Commands/SetSkyboxModifier.h" +#include "ZRoom/Commands/SetSkyboxSettings.h" +#include "ZRoom/Commands/SetSoundSettings.h" +#include "ZRoom/Commands/SetSpecialObjects.h" +#include "ZRoom/Commands/SetStartPositionList.h" +#include "ZRoom/Commands/SetTimeSettings.h" +#include "ZRoom/Commands/SetWind.h" + +void ExporterExample_Room::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZRoom* room = dynamic_cast(res); + + // MemoryStream* memStream = new MemoryStream(); + // BinaryWriter* writer = new BinaryWriter(memStream); + + for (size_t i = 0; i < room->commands.size() * 8; i++) + writer->Write((uint8_t)0); + + for (size_t i = 0; i < room->commands.size(); i++) + { + ZRoomCommand* cmd = room->commands[i]; + + writer->Seek(i * 8, SeekOffsetType::Start); + + writer->Write((uint8_t)cmd->cmdID); + + switch (cmd->cmdID) + { + case RoomCommand::SetWind: + { + SetWind* cmdSetWind = (SetWind*)cmd; + + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + + writer->Write(cmdSetWind->windWest); // 0x04 + writer->Write(cmdSetWind->windVertical); // 0x05 + writer->Write(cmdSetWind->windSouth); // 0x06 + writer->Write(cmdSetWind->clothFlappingStrength); // 0x07 + } + break; + case RoomCommand::SetTimeSettings: + { + SetTimeSettings* cmdTime = (SetTimeSettings*)cmd; + + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + + writer->Write(cmdTime->hour); // 0x04 + writer->Write(cmdTime->min); // 0x05 + writer->Write(cmdTime->unk); // 0x06 + writer->Write((uint8_t)0); // 0x07 + } + break; + case RoomCommand::SetSkyboxModifier: + { + SetSkyboxModifier* cmdSkybox = (SetSkyboxModifier*)cmd; + + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + + writer->Write(cmdSkybox->disableSky); // 0x04 + writer->Write(cmdSkybox->disableSunMoon); // 0x05 + writer->Write((uint8_t)0); // 0x06 + writer->Write((uint8_t)0); // 0x07 + } + break; + case RoomCommand::SetEchoSettings: + { + SetEchoSettings* cmdEcho = (SetEchoSettings*)cmd; + + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write((uint8_t)0); // 0x04 + writer->Write((uint8_t)0); // 0x05 + writer->Write((uint8_t)0); // 0x06 + writer->Write((uint8_t)cmdEcho->echo); // 0x07 + } + break; + case RoomCommand::SetSoundSettings: + { + SetSoundSettings* cmdSound = (SetSoundSettings*)cmd; + + writer->Write((uint8_t)cmdSound->reverb); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write((uint8_t)0); // 0x04 + writer->Write((uint8_t)0); // 0x05 + + writer->Write(cmdSound->nightTimeSFX); // 0x06 + writer->Write(cmdSound->musicSequence); // 0x07 + } + break; + case RoomCommand::SetSkyboxSettings: + { + SetSkyboxSettings* cmdSkybox = (SetSkyboxSettings*)cmd; + + writer->Write((uint8_t)cmdSkybox->unk1); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write((uint8_t)cmdSkybox->skyboxNumber); // 0x04 + writer->Write((uint8_t)cmdSkybox->cloudsType); // 0x05 + writer->Write((uint8_t)cmdSkybox->isIndoors); // 0x06 + } + break; + case RoomCommand::SetRoomBehavior: + { + SetRoomBehavior* cmdRoom = (SetRoomBehavior*)cmd; + + writer->Write((uint8_t)cmdRoom->gameplayFlags); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write(cmdRoom->gameplayFlags2); // 0x04 + } + break; + case RoomCommand::SetCsCamera: + { + SetCsCamera* cmdCsCam = (SetCsCamera*)cmd; + + writer->Write((uint8_t)cmdCsCam->cameras.size()); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + + writer->Write(cmdCsCam->segmentOffset); // 0x04 + } + break; + case RoomCommand::SetMesh: + { + SetMesh* cmdMesh = (SetMesh*)cmd; + + int baseStreamEnd = writer->GetStream().get()->GetLength(); + + writer->Write((uint8_t)cmdMesh->data); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + + writer->Write(baseStreamEnd); // 0x04 + + uint32_t oldOffset = writer->GetBaseAddress(); + writer->Seek(baseStreamEnd, SeekOffsetType::Start); + + // TODO: NOT DONE + + writer->Write(cmdMesh->meshHeaderType); + + if (cmdMesh->meshHeaderType == 0) + { + // writer->Write(cmdMesh->) + } + else if (cmdMesh->meshHeaderType == 1) + { + } + else if (cmdMesh->meshHeaderType == 2) + { + } + + writer->Seek(oldOffset, SeekOffsetType::Start); + } + break; + case RoomCommand::SetCameraSettings: + { + SetCameraSettings* cmdCam = (SetCameraSettings*)cmd; + + writer->Write((uint8_t)cmdCam->cameraMovement); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write(cmdCam->mapHighlight); // 0x04 + } + break; + case RoomCommand::SetLightingSettings: + { + SetLightingSettings* cmdLight = (SetLightingSettings*)cmd; + + writer->Write((uint8_t)cmdLight->settings.size()); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write(cmdLight->segmentOffset); // 0x04 + + uint32_t oldOffset = writer->GetBaseAddress(); + writer->Seek(cmdLight->segmentOffset, SeekOffsetType::Start); + + for (LightingSettings setting : cmdLight->settings) + { + writer->Write(setting.ambientClrR); + writer->Write(setting.ambientClrG); + writer->Write(setting.ambientClrB); + + writer->Write(setting.diffuseClrA_R); + writer->Write(setting.diffuseClrA_G); + writer->Write(setting.diffuseClrA_B); + + writer->Write(setting.diffuseDirA_X); + writer->Write(setting.diffuseDirA_Y); + writer->Write(setting.diffuseDirA_Z); + + writer->Write(setting.diffuseClrB_R); + writer->Write(setting.diffuseClrB_G); + writer->Write(setting.diffuseClrB_B); + + writer->Write(setting.diffuseDirB_X); + writer->Write(setting.diffuseDirB_Y); + writer->Write(setting.diffuseDirB_Z); + + writer->Write(setting.fogClrR); + writer->Write(setting.fogClrG); + writer->Write(setting.fogClrB); + + writer->Write(setting.unk); + writer->Write(setting.drawDistance); + } + + writer->Seek(oldOffset, SeekOffsetType::Start); + } + break; + case RoomCommand::SetRoomList: + { + SetRoomList* cmdRoom = (SetRoomList*)cmd; + + writer->Write((uint8_t)cmdRoom->romfile->rooms.size()); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + + auto baseStreamEnd = writer->GetLength(); + writer->Write(baseStreamEnd); // 0x04 + + uint32_t oldOffset = writer->GetBaseAddress(); + writer->Seek(baseStreamEnd, SeekOffsetType::Start); + + for (const auto& entry : cmdRoom->romfile->rooms) + { + writer->Write(entry.virtualAddressStart); + writer->Write(entry.virtualAddressEnd); + } + + writer->Seek(oldOffset, SeekOffsetType::Start); + } + break; + case RoomCommand::SetCollisionHeader: + { + SetCollisionHeader* cmdCollHeader = (SetCollisionHeader*)cmd; + + int streamEnd = writer->GetStream().get()->GetLength(); + + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write(streamEnd); // 0x04 + + // TODO: NOT DONE + + uint32_t oldOffset = writer->GetBaseAddress(); + writer->Seek(streamEnd, SeekOffsetType::Start); + + ExporterExample_Collision colExp = ExporterExample_Collision(); + + colExp.Save(cmdCollHeader->collisionHeader, outPath, writer); + + writer->Seek(oldOffset, SeekOffsetType::Start); + } + break; + case RoomCommand::SetEntranceList: + { + SetEntranceList* cmdEntrance = (SetEntranceList*)cmd; + + uint32_t baseStreamEnd = writer->GetStream().get()->GetLength(); + + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write(baseStreamEnd); // 0x04 + + uint32_t oldOffset = writer->GetBaseAddress(); + writer->Seek(baseStreamEnd, SeekOffsetType::Start); + + for (Spawn entry : cmdEntrance->entrances) + { + writer->Write((uint8_t)entry.startPositionIndex); + writer->Write((uint8_t)entry.roomToLoad); + } + + writer->Seek(oldOffset, SeekOffsetType::Start); + } + break; + case RoomCommand::SetSpecialObjects: + { + SetSpecialObjects* cmdSpecObj = (SetSpecialObjects*)cmd; + + writer->Write((uint8_t)cmdSpecObj->elfMessage); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write((uint8_t)0); // 0x04 + writer->Write((uint8_t)0); // 0x05 + writer->Write((uint16_t)cmdSpecObj->globalObject); // 0x06 + } + break; + case RoomCommand::SetStartPositionList: + { + SetStartPositionList* cmdStartPos = (SetStartPositionList*)cmd; + + uint32_t baseStreamEnd = writer->GetStream().get()->GetLength(); + + writer->Write((uint8_t)cmdStartPos->actors.size()); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write(baseStreamEnd); // 0x04 + + uint32_t oldOffset = writer->GetBaseAddress(); + writer->Seek(baseStreamEnd, SeekOffsetType::Start); + + for (ActorSpawnEntry entry : cmdStartPos->actors) + { + writer->Write(entry.actorNum); + writer->Write(entry.posX); + writer->Write(entry.posY); + writer->Write(entry.posZ); + writer->Write(entry.rotX); + writer->Write(entry.rotY); + writer->Write(entry.rotZ); + writer->Write(entry.params); + } + + writer->Seek(oldOffset, SeekOffsetType::Start); + } + break; + case RoomCommand::EndMarker: + { + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write((uint8_t)0); // 0x04 + writer->Write((uint8_t)0); // 0x05 + writer->Write((uint8_t)0); // 0x06 + writer->Write((uint8_t)0); // 0x07 + } + break; + default: + printf("UNIMPLEMENTED COMMAND: %i\n", (int)cmd->cmdID); + + writer->Write((uint8_t)0); // 0x01 + writer->Write((uint8_t)0); // 0x02 + writer->Write((uint8_t)0); // 0x03 + writer->Write((uint8_t)0); // 0x04 + writer->Write((uint8_t)0); // 0x05 + writer->Write((uint8_t)0); // 0x06 + writer->Write((uint8_t)0); // 0x07 + + break; + } + } + + // writer->Close(); + // DiskFile::WriteAllBytes(StringHelper::Sprintf("%s", res->GetName().c_str()), + // memStream->ToVector()); +} diff --git a/ZAPDTR/ExporterTest/RoomExporter.h b/ZAPDTR/ExporterTest/RoomExporter.h new file mode 100644 index 000000000..d8f7eae01 --- /dev/null +++ b/ZAPDTR/ExporterTest/RoomExporter.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ZResource.h" +#include "ZRoom/ZRoom.h" + +class ExporterExample_Room : public ZResourceExporter +{ +public: + void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/ZAPDTR/ExporterTest/TextureExporter.cpp b/ZAPDTR/ExporterTest/TextureExporter.cpp new file mode 100644 index 000000000..58d0964d3 --- /dev/null +++ b/ZAPDTR/ExporterTest/TextureExporter.cpp @@ -0,0 +1,14 @@ +#include "TextureExporter.h" +#include "../ZAPD/ZFile.h" + +void ExporterExample_Texture::Save(ZResource* res, [[maybe_unused]] const fs::path& outPath, + BinaryWriter* writer) +{ + ZTexture* tex = (ZTexture*)res; + + auto data = tex->parent->GetRawData(); + + for (offset_t i = tex->GetRawDataIndex(); i < tex->GetRawDataIndex() + tex->GetRawDataSize(); + i++) + writer->Write(data[i]); +} diff --git a/ZAPDTR/ExporterTest/TextureExporter.h b/ZAPDTR/ExporterTest/TextureExporter.h new file mode 100644 index 000000000..f3922cac1 --- /dev/null +++ b/ZAPDTR/ExporterTest/TextureExporter.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Utils/BinaryWriter.h" +#include "ZResource.h" +#include "ZTexture.h" + +class ExporterExample_Texture : public ZResourceExporter +{ +public: + void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/ZAPDTR/LICENSE b/ZAPDTR/LICENSE new file mode 100644 index 000000000..90b734bde --- /dev/null +++ b/ZAPDTR/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Zelda Reverse Engineering Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ZAPDTR/README.md b/ZAPDTR/README.md new file mode 100644 index 000000000..76dfbd1e9 --- /dev/null +++ b/ZAPDTR/README.md @@ -0,0 +1,164 @@ +# ZAPD: Zelda Asset Processor for Decomp + +## Compiling + +### Dependencies + +ZAPD needs a compiler with C++17 support. + +ZAPD has the following library dependencies: + +- `libpng` + +In a Debian/Ubuntu based environment, those could be installed with the following command: + +```bash +sudo apt install libpng-dev +``` + +On a Mac, you will need to install libpng with Homebrew or MacPorts; we currently only support Homebrew. You can run + +```bash +brew install libpng +``` + +to install it via Homebrew. + +### Building + +#### Linux / *nix + +ZAPD uses the classic `Makefile` approach. To build just run `make` (or even better `make -j` for faster compilations). + +You can configure a bit your ZAPD build with the following options: + +- `OPTIMIZATION_ON`: If set to `0` optimizations will be disabled (compile with `-O0`). Any other value compiles with `-O2`. Defaults to `1`. +- `ASAN`: If it is set to a non-zero then ZAPD will be compiled with Address Sanitizer enabled (`-fsanitize=address`). Defaults to `0`. +- `DEPRECATION_ON`: If it is set to a zero then deprecation warnings will be disabled. Defaults to `1`. +- `DEBUG`: If non-zero, ZAPD will be compiled in _development mode_. This implies the following: + - Debugging symbols enabled (`-g3`). They are disabled by default. + - `OPTIMIZATION_ON=0`: Disables optimizations (`-O0`). + - `DEPRECATION_ON=0`: Disables deprecation warnings. +- `LLD=1`: builds with the LLVM linker `ld.lld` instead of the system default. + +As an example, if you want to build ZAPD with optimizations disabled and use the address sanitizer, you could use the following command: + +```bash +make -j OPTIMIZATION_ON=0 ASAN=1 +``` + +#### Windows + +This repository contains `vcxproj` files for compiling under Visual Studio environments. See `ZAPD/ZAPD.vcxproj`. + +## Invoking ZAPD + +ZAPD needs a _File parsing mode_ to be passed as first parameter. The options are: + +- `e`: "Extraction" mode. + - In this mode, ZAPD expects a XML file as input, a folder as ouput and a path to the baserom files. + - ZAPD will read the XML and use it as a guide to extract the contents of the specified asset file from the baserom folder. + - For more info of the format of those XMLs, see the [ZAPD extraction XML reference](docs/zapd_extraction_xml_reference.md). +- `bsf`: "Build source file" mode. + - This is an experimental mode. + - It was going to be used to let you have XMLs that aren't just for extraction. Might get used, might not. Still need to experiment on that. +- `btex`: "Build texture" mode. + - In this mode, ZAPD expects a PNG file as input, a filename as ouput and a texture type parameter (`-tt`). + - ZAPD will try to convert the given PNG into the contents of a `uint64_t` C array. +- `bren`: "Build (render) background" mode. + - In this mode, ZAPD expects a JPG file as input and a filename as ouput. + - ZAPD will try to convert the given JPG into the contents of a `uint64_t` C array. +- `blb`: "Build blob" mode. + - In this mode, ZAPD expects a BIN file as input and a filename as ouput. + - ZAPD will try to convert the given BIN into the contents of a `uint8_t` C array. + +ZAPD also accepts the following list of extra parameters: + +- `-i PATH` / `--inputpath PATH`: Set input path. +- `-o PATH` / `--outputpath PATH`: Set output path. +- `-b PATH` / `--baserompath`: Set baserom path. + - Can be used only in `e` or `bsf` modes. +- `-osf PATH`: Set source output path. This is the path where the `.c` and `.h` files will be extracted to. If omitted, it will use the value passed to `--outputpath` parameter. +- `-gsf MODE`: Generate source file during extraction. If `MODE` is `1`, C source files will be generated. + - Can be used only in `e` mode. +- `-crc` / `--output-crc`: Outputs a CRC file for each extracted texture. + - Can be used only in `e` or `bsf` modes. +- `-ulzdl MODE`: Use "Legacy ZDisplayList" instead of `libgfxd`. Set `MODE` to `1` to enable it. + - Can be used only in `e` or `bsf` modes. +- `-profile MODE`: Enable profiling. Set `MODE` to `1` to enable it. +- `-uer MODE`: Split resources into their individual components (enabled by default). Set `MODE` to non-`1` to disable it. +- `-tt TYPE`: Set texture type. + - Can be used only in mode `btex`. + - Valid values: + - `rgba32` + - `rgb5a1` + - `i4` + - `i8` + - `ia4` + - `ia8` + - `ia16` + - `ci4` + - `ci8` +- `-rconf PATH` Read Config File. +- `-eh`: Enable error handler. + - Only available in non-Windows environments. +- `-v MODE`: Enable verbosity. Currently there are 3 possible values: + - `0`: Default. Completely silent (except for warnings and errors). + - `1`: Information. + - `2` (and higher): Debug. +- `-wu` / `--warn-unaccounted`: Enable warnings for each unaccounted block of data found. + - Can be used only in `e` or `bsf` modes. +- `-vu` / `--verbose-unaccounted`: Changes how unaccounteds are outputted. Max 4 bytes per line (a word) and add a comment with the offset of each of those lines. + - Could be useful for looking at raw data or testing. + - Can be used only in `e` or `bsf` modes. +- `-tm MODE`: Test Mode (enables certain experimental features). To enable it, set `MODE` to `1`. +- `-se` / `--set-exporter` : Sets which exporter to use. +- `--gcc-compat` : Enables GCC compatibly mode. Slower. +- `-us` / `--unaccounted-static` : Mark unaccounted data as `static` +- `-s` / `--static` : Mark every asset as `static`. + - This behaviour can be overridden per asset using `Static=` in the respective XML node. +- `-W...`: warning flags, see below + +Additionally, you can pass the flag `--version` to see the current ZAPD version. If that flag is passed, ZAPD will ignore any other parameter passed. + +### Warning flags + +ZAPD contains a variety of warning types, with similar syntax to GCC or Clang's compiler warnings. Warnings can have three levels: + +- Off (does not display anything) +- Warn (print a warning but continue processing) +- Err (behave like an error, i.e. print and throw an exception to crash ZAPD when occurs) + +Each warning type uses one of these by default, but can be modified with flags, similarly to GCC or Clang: + +- `-Wfoo` enables warnings of type `foo` +- `-Wno-foo` disables warnings of type `foo` +- `-Werror=foo` escalates `foo` to behave like an error +- `-Weverything` enables all warnings (they may be turned off using `-Wno-` flags afterwards) +- `-Werror` escalates all enabled warnings to errors + +All warning types currently implemented, with their default levels: + +| Warning type | Default level | Description | +| ----------------------------- | ------------- | ------------------------------------------------------------------------ | +| `-Wdeprecated` | Warn | Deprecated features | +| `-Whardcoded-generic-pointer` | Off | A generic segmented pointer must be produced | +| `-Whardcoded-pointer` | Warn | ZAPD lacks the info to make a symbol, so must output a hardcoded pointer | +| `-Wintersection` | Warn | Two assets intersect | +| `-Winvalid-attribute-value` | Err | Attribute declared in XML is wrong | +| `-Winvalid-extracted-data` | Err | Extracted data does not have correct form | +| `-Winvalid-jpeg` | Err | JPEG file does not conform to the game's format requirements | +| `-Winvalid-png` | Err | Issues arising when processing PNG data | +| `-Winvalid-xml` | Err | XML has syntax errors | +| `-Wmissing-attribute` | Warn | Required attribute missing in XML tag | +| `-Wmissing-offsets` | Warn | Offset attribute missing in XML tag | +| `-Wmissing-segment` | Warn | Segment not given in File tag in XML | +| `-Wnot-implemented` | Warn | ZAPD does not currently support this feature | +| `-Wunaccounted` | Off | Large blocks of unaccounted | +| `-Wunknown-attribute` | Warn | Unknown attribute in XML entry tag | + +There are also errors that do not have a type, and cannot be disabled. + +For example, here we have invoked ZAPD in the usual way to extract using a (rather badly-written) XML, but escalating `-Wintersection` to an error: + +![ZAPD warnings example](docs/zapd_warning_example.png?raw=true) diff --git a/ZAPDTR/ZAPD/CMakeLists.txt b/ZAPDTR/ZAPD/CMakeLists.txt new file mode 100644 index 000000000..7aa45fa74 --- /dev/null +++ b/ZAPDTR/ZAPD/CMakeLists.txt @@ -0,0 +1,454 @@ +set(PROJECT_NAME ZAPDLib) + +set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use") +#set(CMAKE_C_STANDARD 11 CACHE STRING "The C standard to use") +set(CMAKE_C_STANDARD 11) + +################################################################################ +# Source groups +################################################################################ +set(Header_Files + "../lib/tinyxml2/tinyxml2.h" + "CRC32.h" + "Declaration.h" + "FileWorker.h" + "GameConfig.h" + "Globals.h" + "ImageBackend.h" + "OutputFormatter.h" + "WarningHandler.h" + "CrashHandler.h" +) +source_group("Header Files" FILES ${Header_Files}) + +set(Header_Files__Libraries + "ctpl_stl.h" +) +source_group("Header Files\\Libraries" FILES ${Header_Files__Libraries}) + +set(Header_Files__Libraries__libgfxd + "../lib/libgfxd/gbi.h" + "../lib/libgfxd/gfxd.h" + "../lib/libgfxd/priv.h" +) +source_group("Header Files\\Libraries\\libgfxd" FILES ${Header_Files__Libraries__libgfxd}) + +set(Header_Files__Yaz0 + "yaz0/readwrite.h" + "yaz0/yaz0.h" +) +source_group("Header Files\\Yaz0" FILES ${Header_Files__Yaz0}) + +set(Header_Files__Z64 + "OtherStructs/Cutscene_Common.h" + "OtherStructs/CutsceneMM_Commands.h" + "OtherStructs/CutsceneOoT_Commands.h" + "OtherStructs/SkinLimbStructs.h" + "ZAnimation.h" + "ZActorList.h" + "ZArray.h" + "ZAudio.h" + "ZBackground.h" + "ZBlob.h" + "ZCollision.h" + "ZCollisionPoly.h" + "ZCKeyFrame.h" + "ZCkeyFrameAnim.h" + "ZCutscene.h" + "ZDisplayList.h" + "ZFile.h" + "ZLimb.h" + "ZMtx.h" + "ZPath.h" + "ZPlayerAnimationData.h" + "ZPointer.h" + "ZResource.h" + "ZRom.h" + "ZScalar.h" + "ZSkeleton.h" + "ZSurfaceType.h" + "ZString.h" + "ZSymbol.h" + "ZText.h" + "ZTextMM.cpp" + "ZTexture.h" + "ZTextureAnimation.h" + "ZVector.h" + "ZWaterbox.h" + "ZVtx.h" +) +source_group("Header Files\\Z64" FILES ${Header_Files__Z64}) + +set(Header_Files__Z64__ZRoom + "ZRoom/ZRoom.h" + "ZRoom/ZRoomCommand.h" +) +source_group("Header Files\\Z64\\ZRoom" FILES ${Header_Files__Z64__ZRoom}) + +set(Header_Files__Z64__ZRoom__Commands + "ZRoom/Commands/EndMarker.h" + "ZRoom/Commands/SetActorList.h" + "ZRoom/Commands/SetAlternateHeaders.h" + "ZRoom/Commands/SetAnimatedMaterialList.h" + "ZRoom/Commands/SetCameraSettings.h" + "ZRoom/Commands/SetCollisionHeader.h" + "ZRoom/Commands/SetCsCamera.h" + "ZRoom/Commands/SetCutscenes.h" + "ZRoom/Commands/SetCutsceneEntryList.cpp" + "ZRoom/Commands/SetEchoSettings.h" + "ZRoom/Commands/SetEntranceList.h" + "ZRoom/Commands/SetExitList.h" + "ZRoom/Commands/SetLightingSettings.h" + "ZRoom/Commands/SetLightList.h" + "ZRoom/Commands/SetMesh.h" + "ZRoom/Commands/SetMinimapChests.h" + "ZRoom/Commands/SetMinimapList.h" + "ZRoom/Commands/SetObjectList.h" + "ZRoom/Commands/SetPathways.h" + "ZRoom/Commands/SetRoomBehavior.h" + "ZRoom/Commands/SetRoomList.h" + "ZRoom/Commands/SetSkyboxModifier.h" + "ZRoom/Commands/SetSkyboxSettings.h" + "ZRoom/Commands/SetSoundSettings.h" + "ZRoom/Commands/SetSpecialObjects.h" + "ZRoom/Commands/SetStartPositionList.h" + "ZRoom/Commands/SetTimeSettings.h" + "ZRoom/Commands/SetTransitionActorList.h" + "ZRoom/Commands/SetWind.h" + "ZRoom/Commands/SetWorldMapVisited.h" + "ZRoom/Commands/Unused09.h" + "ZRoom/Commands/Unused1D.h" + "ZRoom/Commands/ZRoomCommandUnk.h" +) +source_group("Header Files\\Z64\\ZRoom\\Commands" FILES ${Header_Files__Z64__ZRoom__Commands}) + +set(Source_Files + "CrashHandler.cpp" + "Declaration.cpp" + "FileWorker.cpp" + "GameConfig.cpp" + "Globals.cpp" + "ImageBackend.cpp" + "Main.cpp" + "OutputFormatter.cpp" + "WarningHandler.cpp" +) +source_group("Source Files" FILES ${Source_Files}) + +set(Source_Files__Libraries__libgfxd + "../lib/libgfxd/gfxd.c" + "../lib/libgfxd/uc.c" + "../lib/libgfxd/uc_f3dex2.c" +) +source_group("Source Files\\Libraries\\libgfxd" FILES ${Source_Files__Libraries__libgfxd}) + +set(Source_Files__Yaz0 + "yaz0/yaz0.cpp" +) +source_group("Source Files\\Yaz0" FILES ${Source_Files__Yaz0}) + +set(Source_Files__Z64 + "OtherStructs/Cutscene_Common.cpp" + "OtherStructs/CutsceneMM_Commands.cpp" + "OtherStructs/CutsceneOoT_Commands.cpp" + "OtherStructs/SkinLimbStructs.cpp" + "ZAnimation.cpp" + "ZActorList.cpp" + "ZArray.cpp" + "ZAudio.cpp" + "ZAudioDecode.cpp" + "ZBackground.cpp" + "ZBlob.cpp" + "ZCollision.cpp" + "ZCollisionPoly.cpp" + "ZCKeyFrame.cpp" + "ZCKeyFrameAnim.cpp" + "ZCutscene.cpp" + "ZDisplayList.cpp" + "ZFile.cpp" + "ZLimb.cpp" + "ZMtx.cpp" + "ZPath.cpp" + "ZPlayerAnimationData.cpp" + "ZPointer.cpp" + "ZResource.cpp" + "ZRom.cpp" + "ZScalar.cpp" + "ZSkeleton.cpp" + "ZSurfaceType.cpp" + "ZString.cpp" + "ZSymbol.cpp" + "ZText.cpp" + "ZTextMM.cpp" + "ZTexture.cpp" + "ZTextureAnimation.cpp" + "ZVector.cpp" + "ZWaterbox.cpp" + "ZVtx.cpp" +) +source_group("Source Files\\Z64" FILES ${Source_Files__Z64}) + +set(Source_Files__Z64__ZRoom + "ZRoom/ZRoom.cpp" + "ZRoom/ZRoomCommand.cpp" +) +source_group("Source Files\\Z64\\ZRoom" FILES ${Source_Files__Z64__ZRoom}) + +set(Source_Files__Z64__ZRoom__Commands + "ZRoom/Commands/EndMarker.cpp" + "ZRoom/Commands/SetActorList.cpp" + "ZRoom/Commands/SetAlternateHeaders.cpp" + "ZRoom/Commands/SetAnimatedMaterialList.cpp" + "ZRoom/Commands/SetCameraSettings.cpp" + "ZRoom/Commands/SetCollisionHeader.cpp" + "ZRoom/Commands/SetCsCamera.cpp" + "ZRoom/Commands/SetCutscenes.cpp" + "ZRoom/Commands/SetCutsceneEntryList.cpp" + "ZRoom/Commands/SetEchoSettings.cpp" + "ZRoom/Commands/SetEntranceList.cpp" + "ZRoom/Commands/SetExitList.cpp" + "ZRoom/Commands/SetLightingSettings.cpp" + "ZRoom/Commands/SetLightList.cpp" + "ZRoom/Commands/SetMesh.cpp" + "ZRoom/Commands/SetMinimapChests.cpp" + "ZRoom/Commands/SetMinimapList.cpp" + "ZRoom/Commands/SetObjectList.cpp" + "ZRoom/Commands/SetPathways.cpp" + "ZRoom/Commands/SetRoomBehavior.cpp" + "ZRoom/Commands/SetRoomList.cpp" + "ZRoom/Commands/SetSkyboxModifier.cpp" + "ZRoom/Commands/SetSkyboxSettings.cpp" + "ZRoom/Commands/SetSoundSettings.cpp" + "ZRoom/Commands/SetSpecialObjects.cpp" + "ZRoom/Commands/SetStartPositionList.cpp" + "ZRoom/Commands/SetTimeSettings.cpp" + "ZRoom/Commands/SetTransitionActorList.cpp" + "ZRoom/Commands/SetWind.cpp" + "ZRoom/Commands/SetWorldMapVisited.cpp" + "ZRoom/Commands/Unused09.cpp" + "ZRoom/Commands/Unused1D.cpp" + "ZRoom/Commands/ZRoomCommandUnk.cpp" +) +source_group("Source Files\\Z64\\ZRoom\\Commands" FILES ${Source_Files__Z64__ZRoom__Commands}) + +file(GLOB Source_Files__Utils RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "Utils/*.c" "Utils/*.cpp") +source_group("Source Files\\Utils" FILES ${Source_Files__Utils}) + +file(GLOB Header_Files__Utils RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "Utils/*.h") +source_group("Header Files\\Utils" FILES ${Header_Files__Utils}) + +set(ALL_FILES + ${Header_Files} + ${Header_Files__Libraries} + ${Header_Files__Libraries__libgfxd} + ${Header_Files__Yaz0} + ${Header_Files__Z64} + ${Header_Files__Z64__ZRoom} + ${Header_Files__Z64__ZRoom__Commands} + ${Header_Files__Utils} + ${Source_Files} + ${Source_Files__Libraries__libgfxd} + ${Source_Files__Yaz0} + ${Source_Files__Z64} + ${Source_Files__Z64__ZRoom} + ${Source_Files__Z64__ZRoom__Commands} + ${Source_Files__Utils} + ${any__any} +) + +################################################################################ +# Target +################################################################################ + +add_library(${PROJECT_NAME} STATIC ${ALL_FILES}) + +add_executable(ZAPD ExecutableMain.cpp) +target_link_libraries(ZAPD ${PROJECT_NAME}) + + + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") +use_props(${PROJECT_NAME} "${CMAKE_CONFIGURATION_TYPES}" "${DEFAULT_CXX_PROPS}") +use_props(ZAPD "${CMAKE_CONFIGURATION_TYPES}" "${DEFAULT_CXX_PROPS}") +endif() +################################################################################ +# Includes for CMake from *.props +################################################################################ + +set(ROOT_NAMESPACE ZAPD) + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties(${PROJECT_NAME} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE "TRUE" + ) +elseif (CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin") + set_target_properties(ZAPD PROPERTIES + OUTPUT_NAME "ZAPD.out" + ) +endif() +################################################################################ +# MSVC runtime library +################################################################################ +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") +foreach(ZTarget ${PROJECT_NAME} ZAPD) + get_property(MSVC_RUNTIME_LIBRARY_DEFAULT TARGET ${ZTarget} PROPERTY MSVC_RUNTIME_LIBRARY) + string(CONCAT "MSVC_RUNTIME_LIBRARY_STR" + $<$: + MultiThreadedDebug + > + $<$: + MultiThreaded + > + $<$,$>>:${MSVC_RUNTIME_LIBRARY_DEFAULT}> + ) + set_target_properties(${ZTarget} PROPERTIES MSVC_RUNTIME_LIBRARY ${MSVC_RUNTIME_LIBRARY_STR}) +endforeach() +endif() +################################################################################ +# Compile definitions +################################################################################ +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + target_compile_definitions(${PROJECT_NAME} PRIVATE + "_CRT_SECURE_NO_WARNINGS;" + "_MBCS" + STORMLIB_NO_AUTO_LINK + ) +endif() + +################################################################################ +# Compile and link options +################################################################################ + +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CMAKE_FIND_FRAMEWORK LAST) +endif() +find_package(PNG REQUIRED) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/src/resource + ${CMAKE_CURRENT_SOURCE_DIR}/../../libultraship/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../ZAPDTR/lib/libgfxd + ${PNG_PNG_INCLUDE_DIR}/ + . + ) + +if(MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE + $<$: + /Od; + /RTC1 + > + $<$: + /O2; + /Oi; + /Gy + > + /permissive-; + /sdl; + /W3; + ${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT}; + ${DEFAULT_CXX_EXCEPTION_HANDLING} + ) + + target_link_options(${PROJECT_NAME} PRIVATE + $<$: + /PROFILE + > + $<$: + /OPT:REF; + /OPT:ICF + > + /DEBUG:FULL + ) +endif() + +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang") + target_compile_options(${PROJECT_NAME} PUBLIC + -Wall -Wextra -Wno-error + -Wno-unused-parameter + -Wno-unused-function + -Wno-unused-variable + -Wno-missing-field-initializers + -Wno-parentheses + -Wno-narrowing + $<$:-Wno-deprecated-enum-enum-conversion> + -pthread + ) + + if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + target_link_options(${PROJECT_NAME} PUBLIC + -pthread + ) + else() + target_link_options(${PROJECT_NAME} PUBLIC + -pthread + -Wl,-export-dynamic + ) + endif() + +endif() + +################################################################################ +# Dependencies +################################################################################ +add_dependencies(${PROJECT_NAME} + OTRExporter + libultraship +) + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + set(ADDITIONAL_LIBRARY_DEPENDENCIES + "-WHOLEARCHIVE:$/$" + "libultraship;" + storm + PNG::PNG + ) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + set(ADDITIONAL_LIBRARY_DEPENDENCIES + -Wl,-force_load $/$ + "libultraship;" + PNG::PNG + ${CMAKE_DL_LIBS} + Threads::Threads + ) +elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch") + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + set(ADDITIONAL_LIBRARY_DEPENDENCIES + -Wl,--whole-archive $/$ -Wl,--no-whole-archive + "libultraship;" + PNG::PNG + Threads::Threads + ) +elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS") + set(ADDITIONAL_LIBRARY_DEPENDENCIES + "libultraship;" + PNG::PNG + ) +else() + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + set(ADDITIONAL_LIBRARY_DEPENDENCIES + -Wl,--whole-archive $/$ -Wl,--no-whole-archive + "libultraship;" + PNG::PNG + ${CMAKE_DL_LIBS} + Threads::Threads + ) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS") +add_library(pathconf OBJECT pathconf.c) +target_link_libraries(${PROJECT_NAME} PUBLIC "${ADDITIONAL_LIBRARY_DEPENDENCIES}" $ ) +else() +target_link_libraries(${PROJECT_NAME} PUBLIC "${ADDITIONAL_LIBRARY_DEPENDENCIES}") +endif() + +if (GAME_STR STREQUAL "MM") + target_compile_definitions(${PROJECT_NAME} PRIVATE GAME_MM) +else() + target_compile_definitions(${PROJECT_NAME} PRIVATE GAME_OOT) +endif() diff --git a/ZAPDTR/ZAPD/CRC32.h b/ZAPDTR/ZAPD/CRC32.h new file mode 100644 index 000000000..1f82c75c5 --- /dev/null +++ b/ZAPDTR/ZAPD/CRC32.h @@ -0,0 +1,23 @@ +#pragma once + +static uint32_t CRC32B(const unsigned char* message, int32_t size) +{ + int32_t byte, crc; + int32_t mask; + + crc = 0xFFFFFFFF; + + for (int32_t i = 0; i < size; i++) + { + byte = message[i]; + crc = crc ^ byte; + + for (int32_t j = 7; j >= 0; j--) + { + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + } + + return ~(uint32_t)(crc); +} \ No newline at end of file diff --git a/ZAPDTR/ZAPD/CrashHandler.cpp b/ZAPDTR/ZAPD/CrashHandler.cpp new file mode 100644 index 000000000..6c0573359 --- /dev/null +++ b/ZAPDTR/ZAPD/CrashHandler.cpp @@ -0,0 +1,208 @@ +#include "CrashHandler.h" +#include "Utils/StringHelper.h" + +#if __has_include() +#define HAS_POSIX 1 +#else +#define HAS_POSIX 0 +#endif + +#include +#include +#include +#include +#include + +#if HAS_POSIX == 1 +#include +#include // for __cxa_demangle +#include // for dladdr +#include +#include +#elif defined(_MSC_VER) +#include +#include + +#include + +#pragma comment(lib, "Dbghelp.lib") +#endif + +// Feel free to add more crash messages. +static std::array crashEasterEgg = { + "\tYou've met with a terrible fate, haven't you?", + "\tSEA BEARS FOAM. SLEEP BEARS DREAMS. \n\tBOTH END IN THE SAME WAY: CRASSSH!", + "\tZAPD has fallen and cannot get up.", + "\tIT'S A SECRET TO EVERYBODY. \n\tBut it shouldn't be, you'd better ask about it!", + "\tI AM ERROR.", + "\tGRUMBLE,GRUMBLE...", + "\tDODONGO DISLIKES SMOKE \n\tAnd ZAPD dislikes whatever you fed it.", + "\tMay the way of the Hero lead \n\tto the debugger.", + "\tTHE WIND FISH SLUMBERS LONG... \n\tTHE HERO'S LIFE GONE... ", + "\tSEA BEARS FOAM, SLEEP BEARS DREAMS. \n\tBOTH END IN THE SAME WAY CRASSSH!", + "\tYou've met with a terrible fate, haven't you?", + "\tMaster, I calculate a 100% probability that ZAPD has crashed. \n\tAdditionally, the " + "batteries in your Wii Remote are nearly depleted.", + "\t CONGRATURATIONS! \n" + "\tAll Pages are displayed.\n" + "\t THANK YOU! \n" + "\t You are great debugger!", + "\tRCP is HUNG UP!!\n" + "\tOh! MY GOD!!", +}; + +#if HAS_POSIX == 1 +void ErrorHandler(int sig) +{ + std::array arr; + constexpr size_t nMaxFrames = arr.size(); + size_t size = backtrace(arr.data(), nMaxFrames); + char** symbols = backtrace_symbols(arr.data(), nMaxFrames); + + fprintf(stderr, "\nZAPD crashed. (Signal: %i)\n", sig); + + srand(time(nullptr)); + auto easterIndex = rand() % crashEasterEgg.size(); + + fprintf(stderr, "\n%s\n\n", crashEasterEgg[easterIndex]); + + fprintf(stderr, "Traceback:\n"); + for (size_t i = 1; i < size; i++) + { + Dl_info info; + uint32_t gotAddress = dladdr(arr[i], &info); + std::string functionName(symbols[i]); + + if (gotAddress != 0 && info.dli_sname != nullptr) + { + int32_t status; + char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); + const char* nameFound = info.dli_sname; + + if (status == 0) + { + nameFound = demangled; + } + + functionName = StringHelper::Sprintf("%s (+0x%X)", nameFound, + (char*)arr[i] - (char*)info.dli_saddr); + free(demangled); + } + + fprintf(stderr, "%-3zd %s\n", i, functionName.c_str()); + } + + fprintf(stderr, "\n"); + + free(symbols); + exit(1); +} +#elif defined(_MSC_VER) + +void printStack(CONTEXT* ctx) +{ + BOOL result; + HANDLE process; + HANDLE thread; + HMODULE hModule; + ULONG frame; + DWORD64 displacement; + DWORD disp; + + srand(time(nullptr)); + auto easterIndex = rand() % crashEasterEgg.size(); + + fprintf(stderr, "\n%s\n\n", crashEasterEgg[easterIndex]); + +#if defined(_M_AMD64) + STACKFRAME64 stack; + memset(&stack, 0, sizeof(STACKFRAME64)); +#else + STACKFRAME stack; + memset(&stack, 0, sizeof(STACKFRAME)); +#endif + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME + sizeof(TCHAR)]; + char module[512]; + + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + + CONTEXT ctx2; + memcpy(&ctx2, ctx, sizeof(CONTEXT)); + + process = GetCurrentProcess(); + thread = GetCurrentThread(); + SymInitialize(process, nullptr, TRUE); + + constexpr DWORD machineType = +#if defined(_M_AMD64) + IMAGE_FILE_MACHINE_AMD64; +#else + IMAGE_FILE_MACHINE_I386; +#endif + + displacement = 0; + + for (frame = 0;; frame++) + { + result = StackWalk(machineType, process, thread, &stack, &ctx2, nullptr, + SymFunctionTableAccess, SymGetModuleBase, nullptr); + if (!result) + { + break; + } + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, symbol); +#if defined(_M_AMD64) + IMAGEHLP_LINE64 line; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); +#else + IMAGEHLP_LINE line; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE); +#endif + if (SymGetLineFromAddr(process, stack.AddrPC.Offset, &disp, &line)) + { + fprintf(stderr, "%u\t %s in %s: line: %lu: \n", frame, symbol->Name, line.FileName, + line.LineNumber); + } + + else + { + fprintf(stderr, "%u\tat % s\n", frame, symbol->Name); + hModule = nullptr; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCTSTR)(stack.AddrPC.Offset), &hModule); + + if (hModule != nullptr) + { + GetModuleFileNameA(hModule, module, 512 - 1); + } + fprintf(stderr, "%u\tIn %s\n", frame, module); + } + } +} + +LONG seh_filter(_EXCEPTION_POINTERS* ex) +{ + fprintf(stderr, "EXCEPTION 0x%x occured\n", ex->ExceptionRecord->ExceptionCode); + printStack(ex->ContextRecord); + return EXCEPTION_EXECUTE_HANDLER; +} +#endif + +void CrashHandler_Init() +{ + #if 0 +#if HAS_POSIX == 1 + signal(SIGSEGV, ErrorHandler); + signal(SIGABRT, ErrorHandler); +#elif defined(_MSC_VER) + SetUnhandledExceptionFilter(seh_filter); +#else + HANDLE_WARNING(WarningType::Always, + "tried to set error handler, but this ZAPD build lacks support for one", ""); +#endif + #endif +} diff --git a/ZAPDTR/ZAPD/CrashHandler.h b/ZAPDTR/ZAPD/CrashHandler.h new file mode 100644 index 000000000..102778bec --- /dev/null +++ b/ZAPDTR/ZAPD/CrashHandler.h @@ -0,0 +1,6 @@ +#ifndef CRASH_HANDLER_H +#define CRASH_HANDLER_H + +void CrashHandler_Init(); + +#endif diff --git a/ZAPDTR/ZAPD/Declaration.cpp b/ZAPDTR/ZAPD/Declaration.cpp new file mode 100644 index 000000000..e4f91d89a --- /dev/null +++ b/ZAPDTR/ZAPD/Declaration.cpp @@ -0,0 +1,248 @@ +#include "Declaration.h" + +#include "Globals.h" +#include "ZVtx.h" +#include "Utils/StringHelper.h" + +Declaration::Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize, + const std::string& nBody) +{ + address = nAddress; + alignment = nAlignment; + size = nSize; + declBody = nBody; +} + +Declaration* Declaration::Create(offset_t declAddr, DeclarationAlignment declAlign, size_t declSize, + const std::string& declType, const std::string& declName, + const std::string& declBody) +{ + Declaration* decl = new Declaration(declAddr, declAlign, declSize, declBody); + + decl->declType = declType; + decl->declName = declName; + decl->declBody = declBody; + + return decl; +} + +Declaration* Declaration::CreateArray(offset_t declAddr, DeclarationAlignment declAlign, + size_t declSize, const std::string& declType, + const std::string& declName, const std::string& declBody, + size_t declArrayItemCnt, bool isDeclExternal) +{ + Declaration* decl = new Declaration(declAddr, declAlign, declSize, declBody); + + decl->declName = declName; + decl->declType = declType; + decl->arrayItemCnt = declArrayItemCnt; + decl->isExternal = isDeclExternal; + decl->isArray = true; + + return decl; +} + +Declaration* Declaration::CreateArray(offset_t declAddr, DeclarationAlignment declAlign, + size_t declSize, const std::string& declType, + const std::string& declName, const std::string& declBody, + const std::string& declArrayItemCntStr, bool isDeclExternal) +{ + Declaration* decl = new Declaration(declAddr, declAlign, declSize, declBody); + + decl->declName = declName; + decl->declType = declType; + decl->arrayItemCntStr = declArrayItemCntStr; + decl->isExternal = isDeclExternal; + decl->isArray = true; + + return decl; +} + +Declaration* Declaration::CreateInclude(offset_t declAddr, const std::string& includePath, + size_t declSize, const std::string& declType, + const std::string& declName, const std::string& defines) +{ + Declaration* decl = new Declaration(declAddr, DeclarationAlignment::Align4, declSize, ""); + decl->includePath = includePath; + decl->declType = declType; + decl->declName = declName; + decl->defines = defines; + + return decl; +} + +Declaration* Declaration::CreatePlaceholder(offset_t declAddr, const std::string& declName) +{ + Declaration* decl = new Declaration(declAddr, DeclarationAlignment::Align4, 0, ""); + decl->declName = declName; + decl->isPlaceholder = true; + + return decl; +} + +Declaration::~Declaration() +{ + //for (auto item : vertexHack) + //delete item; +} + +bool Declaration::IsStatic() const +{ + switch (staticConf) + { + case StaticConfig::Off: + return false; + + case StaticConfig::Global: + return Globals::Instance->forceStatic; + + case StaticConfig::On: + return true; + } + + return false; +} + +std::string Declaration::GetNormalDeclarationStr() const +{ + std::string output; + + if (IsStatic()) + { + output += "static "; + } + + if (isArray) + { + bool includeArraySize = (IsStatic() || forceArrayCnt); + + if (includeArraySize) + { + if (arrayItemCntStr != "") + output += StringHelper::Sprintf("%s %s[%s];\n", declType.c_str(), declName.c_str(), + arrayItemCntStr.c_str()); + else + output += StringHelper::Sprintf("%s %s[%i] = {\n", declType.c_str(), + declName.c_str(), arrayItemCnt); + } + else + { + output += StringHelper::Sprintf("%s %s[] = {\n", declType.c_str(), declName.c_str()); + } + + output += declBody + "\n"; + } + else + { + output += StringHelper::Sprintf("%s %s = { ", declType.c_str(), declName.c_str()); + output += declBody; + } + + if (output.back() == '\n') + output += "};"; + else + output += " };"; + + output += "\n"; + + output += "\n"; + + return output; +} + +std::string Declaration::GetExternalDeclarationStr() const +{ + std::string output; + + if (IsStatic()) + output += "static "; + + bool includeArraySize = (IsStatic() || forceArrayCnt); + + if (includeArraySize) + { + if (arrayItemCntStr != "") + output += StringHelper::Sprintf("%s %s[%s] = ", declType.c_str(), declName.c_str(), + arrayItemCntStr.c_str()); + else + output += StringHelper::Sprintf("%s %s[%i] = ", declType.c_str(), declName.c_str(), + arrayItemCnt); + } + else + { + output += StringHelper::Sprintf("%s %s[] = ", declType.c_str(), declName.c_str()); + } + + output += StringHelper::Sprintf("{\n#include \"%s\"\n};", includePath.c_str()); + output += "\n\n"; + + return output; +} + +std::string Declaration::GetExternStr() const +{ + if (IsStatic() || declType == "" || isUnaccounted) + { + return ""; + } + + if (Globals::Instance->otrMode) /* && (varType == "Gfx" || varType == "u64" || varType == "AnimationHeader" || varType == "LinkAnimationHeader" || + varType == "StandardLimb" || varType == "JointIndex" || varType == "Vtx" || varType == "FlexSkeletonHeader" || varType == "SkeletonHeader") || + varType == "CollisionHeader") */ + return ""; + + if (isArray) + { + if (arrayItemCntStr != "" && (IsStatic() || forceArrayCnt)) + { + return StringHelper::Sprintf("extern %s %s[%s];\n", declType.c_str(), declName.c_str(), + arrayItemCntStr.c_str()); + } + else if (arrayItemCnt != 0 && (IsStatic() || forceArrayCnt)) + { + return StringHelper::Sprintf("extern %s %s[%i];\n", declType.c_str(), declName.c_str(), + arrayItemCnt); + } + else + return StringHelper::Sprintf("extern %s %s[];\n", declType.c_str(), declName.c_str()); + } + + return StringHelper::Sprintf("extern %s %s;\n", declType.c_str(), declName.c_str()); +} + +std::string Declaration::GetDefinesStr() const +{ + if (IsStatic() || (declType == "")) + { + return ""; + } + return StringHelper::Sprintf("%s", defines.c_str()); +} + +std::string Declaration::GetStaticForwardDeclarationStr() const +{ + if (!IsStatic() || isUnaccounted) + return ""; + + if (isArray) + { + if (arrayItemCntStr == "" && arrayItemCnt == 0) + { + // Forward declaring static arrays without specifying the size is not allowed. + return ""; + } + + if (arrayItemCntStr != "") + { + return StringHelper::Sprintf("static %s %s[%s];\n", declType.c_str(), declName.c_str(), + arrayItemCntStr.c_str()); + } + else + { + return StringHelper::Sprintf("static %s %s[%i];\n", declType.c_str(), declName.c_str(), + arrayItemCnt); + } + } + + return StringHelper::Sprintf("static %s %s;\n", declType.c_str(), declName.c_str()); +} diff --git a/ZAPDTR/ZAPD/Declaration.h b/ZAPDTR/ZAPD/Declaration.h new file mode 100644 index 000000000..0a504e9f3 --- /dev/null +++ b/ZAPDTR/ZAPD/Declaration.h @@ -0,0 +1,188 @@ +#pragma once + +#include +#include +#include + +// TODO: should we drop the `_t` suffix because of UNIX compliance? +typedef uint32_t segptr_t; +typedef uint32_t offset_t; + +#define SEGMENTED_NULL ((segptr_t)0) + +enum class DeclarationAlignment +{ + Align4, + Align8 +}; + +enum class StaticConfig +{ + Off, + Global, + On +}; + +class ZVtx; + +/// +/// A declaration is contains the C contents of a symbol for a file. +/// It contains at a minimum the address where the symbol would be in the binary file, alignment +/// settings, the size of the binary data, and the C code that makes it up. Optionally it can also +/// contain comments. +/// +class Declaration +{ +public: + // Where in the binary file (segment) will this C code end up being? + offset_t address = 0; + + // How is this C code aligned? + DeclarationAlignment alignment = DeclarationAlignment::Align4; + + // How many bytes will this C code take up in the resulting binary when compiled? + size_t size = 0; + + // The C type of this declaration + std::string declType = ""; + + // The C variable name of this declaration + std::string declName = ""; + + // The body of the declaration containing the data. + // In "int j = 7;", "7" would be text. + std::string declBody = ""; + + // #define's to be included in the header + std::string defines = ""; + + std::string includePath = ""; + + std::vector vertexHack; + + // Is this declaration in an external file? (ie. a gameplay_keep reference being found in + // another file that wishes to use its data) + bool isExternal = false; + + bool isArray = false; + + // If true, will ensure that the arrays size is included in the declaration + bool forceArrayCnt = false; + + // If this declaration is an array, how many items make it up? + size_t arrayItemCnt = 0; + + // Overrides the brackets for the arrays size with a custom string + std::string arrayItemCntStr = ""; + + std::vector references; + + // If true, this declaration represents data inside the file which we do not understand it's + // purpose for. It will be outputted as just a byte array. + bool isUnaccounted = false; + + // Is this declaration a placeholder that will be replaced later? + bool isPlaceholder = false; + + // Does this declaration come straight from the XML? + // If false, this means that the declaration was created by ZAPD when it was parsing the + // resources. + bool declaredInXml = false; + + StaticConfig staticConf = StaticConfig::Global; + + /// + /// Creates a regular declaration. + /// + /// The address inside a binary file this declaration will be in when + /// compiled. The alignment of this declaration in the compiled + /// binary file. The size of this declaration when it is compiled + /// to binary data. The C variable type this declaration will be + /// declared as. The C variable name this declaration will be + /// declared as. The contents of the C variable + /// declaration. + static Declaration* Create(offset_t declAddr, DeclarationAlignment declAlign, size_t declSize, + const std::string& declType, const std::string& declName, + const std::string& declBody); + + /// + /// Creates an array declaration. + /// + /// The address inside a binary file this declaration will be in when + /// compiled. The alignment of this declaration in the compiled + /// binary file. The size of this declaration when it is compiled + /// to binary data. The C variable type this declaration will be + /// declared as. The C variable name this declaration will be + /// declared as. The contents of the C variable + /// declaration. The number of items in the + /// array. (Optional) Is this declaration from another + /// segment? + static Declaration* CreateArray(offset_t declAddr, DeclarationAlignment declAlign, + size_t declSize, const std::string& declType, + const std::string& declName, const std::string& declBody, + size_t declArrayItemCnt = 0, bool isDeclExternal = false); + + /// + /// Creates an array declaration who's size in the C code uses a custom string. + /// + /// The address inside a binary file this declaration will be in when + /// compiled. The alignment of this declaration in the compiled + /// binary file. The size of this declaration when it is compiled + /// to binary data. The C variable type this declaration will be + /// declared as. The C variable name this declaration will be + /// declared as. The contents of the C variable + /// declaration. The string to be put in the C array's + /// size inbetween the brackets. (Optional) Is this + /// declaration from another segment? + static Declaration* CreateArray(offset_t declAddr, DeclarationAlignment declAlign, + size_t declSize, const std::string& declType, + const std::string& declName, const std::string& declBody, + const std::string& declArrayItemCntStr, + bool isDeclExternal = false); + + /// + /// Creates a declaration who's body uses a #include to include another file + /// + /// The address inside a binary file this declaration will be in when + /// compiled. The path to the file this declaration will be + /// #including. The size of this declaration when it is compiled + /// to binary data. The C variable type this declaration will be + /// declared as. The C variable name this declaration will be + /// declared as. (Optional) Any #define's we want to have + /// outputted by this declaration. + static Declaration* CreateInclude(offset_t declAddr, const std::string& includePath, + size_t declSize, const std::string& declType, + const std::string& declName, const std::string& defines = ""); + + /// + /// Creates a placeholder declaration to be replaced later. + /// + /// The address inside a binary file this declaration will be in when + /// compiled. The C variable name this declaration will be + /// declared as. + static Declaration* CreatePlaceholder(offset_t declAddr, const std::string& declName); + + ~Declaration(); + + bool IsStatic() const; + + // Returns the declaration as C code as it would be in the code file when the body contains the + // needed data + std::string GetNormalDeclarationStr() const; + + // Returns the declaration as C code as it would be in the code file when the body #include's + // another file + std::string GetExternalDeclarationStr() const; + + // Generates the extern for this item to be placed in header files. + std::string GetExternStr() const; + + // Generates any #define's needed + std::string GetDefinesStr() const; + + std::string GetStaticForwardDeclarationStr() const; + +protected: + Declaration(offset_t nAddress, DeclarationAlignment nAlignment, size_t nSize, + const std::string& nBody); +}; diff --git a/ZAPDTR/ZAPD/ExecutableMain.cpp b/ZAPDTR/ZAPD/ExecutableMain.cpp new file mode 100644 index 000000000..102f2af30 --- /dev/null +++ b/ZAPDTR/ZAPD/ExecutableMain.cpp @@ -0,0 +1,9 @@ +#include +#include + +extern "C" int zapd_main(int argc, char* argv[]); +extern "C" int zapd_report(int argc, char* argv[], std::atomic* extractCount, std::atomic* totalExtract); + +int main(int argc, char* argv[]) { + return zapd_report(argc, argv, nullptr, nullptr); +} diff --git a/ZAPDTR/ZAPD/ExporterSet.h b/ZAPDTR/ZAPD/ExporterSet.h new file mode 100644 index 000000000..b86747840 --- /dev/null +++ b/ZAPDTR/ZAPD/ExporterSet.h @@ -0,0 +1,27 @@ +#pragma once + +typedef void (*ExporterSetFunc)(ZFile*); +typedef bool (*ExporterSetFuncBool)(ZFileMode fileMode); +typedef void (*ExporterSetFuncVoid)(int argc, char* argv[], int& i); +typedef void (*ExporterSetFuncVoid2)(const std::string& buildMode, ZFileMode& fileMode); +typedef void (*ExporterSetFuncVoid3)(); +typedef void (*ExporterSetFuncVoid4)(tinyxml2::XMLElement* reader); +typedef void (*ExporterSetResSave)(ZResource* res, BinaryWriter& writer); +//processCompilableFunc +class ExporterSet +{ +public: + ~ExporterSet(); + + std::map exporters; + ExporterSetFuncVoid parseArgsFunc = nullptr; + ExporterSetFuncVoid2 parseFileModeFunc = nullptr; + ExporterSetFuncBool processFileModeFunc = nullptr; + ExporterSetFunc beginFileFunc = nullptr; + ExporterSetFunc endFileFunc = nullptr; + ExporterSetFuncVoid3 beginXMLFunc = nullptr; + ExporterSetFuncVoid3 endXMLFunc = nullptr; + ExporterSetResSave resSaveFunc = nullptr; + ExporterSetFuncVoid3 endProgramFunc = nullptr; + ExporterSetFuncVoid4 processCompilableFunc = nullptr; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/FileWorker.cpp b/ZAPDTR/ZAPD/FileWorker.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/ZAPDTR/ZAPD/FileWorker.h b/ZAPDTR/ZAPD/FileWorker.h new file mode 100644 index 000000000..273e8b867 --- /dev/null +++ b/ZAPDTR/ZAPD/FileWorker.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include +#include "ZFile.h" + +class FileWorker +{ +public: + std::vector files; + std::vector externalFiles; + std::vector segments; + std::vector segmentFiles; + std::map> segmentRefFiles; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/GameConfig.cpp b/ZAPDTR/ZAPD/GameConfig.cpp new file mode 100644 index 000000000..e4c92081c --- /dev/null +++ b/ZAPDTR/ZAPD/GameConfig.cpp @@ -0,0 +1,301 @@ +#include "GameConfig.h" + +#include +#include +#include + +#include "Utils/Directory.h" +#include +#include "Utils/Path.h" +#include "ZFile.h" +#include "tinyxml2.h" + +using ConfigFunc = void (GameConfig::*)(const tinyxml2::XMLElement&); + +GameConfig::~GameConfig() +{ + for (auto& declPair : segmentRefFiles) + { + for (auto& file : declPair.second) + { + delete file; + } + } +} + +void GameConfig::ReadTexturePool(const fs::path& texturePoolXmlPath) +{ + tinyxml2::XMLDocument doc; + tinyxml2::XMLError eResult = doc.LoadFile(texturePoolXmlPath.string().c_str()); + + if (eResult != tinyxml2::XML_SUCCESS) + { + fprintf(stderr, "Warning: Unable to read texture pool XML with error code %i\n", eResult); + return; + } + + tinyxml2::XMLNode* root = doc.FirstChild(); + + if (root == nullptr) + return; + + for (tinyxml2::XMLElement* child = root->FirstChildElement(); child != nullptr; + child = child->NextSiblingElement()) + { + if (std::string_view(child->Name()) == "Texture") + { + std::string crcStr = child->Attribute("CRC"); + fs::path texPath = child->Attribute("Path"); + std::string texName; + + uint32_t crc = strtoul(crcStr.c_str(), nullptr, 16); + + texturePool[crc].path = texPath; + } + } +} + +void GameConfig::GenSymbolMap(const fs::path& symbolMapPath) +{ + auto symbolLines = DiskFile::ReadAllLines(symbolMapPath); + + for (std::string& symbolLine : symbolLines) + { + auto split = StringHelper::Split(symbolLine, " "); + uint32_t addr = strtoul(split[0].c_str(), nullptr, 16); + std::string symbolName = split[1]; + + symbolMap[addr] = std::move(symbolName); + } +} + +void GameConfig::ConfigFunc_SymbolMap(const tinyxml2::XMLElement& element) +{ + std::string fileName = element.Attribute("File"); + GenSymbolMap(Path::GetDirectoryName(configFilePath) / fileName); +} + +void GameConfig::ConfigFunc_ActorList(const tinyxml2::XMLElement& element) +{ + std::string fileName = element.Attribute("File"); + std::vector lines = + DiskFile::ReadAllLines(Path::GetDirectoryName(configFilePath) / fileName); + + for (auto& line : lines) + actorList.emplace_back(std::move(line)); +} + +void GameConfig::ConfigFunc_ObjectList(const tinyxml2::XMLElement& element) +{ + std::string fileName = element.Attribute("File"); + std::vector lines = + DiskFile::ReadAllLines(Path::GetDirectoryName(configFilePath) / fileName); + + for (auto& line : lines) + objectList.emplace_back(std::move(line)); +} + +void GameConfig::ConfigFunc_EntranceList(const tinyxml2::XMLElement& element) +{ + std::string fileName = element.Attribute("File"); + std::vector lines = + DiskFile::ReadAllLines(Path::GetDirectoryName(configFilePath) / fileName); + + for (auto& line : lines) + entranceList.emplace_back(std::move(line)); +} + +void GameConfig::ConfigFunc_specialEntranceList(const tinyxml2::XMLElement& element) +{ + std::string fileName = element.Attribute("File"); + std::vector lines = + DiskFile::ReadAllLines(Path::GetDirectoryName(configFilePath) / fileName); + + for (auto& line : lines) + specialEntranceList.emplace_back(std::move(line)); +} + +void GameConfig::ConfigFunc_TexturePool(const tinyxml2::XMLElement& element) +{ + std::string fileName = element.Attribute("File"); + ReadTexturePool(Path::GetDirectoryName(configFilePath) / fileName); +} + +void GameConfig::ConfigFunc_BGConfig(const tinyxml2::XMLElement& element) +{ + bgScreenWidth = element.IntAttribute("ScreenWidth", 320); + bgScreenHeight = element.IntAttribute("ScreenHeight", 240); + useScreenWidthHeightConstants = element.BoolAttribute("UseScreenWidthHeightConstants", true); +} + +void GameConfig::ConfigFunc_ExternalXMLFolder(const tinyxml2::XMLElement& element) +{ + const char* pathValue = element.Attribute("Path"); + if (pathValue == nullptr) + { + throw std::runtime_error( + StringHelper::Sprintf("Parse: Fatal error in configuration file.\n" + "\t Missing 'Path' attribute in `ExternalXMLFolder` element.\n")); + } + if (externalXmlFolder != "") + { + throw std::runtime_error(StringHelper::Sprintf("Parse: Fatal error in configuration file.\n" + "\t `ExternalXMLFolder` is duplicated.\n")); + } + externalXmlFolder = pathValue; +} + +void GameConfig::ConfigFunc_ExternalFile(const tinyxml2::XMLElement& element) +{ + const char* xmlPathValue = element.Attribute("XmlPath"); + if (xmlPathValue == nullptr) + { + throw std::runtime_error( + StringHelper::Sprintf("Parse: Fatal error in configuration file.\n" + "\t Missing 'XmlPath' attribute in `ExternalFile` element.\n")); + } + const char* outPathValue = element.Attribute("OutPath"); + if (outPathValue == nullptr) + { + throw std::runtime_error( + StringHelper::Sprintf("Parse: Fatal error in configuration file.\n" + "\t Missing 'OutPath' attribute in `ExternalFile` element.\n")); + } + + externalFiles.push_back(ExternalFile(fs::path(xmlPathValue), fs::path(outPathValue))); +} + +void GameConfig::ConfigFunc_EnumData(const tinyxml2::XMLElement& element) +{ + std::string path = Path::GetDirectoryName(configFilePath).string(); + path = path.append("/").append(element.Attribute("File")); + tinyxml2::XMLDocument doc; + tinyxml2::XMLError eResult = doc.LoadFile(path.c_str()); + + if (eResult != tinyxml2::XML_SUCCESS) + { + throw std::runtime_error("Error: Unable to read enum data."); + } + + tinyxml2::XMLNode* root = doc.FirstChild(); + + if (root == nullptr) + return; + + for (tinyxml2::XMLElement* csEnum = root->FirstChildElement(); csEnum != nullptr; + csEnum = csEnum->NextSiblingElement()) + { + for (tinyxml2::XMLElement* item = csEnum->FirstChildElement(); item != nullptr; + item = item->NextSiblingElement()) + { + std::string enumKey = csEnum->Attribute("Key"); + uint16_t itemIndex = atoi(item->Attribute("Index")); + const char* itemID = item->Attribute("ID"); + + // Common + if (enumKey == "cmd") + enumData.cutsceneCmd[itemIndex] = itemID; + + else if (enumKey == "miscType") + enumData.miscType[itemIndex] = itemID; + + else if (enumKey == "textType") + enumData.textType[itemIndex] = itemID; + + else if (enumKey == "fadeOutSeqPlayer") + enumData.fadeOutSeqPlayer[itemIndex] = itemID; + + else if (enumKey == "transitionType") + enumData.transitionType[itemIndex] = itemID; + + else if (enumKey == "destination") + enumData.destination[itemIndex] = itemID; + + else if (enumKey == "naviQuestHintType") + enumData.naviQuestHintType[itemIndex] = itemID; + + else if (enumKey == "ocarinaSongActionId") + enumData.ocarinaSongActionId[itemIndex] = itemID; + + else if (enumKey == "seqId") + enumData.seqId[itemIndex] = itemID; + + else if (enumKey == "playerCueId") + enumData.playerCueId[itemIndex] = itemID; + + // MM + else if (enumKey == "modifySeqType") + enumData.modifySeqType[itemIndex] = itemID; + + else if (enumKey == "chooseCreditsSceneType") + enumData.chooseCreditsSceneType[itemIndex] = itemID; + + else if (enumKey == "destinationType") + enumData.destinationType[itemIndex] = itemID; + + else if (enumKey == "motionBlurType") + enumData.motionBlurType[itemIndex] = itemID; + + else if (enumKey == "transitionGeneralType") + enumData.transitionGeneralType[itemIndex] = itemID; + + else if (enumKey == "rumbleType") + enumData.rumbleType[itemIndex] = itemID; + + else if (enumKey == "spawnFlag") + enumData.spawnFlag[itemIndex] = itemID; + + else if (enumKey == "endSfx") + enumData.endSfx[itemIndex] = itemID; + + else if (enumKey == "csSplineInterpType") + enumData.interpType[itemIndex] = itemID; + + else if (enumKey == "csSplineRelTo") + enumData.relTo[itemIndex] = itemID; + } + } +} + +void GameConfig::ReadConfigFile(const fs::path& argConfigFilePath) +{ + static const std::unordered_map ConfigFuncDictionary = { + {"SymbolMap", &GameConfig::ConfigFunc_SymbolMap}, + {"ActorList", &GameConfig::ConfigFunc_ActorList}, + {"ObjectList", &GameConfig::ConfigFunc_ObjectList}, + {"EntranceList", &GameConfig::ConfigFunc_EntranceList}, + {"SpecialEntranceList", &GameConfig::ConfigFunc_specialEntranceList}, + {"TexturePool", &GameConfig::ConfigFunc_TexturePool}, + {"BGConfig", &GameConfig::ConfigFunc_BGConfig}, + {"EnumData", &GameConfig::ConfigFunc_EnumData}, + {"ExternalXMLFolder", &GameConfig::ConfigFunc_ExternalXMLFolder}, + {"ExternalFile", &GameConfig::ConfigFunc_ExternalFile}, + }; + + configFilePath = argConfigFilePath.string(); + tinyxml2::XMLDocument doc; + tinyxml2::XMLError eResult = doc.LoadFile(configFilePath.c_str()); + + if (eResult != tinyxml2::XML_SUCCESS) + { + throw std::runtime_error("Error: Unable to read config file."); + } + + tinyxml2::XMLNode* root = doc.FirstChild(); + + if (root == nullptr) + return; + + for (tinyxml2::XMLElement* child = root->FirstChildElement(); child != nullptr; + child = child->NextSiblingElement()) + { + auto it = ConfigFuncDictionary.find(child->Name()); + if (it == ConfigFuncDictionary.end()) + { + fprintf(stderr, "Unsupported configuration variable: %s\n", child->Name()); + continue; + } + + std::invoke(it->second, *this, *child); + } +} diff --git a/ZAPDTR/ZAPD/GameConfig.h b/ZAPDTR/ZAPD/GameConfig.h new file mode 100644 index 000000000..4f3b91f8c --- /dev/null +++ b/ZAPDTR/ZAPD/GameConfig.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include +#include + +#include "Utils/Directory.h" +#include "tinyxml2.h" + +struct TexturePoolEntry +{ + fs::path path = ""; // Path to Shared Texture +}; + +class ExternalFile +{ +public: + fs::path xmlPath, outPath; + + ExternalFile(fs::path nXmlPath, fs::path nOutPath); +}; + +// Stores data from the XML file, the integer is the index (via ATOI) and the string is the value +class EnumData +{ +public: + // Common + std::map cutsceneCmd; + std::map miscType; + std::map fadeOutSeqPlayer; + std::map transitionType; + std::map naviQuestHintType; + std::map ocarinaSongActionId; + std::map seqId; + + // OoT + std::map textType; + std::map destination; + std::map playerCueId; + + // MM + std::map modifySeqType; + std::map chooseCreditsSceneType; + std::map destinationType; + std::map motionBlurType; + std::map transitionGeneralType; + std::map rumbleType; + std::map spawnFlag; + std::map endSfx; + std::map interpType; + std::map relTo; +}; + +class ZFile; + +class GameConfig +{ +public: + std::string configFilePath; + std::map> segmentRefFiles; + std::map symbolMap; + std::vector actorList; + std::vector objectList; + std::vector entranceList; + std::vector specialEntranceList; + std::map texturePool; // Key = CRC + EnumData enumData; + + // ZBackground + uint32_t bgScreenWidth = 320, bgScreenHeight = 240; + bool useScreenWidthHeightConstants = true; // If true, ZBackground's will be declared with + // SCREEN_WIDTH * SCREEN_HEIGHT in the C file + + // ExternalFile + fs::path externalXmlFolder; + std::vector externalFiles; + + GameConfig() = default; + ~GameConfig(); + + void ReadTexturePool(const fs::path& texturePoolXmlPath); + void GenSymbolMap(const fs::path& symbolMapPath); + + void ConfigFunc_SymbolMap(const tinyxml2::XMLElement& element); + void ConfigFunc_ActorList(const tinyxml2::XMLElement& element); + void ConfigFunc_ObjectList(const tinyxml2::XMLElement& element); + void ConfigFunc_EntranceList(const tinyxml2::XMLElement& element); + void ConfigFunc_specialEntranceList(const tinyxml2::XMLElement& element); + void ConfigFunc_TexturePool(const tinyxml2::XMLElement& element); + void ConfigFunc_BGConfig(const tinyxml2::XMLElement& element); + void ConfigFunc_ExternalXMLFolder(const tinyxml2::XMLElement& element); + void ConfigFunc_ExternalFile(const tinyxml2::XMLElement& element); + void ConfigFunc_EnumData(const tinyxml2::XMLElement& element); + + void ReadConfigFile(const fs::path& configFilePath); +}; diff --git a/ZAPDTR/ZAPD/Globals.cpp b/ZAPDTR/ZAPD/Globals.cpp new file mode 100644 index 000000000..7838d2fe2 --- /dev/null +++ b/ZAPDTR/ZAPD/Globals.cpp @@ -0,0 +1,363 @@ +#include "Globals.h" + +#include +#include + +#include +#include "Utils/Path.h" +#include "WarningHandler.h" +#include "tinyxml2.h" + +Globals* Globals::Instance; + +Globals::Globals() +{ + Instance = this; + + game = ZGame::OOT_RETAIL; + genSourceFile = true; + testMode = false; + profile = false; + useLegacyZDList = false; + useExternalResources = true; + singleThreaded = false; + verbosity = VerbosityLevel::VERBOSITY_SILENT; + outputPath = Directory::GetCurrentDirectory(); +} + +Globals::~Globals() +{ + for (const auto& w : workerData) + { + delete w.second; + } + + if (rom != nullptr) + { + delete rom; + } +} + +void Globals::AddSegment(int32_t segment, ZFile* file, int workerID) +{ + if (!Globals::Instance->singleThreaded) + { + auto worker = workerData[workerID]; + + if (std::find(worker->segments.begin(), worker->segments.end(), segment) == + worker->segments.end()) + { + worker->segments.push_back(segment); + worker->segmentFiles.push_back(file); + } + if (worker->segmentRefFiles.find(segment) == worker->segmentRefFiles.end()) + worker->segmentRefFiles[segment] = std::vector(); + + worker->segmentRefFiles[segment].push_back(file); + } + else + { + if (std::find(segments.begin(), segments.end(), segment) == segments.end()) + { + segments.push_back(segment); + segmentFiles.push_back(file); + } + if (cfg.segmentRefFiles.find(segment) == cfg.segmentRefFiles.end()) + cfg.segmentRefFiles[segment] = std::vector(); + + cfg.segmentRefFiles[segment].push_back(file); + } +} + +bool Globals::HasSegment(int32_t segment, int workerID) +{ + if (!Globals::Instance->singleThreaded) + return std::find(workerData[workerID]->segments.begin(), + workerData[workerID]->segments.end(), segment) != workerData[workerID]->segments.end(); + else + return std::find(segments.begin(), segments.end(), segment) != segments.end(); +} + +ZFile* Globals::GetSegment(int32_t segment, int workerID) +{ + if (!Globals::Instance->singleThreaded) + { + if (HasSegment(segment, workerID)) + { + int idx = std::find(workerData[workerID]->segments.begin(), + workerData[workerID]->segments.end(), segment) - + workerData[workerID]->segments.begin(); + return workerData[workerID]->segmentFiles[idx]; + } + else + return nullptr; + } + else + { + if (HasSegment(segment, workerID)) + { + int idx = std::find(segments.begin(), segments.end(), segment) - segments.begin(); + return segmentFiles[idx]; + } + else + return nullptr; + } +} + +std::map> Globals::GetSegmentRefFiles(int workerID) +{ + if (!Globals::Instance->singleThreaded) + return workerData[workerID]->segmentRefFiles; + else + return cfg.segmentRefFiles; +} + +void Globals::AddFile(ZFile* file, int workerID) +{ + if (singleThreaded) + files.push_back(file); + else + workerData[workerID]->files.push_back(file); +} + +void Globals::AddExternalFile(ZFile* file, int workerID) +{ + if (singleThreaded) + externalFiles.push_back(file); + else + workerData[workerID]->externalFiles.push_back(file); +} + +void Globals::BuildAssetTexture(const fs::path& pngFilePath, TextureType texType, const fs::path& outPath) +{ + std::string name = outPath.stem().string(); + + ZTexture tex(nullptr); + + if (name.find("u32") != std::string::npos) + tex.dWordAligned = false; + + tex.FromPNG(pngFilePath.string(), texType); + std::string cfgPath = StringHelper::Split(pngFilePath.string(), ".")[0] + ".cfg"; + + if (DiskFile::Exists(cfgPath)) + name = DiskFile::ReadAllText(cfgPath); + + std::string src = tex.GetBodySourceCode(); + + DiskFile::WriteAllBytes(outPath.string(), src.c_str(), src.size()); +} + +std::map& Globals::GetExporterMap() +{ + static std::map exporters; + return exporters; +} + +void Globals::AddExporter(std::string exporterName, ExporterSet* exporterSet) +{ + auto& exporters = GetExporterMap(); + exporters[exporterName] = exporterSet; +} + +ZResourceExporter* Globals::GetExporter(ZResourceType resType) +{ + auto& exporters = GetExporterMap(); + + if (currentExporter != "" && exporters[currentExporter]->exporters.find(resType) != + exporters[currentExporter]->exporters.end()) + return exporters[currentExporter]->exporters[resType]; + else + return nullptr; +} + +ExporterSet* Globals::GetExporterSet() +{ + auto& exporters = GetExporterMap(); + + if (currentExporter != "") + return exporters[currentExporter]; + else + return nullptr; +} + +std::vector Globals::GetBaseromFile(std::string fileName) +{ + if (fileMode == ZFileMode::ExtractDirectory) + { + if (StringHelper::Contains(fileName, "baserom/")) + fileName = StringHelper::Split(fileName, "baserom/")[1]; + + return rom->GetFile(fileName); + + } + else + return DiskFile::ReadAllBytes(fileName); +} + +bool Globals::GetSegmentedPtrName(segptr_t segAddress, ZFile* currentFile, + const std::string& expectedType, std::string& declName, + int workerID, bool warnIfNotFound) +{ + if (segAddress == SEGMENTED_NULL) + { + declName = "NULL"; + return true; + } + + uint8_t segment = GETSEGNUM(segAddress); + uint32_t offset = Seg2Filespace(segAddress, currentFile->baseAddress); + ZSymbol* sym; + + sym = currentFile->GetSymbolResource(offset); + if (sym != nullptr) + { + if (expectedType == "" || expectedType == sym->GetSourceTypeName()) + { + declName = sym->GetName(); + return true; + } + } + sym = currentFile->GetSymbolResource(segAddress); + if (sym != nullptr) + { + if (expectedType == "" || expectedType == sym->GetSourceTypeName()) + { + declName = sym->GetName(); + return true; + } + } + + if (currentFile->IsSegmentedInFilespaceRange(segAddress)) + { + if (currentFile->GetDeclarationPtrName(segAddress, expectedType, declName)) + return true; + } + else if (HasSegment(segment, workerID)) + { + // OTRTODO: Multithreading + auto segs = GetSegmentRefFiles(workerID); + for (auto file : segs[segment]) + { + offset = Seg2Filespace(segAddress, file->baseAddress); + + sym = file->GetSymbolResource(offset); + if (sym != nullptr) + { + if (expectedType == "" || expectedType == sym->GetSourceTypeName()) + { + declName = sym->GetName(); + return true; + } + } + sym = file->GetSymbolResource(segAddress); + if (sym != nullptr) + { + if (expectedType == "" || expectedType == sym->GetSourceTypeName()) + { + declName = sym->GetName(); + return true; + } + } + + if (file->IsSegmentedInFilespaceRange(segAddress)) + { + if (file->GetDeclarationPtrName(segAddress, expectedType, declName)) + return true; + } + } + } + + const auto& symbolFromMap = Globals::Instance->cfg.symbolMap.find(segAddress); + if (symbolFromMap != Globals::Instance->cfg.symbolMap.end()) + { + declName = "&" + symbolFromMap->second; + return true; + } + + declName = StringHelper::Sprintf("0x%08X", segAddress); + if (warnIfNotFound) + { + WarnHardcodedPointer(segAddress, currentFile, nullptr, -1); + } + return false; +} + +bool Globals::GetSegmentedArrayIndexedName(segptr_t segAddress, size_t elementSize, + ZFile* currentFile, const std::string& expectedType, + std::string& declName, int workerID, bool warnIfNotFound) +{ + if (segAddress == SEGMENTED_NULL) + { + declName = "NULL"; + return true; + } + + uint8_t segment = GETSEGNUM(segAddress); + + if (currentFile->IsSegmentedInFilespaceRange(segAddress)) + { + bool addressFound = currentFile->GetDeclarationArrayIndexedName(segAddress, elementSize, + expectedType, declName); + if (addressFound) + return true; + } + else if (HasSegment(segment, workerID)) + { + // OTRTODO: Multithreading + auto segs = GetSegmentRefFiles(workerID); + for (auto file : segs[segment]) + { + if (file->IsSegmentedInFilespaceRange(segAddress)) + { + bool addressFound = file->GetDeclarationArrayIndexedName(segAddress, elementSize, + expectedType, declName); + if (addressFound) + return true; + } + } + } + + declName = StringHelper::Sprintf("0x%08X", segAddress); + if (warnIfNotFound) + { + WarnHardcodedPointer(segAddress, currentFile, nullptr, -1); + } + return false; +} + +void Globals::WarnHardcodedPointer(segptr_t segAddress, ZFile* currentFile, ZResource* res, + offset_t currentOffset) +{ + uint8_t segment = GETSEGNUM(segAddress); + + if ((segment >= 2 && segment <= 6) || segment == 0x80) + { + std::string errorHeader = "A hardcoded pointer was found"; + std::string errorBody = StringHelper::Sprintf("Pointer: 0x%08X", segAddress); + + HANDLE_WARNING_RESOURCE(WarningType::HardcodedPointer, currentFile, res, currentOffset, + errorHeader, errorBody); + } + else + { + std::string errorHeader = "A general purpose hardcoded pointer was found"; + std::string errorBody = StringHelper::Sprintf("Pointer: 0x%08X", segAddress); + + HANDLE_WARNING_RESOURCE(WarningType::HardcodedGenericPointer, currentFile, res, + currentOffset, errorHeader, errorBody); + } +} + +ExternalFile::ExternalFile(fs::path nXmlPath, fs::path nOutPath) + : xmlPath{nXmlPath}, outPath{nOutPath} +{ +} + +ExporterSet::~ExporterSet() +{ + for (auto& it : exporters) + { + delete it.second; + } +} diff --git a/ZAPDTR/ZAPD/Globals.h b/ZAPDTR/ZAPD/Globals.h new file mode 100644 index 000000000..72e3793c0 --- /dev/null +++ b/ZAPDTR/ZAPD/Globals.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#include "GameConfig.h" +#include "ZFile.h" +#include "ZRom.h" +#include "FileWorker.h" +#include "ExporterSet.h" + +class ZRoom; + +enum class VerbosityLevel +{ + VERBOSITY_SILENT, + VERBOSITY_INFO, + VERBOSITY_DEBUG +}; + +enum class XMLModeShift : int +{ + SoundFont, + Sample, + Sequence, + // Add more as more assets support XML exporting. +}; + +class Globals +{ +public: + static Globals* Instance; + + bool genSourceFile; // Used for extraction + bool useExternalResources; + bool testMode; // Enables certain experimental features + bool outputCrc = false; + bool profile; // Measure performance of certain operations + bool useLegacyZDList; + bool singleThreaded; + VerbosityLevel verbosity; // ZAPD outputs additional information + ZFileMode fileMode = ZFileMode::Invalid; + fs::path baseRomPath, inputPath, outputPath, sourceOutputPath, cfgPath, fileListPath; + TextureType texType; + ZGame game; + GameConfig cfg; + bool verboseUnaccounted = false; + bool gccCompat = false; + bool forceStatic = false; + bool forceUnaccountedStatic = false; + bool otrMode = true; + bool buildRawTexture = false; + bool onlyGenCustomOtr = false; + uint32_t xmlExtractModes = 0; + + ZRom* rom = nullptr; + std::vector files; + std::vector externalFiles; + std::vector segments; + std::vector segmentFiles; + + std::map workerData; + + std::string currentExporter; + static std::map& GetExporterMap(); + static void AddExporter(std::string exporterName, ExporterSet* exporterSet); + + Globals(); + ~Globals(); + + void AddSegment(int32_t segment, ZFile* file, int workerID); + bool HasSegment(int32_t segment, int workerID); + ZFile* GetSegment(int32_t segment, int workerID); + std::map> GetSegmentRefFiles(int workerID); + void AddFile(ZFile* file, int workerID); + void AddExternalFile(ZFile* file, int workerID); + void BuildAssetTexture(const fs::path& pngFilePath, TextureType texType, const fs::path& outPath); + + ZResourceExporter* GetExporter(ZResourceType resType); + ExporterSet* GetExporterSet(); + + std::vector GetBaseromFile(std::string fileName); + + /** + * Search in every file (and the symbol map) for the `segAddress` passed as parameter. + * If the segment of `currentFile` is the same segment of `segAddress`, then that file will be + * used only, otherwise, the search will be performed in every other file. + * The name of that variable will be stored in the `declName` parameter. + * Returns `true` if the address is found. `false` otherwise, + * in which case `declName` will be set to the address formatted as a pointer. + */ + bool GetSegmentedPtrName(segptr_t segAddress, ZFile* currentFile, + const std::string& expectedType, std::string& declName, + int workerID, bool warnIfNotFound = true); + + bool GetSegmentedArrayIndexedName(segptr_t segAddress, size_t elementSize, ZFile* currentFile, + const std::string& expectedType, std::string& declName, + int workerID, bool warnIfNotFound = true); + + // TODO: consider moving to another place + void WarnHardcodedPointer(segptr_t segAddress, ZFile* currentFile, ZResource* res, + offset_t currentOffset); +}; diff --git a/ZAPDTR/ZAPD/ImageBackend.cpp b/ZAPDTR/ZAPD/ImageBackend.cpp new file mode 100644 index 000000000..7c7594762 --- /dev/null +++ b/ZAPDTR/ZAPD/ImageBackend.cpp @@ -0,0 +1,507 @@ +#define _CRT_SECURE_NO_WARNINGS +#include "ImageBackend.h" + +#include +#include +#include +#include + +#include "Utils/StringHelper.h" +#include "WarningHandler.h" + +/* ImageBackend */ + +ImageBackend::~ImageBackend() +{ + FreeImageData(); +} + +void ImageBackend::ReadPng(const char* filename) +{ + FreeImageData(); + + FILE* fp = fopen(filename, "rb"); + if (fp == nullptr) + { + std::string errorHeader = StringHelper::Sprintf("could not open file '%s'", filename); + HANDLE_ERROR(WarningType::InvalidPNG, errorHeader, ""); + } + + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (png == nullptr) + { + HANDLE_ERROR(WarningType::InvalidPNG, "could not create png struct", ""); + } + + png_infop info = png_create_info_struct(png); + if (info == nullptr) + { + HANDLE_ERROR(WarningType::InvalidPNG, "could not create png info", ""); + } + + if (setjmp(png_jmpbuf(png))) + { + // TODO: better warning explanation + HANDLE_ERROR(WarningType::InvalidPNG, "setjmp(png_jmpbuf(png))", ""); + } + + png_init_io(png, fp); + + png_read_info(png, info); + + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); + colorType = png_get_color_type(png, info); + bitDepth = png_get_bit_depth(png, info); + +#ifdef TEXTURE_DEBUG + printf("Width: %u\n", width); + printf("Height: %u\n", height); + printf("ColorType: "); + switch (colorType) + { + case PNG_COLOR_TYPE_RGBA: + printf("PNG_COLOR_TYPE_RGBA\n"); + break; + + case PNG_COLOR_TYPE_RGB: + printf("PNG_COLOR_TYPE_RGB\n"); + break; + + case PNG_COLOR_TYPE_PALETTE: + printf("PNG_COLOR_TYPE_PALETTE\n"); + break; + + default: + printf("%u\n", colorType); + break; + } + printf("BitDepth: %u\n", bitDepth); + printf("\n"); +#endif + + // Read any color_type into 8bit depth, RGBA format. + // See http://www.libpng.org/pub/png/libpng-manual.txt + + if (bitDepth == 16) + png_set_strip_16(png); + + if (colorType == PNG_COLOR_TYPE_PALETTE) + { + // png_set_palette_to_rgb(png); + isColorIndexed = true; + } + + // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. + if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) + png_set_expand_gray_1_2_4_to_8(png); + + /*if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png);*/ + + // These color_type don't have an alpha channel then fill it with 0xff. + /*if(*color_type == PNG_COLOR_TYPE_RGB || + *color_type == PNG_COLOR_TYPE_GRAY || + *color_type == PNG_COLOR_TYPE_PALETTE) + png_set_filler(png, 0xFF, PNG_FILLER_AFTER);*/ + + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + + png_read_update_info(png, info); + + size_t rowBytes = png_get_rowbytes(png, info); + pixelMatrix = (uint8_t**)malloc(sizeof(uint8_t*) * height); + for (size_t y = 0; y < height; y++) + { + pixelMatrix[y] = (uint8_t*)malloc(rowBytes); + } + + png_read_image(png, pixelMatrix); + +#ifdef TEXTURE_DEBUG + printf("rowBytes: %zu\n", rowBytes); + + size_t bytePerPixel = GetBytesPerPixel(); + printf("imgData\n"); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + for (size_t z = 0; z < bytePerPixel; z++) + { + printf("%02X ", pixelMatrix[y][x * bytePerPixel + z]); + } + printf(" "); + } + printf("\n"); + } + printf("\n"); +#endif + + fclose(fp); + + png_destroy_read_struct(&png, &info, nullptr); + + hasImageData = true; +} + +void ImageBackend::ReadPng(const fs::path& filename) +{ + ReadPng(filename.string().c_str()); +} + +void ImageBackend::WritePng(const char* filename) +{ + assert(hasImageData); + + FILE* fp = fopen(filename, "wb"); + if (fp == nullptr) + { + std::string errorHeader = + StringHelper::Sprintf("could not open file '%s' in write mode", filename); + HANDLE_ERROR(WarningType::InvalidPNG, errorHeader, ""); + } + + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (png == nullptr) + { + HANDLE_ERROR(WarningType::InvalidPNG, "could not create png struct", ""); + } + + png_infop info = png_create_info_struct(png); + if (info == nullptr) + { + HANDLE_ERROR(WarningType::InvalidPNG, "could not create png info", ""); + } + + if (setjmp(png_jmpbuf(png))) + { + // TODO: better warning description + HANDLE_ERROR(WarningType::InvalidPNG, "setjmp(png_jmpbuf(png))", ""); + } + + png_init_io(png, fp); + + png_set_IHDR(png, info, width, height, + bitDepth, // 8, + colorType, // PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + if (isColorIndexed) + { + png_set_PLTE(png, info, static_cast(colorPalette), paletteSize); + +#ifdef TEXTURE_DEBUG + printf("palette\n"); + png_color* aux = (png_color*)colorPalette; + for (size_t y = 0; y < paletteSize; y++) + { + printf("#%02X%02X%02X ", aux[y].red, aux[y].green, aux[y].blue); + if ((y + 1) % 8 == 0) + printf("\n"); + } + printf("\n"); +#endif + + png_set_tRNS(png, info, alphaPalette, paletteSize, nullptr); + } + + png_write_info(png, info); + + // To remove the alpha channel for PNG_COLOR_TYPE_RGB format, + // Use png_set_filler(). + // png_set_filler(png, 0, PNG_FILLER_AFTER); + +#ifdef TEXTURE_DEBUG + size_t bytePerPixel = GetBytesPerPixel(); + printf("imgData\n"); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width * bytePerPixel; x++) + { + printf("%02X ", pixelMatrix[y][x]); + } + printf("\n"); + } + printf("\n"); +#endif + + png_write_image(png, pixelMatrix); + png_write_end(png, nullptr); + + fclose(fp); + + png_destroy_write_struct(&png, &info); +} + +void ImageBackend::WritePng(const fs::path& filename) +{ + // Note: The .string() is necessary for MSVC, due to the implementation of std::filesystem + // differing from GCC. Do not remove! + WritePng(filename.string().c_str()); +} + +void ImageBackend::SetTextureData(const std::vector>& texData, + uint32_t nWidth, uint32_t nHeight, uint8_t nColorType, + uint8_t nBitDepth) +{ + FreeImageData(); + + width = nWidth; + height = nHeight; + colorType = nColorType; + bitDepth = nBitDepth; + + size_t bytePerPixel = GetBytesPerPixel(); + + pixelMatrix = static_cast(malloc(sizeof(uint8_t*) * height)); + for (size_t y = 0; y < height; y++) + { + pixelMatrix[y] = static_cast(malloc(sizeof(uint8_t*) * width * bytePerPixel)); + for (size_t x = 0; x < width; x++) + { + pixelMatrix[y][x * bytePerPixel + 0] = texData.at(y).at(x).r; + pixelMatrix[y][x * bytePerPixel + 1] = texData.at(y).at(x).g; + pixelMatrix[y][x * bytePerPixel + 2] = texData.at(y).at(x).b; + + if (colorType == PNG_COLOR_TYPE_RGBA) + pixelMatrix[y][x * bytePerPixel + 3] = texData.at(y).at(x).a; + } + } + hasImageData = true; +} + +void ImageBackend::InitEmptyRGBImage(uint32_t nWidth, uint32_t nHeight, bool alpha) +{ + FreeImageData(); + + width = nWidth; + height = nHeight; + colorType = PNG_COLOR_TYPE_RGB; + if (alpha) + colorType = PNG_COLOR_TYPE_RGBA; + bitDepth = 8; // nBitDepth; + + size_t bytePerPixel = GetBytesPerPixel(); + + pixelMatrix = static_cast(malloc(sizeof(uint8_t*) * height)); + for (size_t y = 0; y < height; y++) + { + pixelMatrix[y] = static_cast(calloc(width * bytePerPixel, sizeof(uint8_t*))); + } + + hasImageData = true; +} + +void ImageBackend::InitEmptyPaletteImage(uint32_t nWidth, uint32_t nHeight) +{ + FreeImageData(); + + width = nWidth; + height = nHeight; + colorType = PNG_COLOR_TYPE_PALETTE; + bitDepth = 8; + + size_t bytePerPixel = GetBytesPerPixel(); + + pixelMatrix = (uint8_t**)malloc(sizeof(uint8_t*) * height); + for (size_t y = 0; y < height; y++) + { + pixelMatrix[y] = static_cast(calloc(width * bytePerPixel, sizeof(uint8_t*))); + } + colorPalette = calloc(paletteSize, sizeof(png_color)); + alphaPalette = static_cast(calloc(paletteSize, sizeof(uint8_t))); + + hasImageData = true; + isColorIndexed = true; +} + +RGBAPixel ImageBackend::GetPixel(size_t y, size_t x) const +{ + assert(y < height); + assert(x < width); + assert(!isColorIndexed); + + RGBAPixel pixel; + size_t bytePerPixel = GetBytesPerPixel(); + pixel.r = pixelMatrix[y][x * bytePerPixel + 0]; + pixel.g = pixelMatrix[y][x * bytePerPixel + 1]; + pixel.b = pixelMatrix[y][x * bytePerPixel + 2]; + if (colorType == PNG_COLOR_TYPE_RGBA) + pixel.a = pixelMatrix[y][x * bytePerPixel + 3]; + return pixel; +} + +uint8_t ImageBackend::GetIndexedPixel(size_t y, size_t x) const +{ + assert(y < height); + assert(x < width); + assert(isColorIndexed); + + return pixelMatrix[y][x]; +} + +void ImageBackend::SetRGBPixel(size_t y, size_t x, uint8_t nR, uint8_t nG, uint8_t nB, uint8_t nA) +{ + assert(hasImageData); + assert(y < height); + assert(x < width); + + size_t bytePerPixel = GetBytesPerPixel(); + pixelMatrix[y][x * bytePerPixel + 0] = nR; + pixelMatrix[y][x * bytePerPixel + 1] = nG; + pixelMatrix[y][x * bytePerPixel + 2] = nB; + if (colorType == PNG_COLOR_TYPE_RGBA) + pixelMatrix[y][x * bytePerPixel + 3] = nA; +} + +void ImageBackend::SetGrayscalePixel(size_t y, size_t x, uint8_t grayscale, uint8_t alpha) +{ + assert(hasImageData); + assert(y < height); + assert(x < width); + + size_t bytePerPixel = GetBytesPerPixel(); + pixelMatrix[y][x * bytePerPixel + 0] = grayscale; + pixelMatrix[y][x * bytePerPixel + 1] = grayscale; + pixelMatrix[y][x * bytePerPixel + 2] = grayscale; + if (colorType == PNG_COLOR_TYPE_RGBA) + pixelMatrix[y][x * bytePerPixel + 3] = alpha; +} + +void ImageBackend::SetIndexedPixel(size_t y, size_t x, uint8_t index, uint8_t grayscale) +{ + assert(hasImageData); + assert(y < height); + assert(x < width); + + size_t bytePerPixel = GetBytesPerPixel(); + pixelMatrix[y][x * bytePerPixel + 0] = index; + + assert(index < paletteSize); + png_color* pal = static_cast(colorPalette); + pal[index].red = grayscale; + pal[index].green = grayscale; + pal[index].blue = grayscale; + alphaPalette[index] = 255; +} + +void ImageBackend::SetPaletteIndex(size_t index, uint8_t nR, uint8_t nG, uint8_t nB, uint8_t nA) +{ + assert(isColorIndexed); + assert(index < paletteSize); + + png_color* pal = static_cast(colorPalette); + pal[index].red = nR; + pal[index].green = nG; + pal[index].blue = nB; + alphaPalette[index] = nA; +} + +void ImageBackend::SetPalette(const ImageBackend& pal, uint32_t offset) +{ + assert(isColorIndexed); + size_t bytePerPixel = pal.GetBytesPerPixel(); + + for (size_t y = 0; y < pal.height; y++) + { + for (size_t x = 0; x < pal.width; x++) + { + size_t index = y * pal.width + x; + if (index >= paletteSize) + { + /* + * Some TLUTs are bigger than 256 colors. + * For those cases, we will only take the first 256 + * to colorize this CI texture. + */ + return; + } + + uint8_t r = pal.pixelMatrix[y][x * bytePerPixel + 0]; + uint8_t g = pal.pixelMatrix[y][x * bytePerPixel + 1]; + uint8_t b = pal.pixelMatrix[y][x * bytePerPixel + 2]; + uint8_t a = pal.pixelMatrix[y][x * bytePerPixel + 3]; + SetPaletteIndex(index + offset, r, g, b, a); + } + } +} + +uint32_t ImageBackend::GetWidth() const +{ + return width; +} + +uint32_t ImageBackend::GetHeight() const +{ + return height; +} + +uint8_t ImageBackend::GetColorType() const +{ + return colorType; +} + +uint8_t ImageBackend::GetBitDepth() const +{ + return bitDepth; +} + +double ImageBackend::GetBytesPerPixel() const +{ + switch (colorType) + { + case PNG_COLOR_TYPE_RGBA: + return 4 * bitDepth / 8; + + case PNG_COLOR_TYPE_RGB: + return 3 * bitDepth / 8; + + case PNG_COLOR_TYPE_PALETTE: + return 1 * bitDepth / 8; + + default: + HANDLE_ERROR(WarningType::InvalidPNG, "invalid color type", ""); + } +} + +void ImageBackend::FreeImageData() +{ + if (hasImageData) + { + for (size_t y = 0; y < height; y++) + free(pixelMatrix[y]); + free(pixelMatrix); + pixelMatrix = nullptr; + } + + if (isColorIndexed) + { + free(colorPalette); + free(alphaPalette); + colorPalette = nullptr; + alphaPalette = nullptr; + isColorIndexed = false; + } + + hasImageData = false; +} + +/* RGBAPixel */ + +void RGBAPixel::SetRGBA(uint8_t nR, uint8_t nG, uint8_t nB, uint8_t nA) +{ + r = nR; + g = nG; + b = nB; + a = nA; +} + +void RGBAPixel::SetGrayscale(uint8_t grayscale, uint8_t alpha) +{ + r = grayscale; + g = grayscale; + b = grayscale; + a = alpha; +} diff --git a/ZAPDTR/ZAPD/ImageBackend.h b/ZAPDTR/ZAPD/ImageBackend.h new file mode 100644 index 000000000..0b1f4806c --- /dev/null +++ b/ZAPDTR/ZAPD/ImageBackend.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include "Utils/Directory.h" + +class RGBAPixel +{ +public: + RGBAPixel() = default; + + void SetRGBA(uint8_t nR, uint8_t nG, uint8_t nB, uint8_t nA); + void SetGrayscale(uint8_t grayscale, uint8_t alpha = 0); + + uint8_t r = 0; + uint8_t g = 0; + uint8_t b = 0; + uint8_t a = 0; +}; + +class ImageBackend +{ +public: + ImageBackend() = default; + ~ImageBackend(); + + void ReadPng(const char* filename); + void ReadPng(const fs::path& filename); + void WritePng(const char* filename); + void WritePng(const fs::path& filename); + + void SetTextureData(const std::vector>& texData, uint32_t nWidth, + uint32_t nHeight, uint8_t nColorType, uint8_t nBitDepth); + void InitEmptyRGBImage(uint32_t nWidth, uint32_t nHeight, bool alpha); + void InitEmptyPaletteImage(uint32_t nWidth, uint32_t nHeight); + + RGBAPixel GetPixel(size_t y, size_t x) const; + uint8_t GetIndexedPixel(size_t y, size_t x) const; + + void SetRGBPixel(size_t y, size_t x, uint8_t nR, uint8_t nG, uint8_t nB, uint8_t nA = 0); + void SetGrayscalePixel(size_t y, size_t x, uint8_t grayscale, uint8_t alpha = 0); + + void SetIndexedPixel(size_t y, size_t x, uint8_t index, uint8_t grayscale); + void SetIndexedPixel(size_t y, size_t x, uint8_t index, RGBAPixel pixel); + void SetPaletteIndex(size_t index, uint8_t nR, uint8_t nG, uint8_t nB, uint8_t nA); + void SetPalette(const ImageBackend& pal, uint32_t offset = 0); + + uint32_t GetWidth() const; + uint32_t GetHeight() const; + uint8_t GetColorType() const; + uint8_t GetBitDepth() const; + +protected: + uint8_t** pixelMatrix = nullptr; // height * [width * bytePerPixel] + + void* colorPalette = nullptr; + uint8_t* alphaPalette = nullptr; + size_t paletteSize = 16 * 16; + + uint32_t width = 0; + uint32_t height = 0; + uint8_t colorType = 0; + uint8_t bitDepth = 0; + + bool hasImageData = false; + bool isColorIndexed = false; + + double GetBytesPerPixel() const; + + void FreeImageData(); +}; diff --git a/ZAPDTR/ZAPD/Main.cpp b/ZAPDTR/ZAPD/Main.cpp new file mode 100644 index 000000000..c5f146e32 --- /dev/null +++ b/ZAPDTR/ZAPD/Main.cpp @@ -0,0 +1,756 @@ +#include "Globals.h" +#include +#include +#include +#include +#include "WarningHandler.h" + +// Linker Hacks Begin +#include "ZAnimation.h" +ZNormalAnimation nAnim(nullptr); +ZCurveAnimation cAnim(nullptr); +ZLinkAnimation lAnim(nullptr); +ZLegacyAnimation lAnim2(nullptr); + +#include "ZArray.h" +ZArray arr(nullptr); + +#include "ZAudio.h" +ZAudio audio(nullptr); + +#include "ZBackground.h" +ZBackground back(nullptr); + +#include "ZBlob.h" +ZBlob blob(nullptr); + +#include "ZCollision.h" +ZCollisionHeader colHeader(nullptr); + +#include "ZCutscene.h" +ZCutscene cs(nullptr); + +#include "ZLimb.h" +ZLimb limb(nullptr); + +#include "ZMtx.h" +ZMtx mtx(nullptr); + +#include "ZPath.h" +ZPath path(nullptr); + +#include "ZPlayerAnimationData.h" +ZPlayerAnimationData pAnimData(nullptr); + +#include "ZScalar.h" +ZScalar scalar(nullptr); + +#include "ZSkeleton.h" +ZLimbTable limbTbl(nullptr); +ZSkeleton skel(nullptr); + +#include "ZString.h" +ZString str(nullptr); + +#include "ZSymbol.h" +ZSymbol sym(nullptr); + +#include "ZText.h" +ZText txt(nullptr); + +#include "ZTextMM.h" +ZTextMM txtMM(nullptr); + +#include "ZTexture.h" +ZTexture tex(nullptr); + +#include "ZVector.h" +ZVector vec(nullptr); + +#include "ZVtx.h" +ZVtx vtx(nullptr); + +#include "ZRoom/ZRoom.h" +ZRoom room(nullptr); + +#include "ZPointer.h" +ZPointer pointer(nullptr); + +#include "ZCKeyFrame.h" +ZKeyFrameSkel kfSKel(nullptr); + +#include "ZCkeyFrameAnim.h" +ZKeyFrameAnim kfAnim(nullptr); + +// Linker Hacks End + +#include "ZFile.h" +#include "ZTexture.h" + +#include +#include "CrashHandler.h" + +#include +#include +#include "tinyxml2.h" +#include + +const char gBuildHash[] = ""; + +using ArgFunc = void (*)(int&, char**); + +void Arg_SetOutputPath(int& i, char* argv[]); +void Arg_SetInputPath(int& i, char* argv[]); +void Arg_SetBaseromPath(int& i, char* argv[]); +void Arg_SetSourceOutputPath(int& i, char* argv[]); +void Arg_GenerateSourceFile(int& i, char* argv[]); +void Arg_TestMode(int& i, char* argv[]); +void Arg_LegacyDList(int& i, char* argv[]); +void Arg_EnableProfiling(int& i, char* argv[]); +void Arg_UseExternalResources(int& i, char* argv[]); +void Arg_SetTextureType(int& i, char* argv[]); +void Arg_ReadConfigFile(int& i, char* argv[]); +void Arg_EnableErrorHandler(int& i, char* argv[]); +void Arg_SetVerbosity(int& i, char* argv[]); +void Arg_VerboseUnaccounted(int& i, char* argv[]); +void Arg_SetExporter(int& i, char* argv[]); +void Arg_EnableGCCCompat(int& i, char* argv[]); +void Arg_ForceStatic(int& i, char* argv[]); +void Arg_ForceUnaccountedStatic(int& i, char* argv[]); +void Arg_SetFileListPath(int& i, char* argv[]); +void Arg_SetBuildRawTexture(int& i, char* argv[]); +void Arg_SetNoRomMode(int& i, char* argv[]); +void Arg_SetXMLMode(int& i, char* argv[]); + +int main(int argc, char* argv[]); + +bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path& outPath, + ZFileMode fileMode, int workerID); + +void ParseArgs(int& argc, char* argv[]); + +void BuildAssetTexture(const fs::path& pngFilePath, TextureType texType, const fs::path& outPath); +void BuildAssetBackground(const fs::path& imageFilePath, const fs::path& outPath); +void BuildAssetBlob(const fs::path& blobFilePath, const fs::path& outPath); +ZFileMode ParseFileMode(const std::string& buildMode, ExporterSet* exporterSet); +int HandleExtract(ZFileMode fileMode, ExporterSet* exporterSet, std::atomic* extractCount = nullptr, std::atomic* totalExtract = nullptr); +int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileMode fileMode); + +std::atomic numWorkersLeft = 0; + +extern const char gBuildHash[]; + +extern void ImportExporters(); + +extern "C" int zapd_report(int argc, char* argv[], std::atomic* extractCount, std::atomic* totalExtract) +{ + int returnCode = 0; + + if (argc < 2) + { + printf("ZAPD.out (%s) [mode (btex/bovl/bsf/bblb/bmdlintr/bamnintr/e)] ...\n", gBuildHash); + return 1; + } + + Globals* g = new Globals(); + WarningHandler::Init(argc, argv); + + for (int i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "--version")) + { + printf("ZAPD.out %s\n", gBuildHash); + return 0; + } + else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) + { + printf("Congratulations!\n"); + printf("You just found the (unimplemented and undocumented) ZAPD's help message.\n"); + printf("Feel free to implement it if you want :D\n"); + + WarningHandler::PrintHelp(); + return 0; + } + } + + ParseArgs(argc, argv); + + // Parse File Mode + ExporterSet* exporterSet = Globals::Instance->GetExporterSet(); + + // We've parsed through our commands once. If an exporter exists, it's been set by now. + // Now we'll parse through them again but pass them on to our exporter if one is available. + if (exporterSet != nullptr && exporterSet->parseArgsFunc != nullptr) { + for (int32_t i = 2; i < argc; i++) { + exporterSet->parseArgsFunc(argc, argv, i); + } + } + + if (Globals::Instance->onlyGenCustomOtr) { + if (exporterSet != nullptr) { + exporterSet->endProgramFunc(); + } else { + printf("Error: No exporter set, unable to make custom otr.\n"); + } + + delete g; + return 0; + } + + std::string buildMode = argv[1]; + ZFileMode fileMode = ParseFileMode(buildMode, exporterSet); + + if (fileMode == ZFileMode::Invalid) + { + printf("Error: Invalid file mode '%s'\n", buildMode.c_str()); + return 1; + } + + Globals::Instance->fileMode = fileMode; + + if (fileMode == ZFileMode::ExtractDirectory) + Globals::Instance->rom = new ZRom(Globals::Instance->baseRomPath.string()); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + printf("ZAPD: Zelda Asset Processor For Decomp: %s\n", gBuildHash); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + WarningHandler::PrintWarningsDebugInfo(); + + if (fileMode == ZFileMode::Extract || fileMode == ZFileMode::BuildSourceFile || fileMode == ZFileMode::ExtractDirectory) + returnCode = HandleExtract(fileMode, exporterSet, extractCount, totalExtract); + else if (fileMode == ZFileMode::BuildTexture) + BuildAssetTexture(Globals::Instance->inputPath, Globals::Instance->texType, + Globals::Instance->outputPath); + else if (fileMode == ZFileMode::BuildBackground) + BuildAssetBackground(Globals::Instance->inputPath, Globals::Instance->outputPath); + else if (fileMode == ZFileMode::BuildBlob) + BuildAssetBlob(Globals::Instance->inputPath, Globals::Instance->outputPath); + + if (exporterSet != nullptr && exporterSet->endProgramFunc != nullptr) + exporterSet->endProgramFunc(); + + delete g; + return returnCode; +} + +extern "C" int zapd_main(int argc, char* argv[]) { + return zapd_report(argc, argv, nullptr, nullptr); +} + +int ExtractFunc(int workerID, int fileListSize, std::string fileListItem, ZFileMode fileMode) +{ + bool parseSuccessful; + + printf("(%i / %i): %s\n", (workerID + 1), fileListSize, fileListItem.c_str()); + + for (auto& extFile : Globals::Instance->cfg.externalFiles) + { + fs::path externalXmlFilePath = Globals::Instance->cfg.externalXmlFolder / extFile.xmlPath; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + { + printf("Parsing external file from config: '%s'\n", externalXmlFilePath.c_str()); + } + + parseSuccessful = Parse(externalXmlFilePath, Globals::Instance->baseRomPath, + extFile.outPath, ZFileMode::ExternalFile, workerID); + + if (!parseSuccessful) + return 1; + } + + parseSuccessful = Parse(fileListItem, Globals::Instance->baseRomPath, + Globals::Instance->outputPath, fileMode, workerID); + + if (!parseSuccessful) + return 1; + + if (Globals::Instance->singleThreaded) + { + for (int i = 0; i < Globals::Instance->files.size(); i++) + { + delete Globals::Instance->files[i]; + Globals::Instance->files.erase(Globals::Instance->files.begin() + i); + i--; + } + + Globals::Instance->externalFiles.clear(); + Globals::Instance->segments.clear(); + Globals::Instance->segmentFiles.clear(); + Globals::Instance->cfg.segmentRefFiles.clear(); + } + else + { + for (int i = 0; i < Globals::Instance->workerData[workerID]->files.size(); i++) + { + delete Globals::Instance->workerData[workerID]->files[i]; + Globals::Instance->workerData[workerID]->files.erase( + Globals::Instance->workerData[workerID]->files.begin() + + i); + i--; + } + + Globals::Instance->workerData[workerID]->externalFiles.clear(); + Globals::Instance->workerData[workerID]->segments.clear(); + Globals::Instance->workerData[workerID]->segmentFiles.clear(); + Globals::Instance->workerData[workerID]->segmentRefFiles.clear(); + + numWorkersLeft--; + } + return 0; +} + +bool Parse(const fs::path& xmlFilePath, const fs::path& basePath, const fs::path& outPath, + ZFileMode fileMode, int workerID) +{ + tinyxml2::XMLDocument doc; + tinyxml2::XMLError eResult = doc.LoadFile(xmlFilePath.string().c_str()); + + if (eResult != tinyxml2::XML_SUCCESS) + { + // TODO: use XMLDocument::ErrorIDToName to get more specific error messages here + HANDLE_ERROR(WarningType::InvalidXML, + StringHelper::Sprintf("invalid XML file: '%s'", xmlFilePath.c_str()), ""); + return false; + } + + tinyxml2::XMLNode* root = doc.FirstChild(); + + if (root == nullptr) + { + HANDLE_WARNING( + WarningType::InvalidXML, + StringHelper::Sprintf("missing Root tag in xml file: '%s'", xmlFilePath.c_str()), ""); + return false; + } + + for (tinyxml2::XMLElement* child = root->FirstChildElement(); child != NULL; + child = child->NextSiblingElement()) + { + if (std::string_view(child->Name()) == "File") + { + ZFile* file = new ZFile(fileMode, child, basePath, outPath, "", xmlFilePath, workerID); + Globals::Instance->AddFile(file, workerID); + if (fileMode == ZFileMode::ExternalFile) + { + Globals::Instance->AddExternalFile(file, workerID); + file->isExternalFile = true; + } + } + else if (std::string(child->Name()) == "ExternalFile") + { + const char* xmlPathValue = child->Attribute("XmlPath"); + if (xmlPathValue == nullptr) + { + throw std::runtime_error(StringHelper::Sprintf( + "Parse: Fatal error in '%s'.\n" + "\t Missing 'XmlPath' attribute in `ExternalFile` element.\n", + xmlFilePath.c_str())); + } + const char* outPathValue = child->Attribute("OutPath"); + if (outPathValue == nullptr) + { + throw std::runtime_error(StringHelper::Sprintf( + "Parse: Fatal error in '%s'.\n" + "\t Missing 'OutPath' attribute in `ExternalFile` element.\n", + xmlFilePath.c_str())); + } + + fs::path externalXmlFilePath = + Globals::Instance->cfg.externalXmlFolder / fs::path(xmlPathValue); + fs::path externalOutFilePath = fs::path(outPathValue); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + { + printf("Parsing external file: '%s'\n", externalXmlFilePath.c_str()); + } + + // Recursion. What can go wrong? + Parse(externalXmlFilePath, basePath, externalOutFilePath, ZFileMode::ExternalFile, workerID); + } + else + { + std::string errorHeader = + StringHelper::Sprintf("when parsing file '%s'", xmlFilePath.c_str()); + std::string errorBody = StringHelper::Sprintf( + "Found a resource outside a File element: '%s'", child->Name()); + HANDLE_ERROR(WarningType::InvalidXML, errorHeader, errorBody); + } + } + + if (fileMode != ZFileMode::ExternalFile) + { + ExporterSet* exporterSet = Globals::Instance->GetExporterSet(); + + if (exporterSet != nullptr && exporterSet->beginXMLFunc != nullptr) + exporterSet->beginXMLFunc(); + + std::vector files; + + if (Globals::Instance->singleThreaded) + files = Globals::Instance->files; + else + files = Globals::Instance->workerData[workerID]->files; + + for (ZFile* file : files) + { + if (fileMode == ZFileMode::BuildSourceFile) + file->BuildSourceFile(); + else + file->ExtractResources(); + } + + if (exporterSet != nullptr && exporterSet->endXMLFunc != nullptr) + exporterSet->endXMLFunc(); + } + + return true; +} + +void ParseArgs(int& argc, char* argv[]) +{ + static const std::unordered_map ArgFuncDictionary = { + {"-o", &Arg_SetOutputPath}, + {"--outputpath", &Arg_SetOutputPath}, + {"-i", &Arg_SetInputPath}, + {"--inputpath", &Arg_SetInputPath}, + {"-b", &Arg_SetBaseromPath}, + {"--baserompath", &Arg_SetBaseromPath}, + {"-osf", &Arg_SetSourceOutputPath}, + {"-gsf", &Arg_GenerateSourceFile}, + {"-tm", &Arg_TestMode}, + {"-ulzdl", &Arg_LegacyDList}, + {"-profile", &Arg_EnableProfiling}, + {"-uer", &Arg_UseExternalResources}, + {"-tt", &Arg_SetTextureType}, + {"-rconf", &Arg_ReadConfigFile}, + {"-eh", &Arg_EnableErrorHandler}, + {"-v", &Arg_SetVerbosity}, + {"-vu", &Arg_VerboseUnaccounted}, + {"--verbose-unaccounted", &Arg_VerboseUnaccounted}, + {"-se", &Arg_SetExporter}, + {"--set-exporter", &Arg_SetExporter}, + {"--gcc-compat", &Arg_EnableGCCCompat}, + {"-s", &Arg_ForceStatic}, + {"--static", &Arg_ForceStatic}, + {"-us", &Arg_ForceUnaccountedStatic}, + {"--unaccounted-static", &Arg_ForceUnaccountedStatic}, + {"-fl", &Arg_SetFileListPath}, + {"-brt", &Arg_SetBuildRawTexture}, + {"--norom", &Arg_SetNoRomMode}, + {"-oxml", &Arg_SetXMLMode}, + }; + + for (int32_t i = 2; i < argc; i++) + { + std::string arg = argv[i]; + + // Ignore warning args as they have already been parsed + if (arg.length() > 2 && arg[0] == '-' && arg[1] == 'W' && arg[2] != '\0') + { + continue; + } + + auto it = ArgFuncDictionary.find(arg); + if (it == ArgFuncDictionary.end()) + { + fprintf(stderr, "Unsupported argument: %s\n", arg.c_str()); + ExporterSet* exporterSet = Globals::Instance->GetExporterSet(); + if (exporterSet != nullptr) + exporterSet->parseArgsFunc(argc, argv, i); + continue; + } + + std::invoke(it->second, i, argv); + } +} + +ZFileMode ParseFileMode(const std::string& buildMode, ExporterSet* exporterSet) +{ + ZFileMode fileMode = ZFileMode::Invalid; + + if (buildMode == "btex") + fileMode = ZFileMode::BuildTexture; + else if (buildMode == "bren") + fileMode = ZFileMode::BuildBackground; + else if (buildMode == "bsf") + fileMode = ZFileMode::BuildSourceFile; + else if (buildMode == "bblb") + fileMode = ZFileMode::BuildBlob; + else if (buildMode == "e") + fileMode = ZFileMode::Extract; + else if (buildMode == "ed") + fileMode = ZFileMode::ExtractDirectory; + else if (exporterSet != nullptr && exporterSet->parseFileModeFunc != nullptr) + exporterSet->parseFileModeFunc(buildMode, fileMode); + + return fileMode; +} + +void Arg_SetOutputPath(int& i, [[maybe_unused]] char* argv[]) +{ + Globals::Instance->outputPath = argv[++i]; + + if (Globals::Instance->sourceOutputPath == "") + Globals::Instance->sourceOutputPath = Globals::Instance->outputPath; +} + +void Arg_SetInputPath(int& i, char* argv[]) +{ + Globals::Instance->inputPath = argv[++i]; +} + +void Arg_SetBaseromPath(int& i, char* argv[]) +{ + Globals::Instance->baseRomPath = argv[++i]; +} + +void Arg_SetSourceOutputPath(int& i, char* argv[]) +{ + Globals::Instance->sourceOutputPath = argv[++i]; +} + +void Arg_GenerateSourceFile(int& i, char* argv[]) +{ + // Generate source file during extraction + Globals::Instance->genSourceFile = std::string_view(argv[++i]) == "1"; +} + +void Arg_TestMode(int& i, char* argv[]) +{ + // Test Mode (enables certain experimental features) + Globals::Instance->testMode = std::string_view(argv[++i]) == "1"; +} + +void Arg_LegacyDList(int& i, char* argv[]) +{ + Globals::Instance->useLegacyZDList = std::string_view(argv[++i]) == "1"; +} + +void Arg_EnableProfiling(int& i, char* argv[]) +{ + Globals::Instance->profile = std::string_view(argv[++i]) == "1"; +} + +void Arg_UseExternalResources(int& i, char* argv[]) +{ + // Split resources into their individual components(enabled by default) + // TODO: We may wish to make this a part of the config file... + Globals::Instance->useExternalResources = std::string_view(argv[++i]) == "1"; +} + +void Arg_SetTextureType(int& i, char* argv[]) +{ + Globals::Instance->texType = ZTexture::GetTextureTypeFromString(argv[++i]); +} + +void Arg_ReadConfigFile(int& i, char* argv[]) +{ + Globals::Instance->cfg.ReadConfigFile(argv[++i]); +} + +void Arg_EnableErrorHandler([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) +{ + CrashHandler_Init(); +} + +void Arg_SetVerbosity(int& i, char* argv[]) +{ + Globals::Instance->verbosity = static_cast(strtol(argv[++i], NULL, 16)); +} + +void Arg_VerboseUnaccounted([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) +{ + Globals::Instance->verboseUnaccounted = true; +} + +void Arg_SetExporter(int& i, char* argv[]) +{ + ImportExporters(); + Globals::Instance->currentExporter = argv[++i]; +} + +void Arg_EnableGCCCompat([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) +{ + Globals::Instance->gccCompat = true; +} + +void Arg_ForceStatic([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) +{ + Globals::Instance->forceStatic = true; +} + +void Arg_ForceUnaccountedStatic([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) +{ + Globals::Instance->forceUnaccountedStatic = true; +} + +void Arg_SetFileListPath(int& i, char* argv[]) +{ + Globals::Instance->fileListPath = argv[++i]; +} + +void Arg_SetBuildRawTexture([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) +{ + Globals::Instance->buildRawTexture = true; +} + +void Arg_SetNoRomMode([[maybe_unused]] int& i, [[maybe_unused]] char* argv[]) +{ + Globals::Instance->onlyGenCustomOtr = true; +} + +void Arg_SetXMLMode(int& i, char* argv[]) +{ + char* xmlList = argv[++i]; + size_t xmlListPos = 0; + size_t xmlListLen = strlen(xmlList); + + while (xmlListPos < xmlListLen) + { + char* nextDelimiterPtr = strchr(&xmlList[xmlListPos], ','); + if (nextDelimiterPtr == nullptr) + nextDelimiterPtr = strchr(&xmlList[xmlListPos], 0); + + if (nextDelimiterPtr == nullptr) + return; + + size_t curModeLen = nextDelimiterPtr - &xmlList[xmlListPos]; + + // Audio Sound Font (asf) + if (strncmp(&xmlList[xmlListPos], "asf", curModeLen) == 0) + Globals::Instance->xmlExtractModes |= 1 << (int)XMLModeShift::SoundFont; + // Audio Sample (asp) + else if (strncmp(&xmlList[xmlListPos], "asp", curModeLen) == 0) + Globals::Instance->xmlExtractModes |= 1 << (int)XMLModeShift::Sample; + // Audio Sequence (asq) + else if (strncmp(&xmlList[xmlListPos], "asq", curModeLen) == 0) + Globals::Instance->xmlExtractModes |= 1 << (int)XMLModeShift::Sequence; + + // We read the 3 chars for the type + xmlListPos += curModeLen; + + if (xmlList[xmlListPos] != ',' && xmlList[xmlListPos] != 0) + HANDLE_WARNING( + WarningType::Always, "Invalid Argument", + "XML Extract mode not separated by ','. This may have unintended side effects."); + + xmlListPos++; + } +} + +int HandleExtract(ZFileMode fileMode, ExporterSet* exporterSet, std::atomic* extractCount, std::atomic* totalExtract) +{ + bool procFileModeSuccess = false; + + if (exporterSet != nullptr && exporterSet->processFileModeFunc != nullptr) + procFileModeSuccess = exporterSet->processFileModeFunc(fileMode); + + if (!procFileModeSuccess) + { + bool parseSuccessful; + + for (auto& extFile : Globals::Instance->cfg.externalFiles) + { + if (fileMode == ZFileMode::ExtractDirectory) + { + std::vector fileList = + Directory::ListFiles(Globals::Instance->inputPath.string()); + + const int num_threads = std::thread::hardware_concurrency(); + ctpl::thread_pool pool(num_threads > 1 ? num_threads / 2 : 1); + + bool parseSuccessful; + + auto start = std::chrono::steady_clock::now(); + size_t fileListSize = fileList.size(); + Globals::Instance->singleThreaded = true; + + for (size_t i = 0; i < fileListSize; i++) + Globals::Instance->workerData[i] = new FileWorker(); + + numWorkersLeft = fileListSize; + if (totalExtract != nullptr) { + *totalExtract = fileListSize; + } + + for (size_t i = 0; i < fileListSize; i++) + { + if (Globals::Instance->singleThreaded) + { + ExtractFunc(i, fileList.size(), fileList[i], fileMode); + if (extractCount != nullptr) { + *extractCount = i; + } + } + else + { + std::string fileListItem = fileList[i]; + pool.push([i, fileListSize, fileListItem, fileMode](int) { + ExtractFunc(i, fileListSize, fileListItem, fileMode); + }); + } + } + + if (!Globals::Instance->singleThreaded) + { + while (true) + { + if (numWorkersLeft <= 0) + break; + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + } + + auto end = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast(end - start).count(); + + printf("Generated OTR File Data in %i seconds\n", diff); + } + else + { + fs::path externalXmlFilePath = + Globals::Instance->cfg.externalXmlFolder / extFile.xmlPath; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + printf("Parsing external file from config: '%s'\n", + externalXmlFilePath.c_str()); + + parseSuccessful = Parse(externalXmlFilePath, Globals::Instance->baseRomPath, + extFile.outPath, ZFileMode::ExternalFile, 0); + + if (!parseSuccessful) + return 1; + } + } + } + + return 0; +} + +void BuildAssetTexture(const fs::path& pngFilePath, TextureType texType, const fs::path& outPath) +{ + return Globals::Instance->BuildAssetTexture(pngFilePath, texType, outPath); +} + +void BuildAssetBackground(const fs::path& imageFilePath, const fs::path& outPath) +{ + ZBackground background(nullptr); + background.ParseBinaryFile(imageFilePath.string(), false); + + DiskFile::WriteAllText(outPath.string(), background.GetBodySourceCode()); +} + +void BuildAssetBlob(const fs::path& blobFilePath, const fs::path& outPath) +{ + ZBlob* blob = ZBlob::FromFile(blobFilePath.string()); + std::string name = outPath.stem().string(); // filename without extension + + std::string src = blob->GetBodySourceCode(); + + DiskFile::WriteAllText(outPath.string(), src); + + delete blob; +} diff --git a/ZAPDTR/ZAPD/NuGet/libpng.static.txt b/ZAPDTR/ZAPD/NuGet/libpng.static.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.cpp b/ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.cpp new file mode 100644 index 000000000..401e7d687 --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.cpp @@ -0,0 +1,614 @@ +#include "CutsceneMM_Commands.h" + +#include +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" + +/**** GENERIC ****/ + +// Specific for command lists where each entry has size 8 bytes +const std::unordered_map csCommandsDescMM = { + {CutsceneMM_CommandType::CS_CMD_MISC, {"CS_MISC", "(%s, %i, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_LIGHT_SETTING, {"CS_LIGHT_SETTING", "(0x%02X, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_TRANSITION, {"CS_TRANSITION", "(%s, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_MOTION_BLUR, {"CS_MOTION_BLUR", "(%s, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_GIVE_TATL, {"CS_GIVE_TATL", "(%s, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_START_SEQ, {"CS_START_SEQ", "(%s, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_SFX_REVERB_INDEX_2, + {"CS_SFX_REVERB_INDEX_2", "(0x%04X, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_SFX_REVERB_INDEX_1, + {"CS_SFX_REVERB_INDEX_1", "(0x%04X, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_MODIFY_SEQ, {"CS_MODIFY_SEQ", "(%s, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_STOP_SEQ, {"CS_STOP_SEQ", "(%s, %i, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_START_AMBIENCE, {"CS_START_AMBIENCE", "(0x%04X, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_FADE_OUT_AMBIENCE, + {"CS_FADE_OUT_AMBIENCE", "(0x%04X, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_DESTINATION, {"CS_DESTINATION", "(%s, %i, %i)"}}, + {CutsceneMM_CommandType::CS_CMD_CHOOSE_CREDITS_SCENES, + {"CS_CHOOSE_CREDITS_SCENES", "(%s, %i, %i)"}}, +}; + +CutsceneMMSubCommandEntry_GenericCmd::CutsceneMMSubCommandEntry_GenericCmd( + const std::vector& rawData, offset_t rawDataIndex, CutsceneMM_CommandType cmdId) + : CutsceneSubCommandEntry(rawData, rawDataIndex), commandId(cmdId) +{ +} + +std::string CutsceneMMSubCommandEntry_GenericCmd::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + const auto& element = csCommandsDescMM.find(commandId); + std::string entryFmt = "CS_UNK_DATA(0x%02X, %i, %i, %i)"; + std::string type = ""; + bool isIndexInSeqId = enumData->seqId.find(base - 1) != enumData->seqId.end(); + + if (element != csCommandsDescMM.end()) + { + entryFmt = element->second.cmdMacro; + entryFmt += element->second.args; + } + + if (commandId == CutsceneMM_CommandType::CS_CMD_MISC && + enumData->miscType.find(base) != enumData->miscType.end()) + type = enumData->miscType[base]; + + else if (commandId == CutsceneMM_CommandType::CS_CMD_TRANSITION && + enumData->transitionType.find(base) != enumData->transitionType.end()) + type = enumData->transitionType[base]; + + else if (commandId == CutsceneMM_CommandType::CS_CMD_MOTION_BLUR && + enumData->motionBlurType.find(base) != enumData->motionBlurType.end()) + type = enumData->motionBlurType[base]; + + else if (commandId == CutsceneMM_CommandType::CS_CMD_MODIFY_SEQ && + enumData->modifySeqType.find(base) != enumData->modifySeqType.end()) + type = enumData->modifySeqType[base]; + + else if (commandId == CutsceneMM_CommandType::CS_CMD_DESTINATION && + enumData->destinationType.find(base) != enumData->destinationType.end()) + type = enumData->destinationType[base]; + + else if (commandId == CutsceneMM_CommandType::CS_CMD_CHOOSE_CREDITS_SCENES && + enumData->chooseCreditsSceneType.find(base) != enumData->chooseCreditsSceneType.end()) + type = enumData->chooseCreditsSceneType[base]; + + else if ((commandId == CutsceneMM_CommandType::CS_CMD_START_SEQ || + commandId == CutsceneMM_CommandType::CS_CMD_STOP_SEQ) && + isIndexInSeqId) + type = enumData->seqId[base - 1]; + + else if (commandId == CutsceneMM_CommandType::CS_CMD_GIVE_TATL) + type = base ? "true" : "false"; + + if (type != "") + return StringHelper::Sprintf("%s", entryFmt.c_str(), type.c_str(), startFrame, endFrame, + pad); + + if (commandId == CutsceneMM_CommandType::CS_CMD_LIGHT_SETTING || + commandId == CutsceneMM_CommandType::CS_CMD_START_SEQ || + commandId == CutsceneMM_CommandType::CS_CMD_STOP_SEQ) + { + return StringHelper::Sprintf("%s", entryFmt.c_str(), base - 1, startFrame, endFrame, pad); + } + + return StringHelper::Sprintf("%s", entryFmt.c_str(), base, startFrame, endFrame, pad); +} + +CutsceneMMCommand_GenericCmd::CutsceneMMCommand_GenericCmd(const std::vector& rawData, + offset_t rawDataIndex, + CutsceneMM_CommandType cmdId) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + commandID = static_cast(cmdId); + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneMMSubCommandEntry_GenericCmd(rawData, rawDataIndex, cmdId); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneMMCommand_GenericCmd::GetCommandMacro() const +{ + const auto& element = csCommandsDescMM.find(static_cast(commandID)); + + if (element != csCommandsDescMM.end()) + { + return StringHelper::Sprintf("%s_LIST(%i)", element->second.cmdMacro, numEntries); + } + + return StringHelper::Sprintf("CS_UNK_DATA_LIST(0x%X, %i)", commandID, numEntries); +} + +/**** CAMERA ****/ + +CutsceneSubCommandEntry_SplineCamPoint::CutsceneSubCommandEntry_SplineCamPoint( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + interpType = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0); + weight = BitConverter::ToUInt8BE(rawData, rawDataIndex + 1); + duration = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + posX = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); + posY = BitConverter::ToUInt16BE(rawData, rawDataIndex + 6); + posZ = BitConverter::ToUInt16BE(rawData, rawDataIndex + 8); + relTo = BitConverter::ToUInt16BE(rawData, rawDataIndex + 10); +} + +std::string CutsceneSubCommandEntry_SplineCamPoint::GetBodySourceCode() const +{ + const auto interpTypeMap = &Globals::Instance->cfg.enumData.interpType; + const auto relToMap = &Globals::Instance->cfg.enumData.relTo; + + return StringHelper::Sprintf("CS_CAM_POINT(%s, 0x%02X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, %s)", + interpTypeMap->at(interpType).c_str(), weight, duration, posX, + posY, posZ, relToMap->at(relTo).c_str()); +} + +size_t CutsceneSubCommandEntry_SplineCamPoint::GetRawSize() const +{ + return 0x0C; +} + +CutsceneSubCommandEntry_SplineMiscPoint::CutsceneSubCommandEntry_SplineMiscPoint( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + unused0 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0); + roll = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + fov = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); + unused1 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 6); +} + +std::string CutsceneSubCommandEntry_SplineMiscPoint::GetBodySourceCode() const +{ + return StringHelper::Sprintf("CS_CAM_MISC(0x%04X, 0x%04X, 0x%04X, 0x%04X)", unused0, roll, fov, + unused1); +} + +size_t CutsceneSubCommandEntry_SplineMiscPoint::GetRawSize() const +{ + return 0x08; +} + +CutsceneSubCommandEntry_SplineHeader::CutsceneSubCommandEntry_SplineHeader( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + numEntries = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0); + unused0 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + unused1 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); + duration = BitConverter::ToUInt16BE(rawData, rawDataIndex + 6); +} + +std::string CutsceneSubCommandEntry_SplineHeader::GetBodySourceCode() const +{ + return StringHelper::Sprintf("CS_CAM_SPLINE(0x%04X, 0x%04X, 0x%04X, 0x%04X)", numEntries, + unused0, unused1, duration); +} + +size_t CutsceneSubCommandEntry_SplineHeader::GetRawSize() const +{ + return 0x08; +} + +CutsceneSubCommandEntry_SplineFooter::CutsceneSubCommandEntry_SplineFooter( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + uint16_t firstHalfWord = BitConverter::ToUInt16BE(rawData, rawDataIndex); + uint16_t secondHalfWord = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + + if (firstHalfWord != 0xFFFF || secondHalfWord != 4) + { + HANDLE_ERROR(WarningType::InvalidExtractedData, "Invalid Spline Footer", + StringHelper::Sprintf( + "Invalid Spline footer. Was expecting 0xFFFF, 0x0004. Got 0x%04X, 0x%04X", + firstHalfWord, secondHalfWord)); + } +} + +std::string CutsceneSubCommandEntry_SplineFooter::GetBodySourceCode() const +{ + return "CS_CAM_END()"; +} + +size_t CutsceneSubCommandEntry_SplineFooter::GetRawSize() const +{ + return 0x04; +} + +CutsceneMMCommand_Spline::CutsceneMMCommand_Spline(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + numHeaders = 0; + totalCommands = 0; + rawDataIndex += 4; + + while (1) + { + if (BitConverter::ToUInt16BE(rawData, rawDataIndex) == 0xFFFF) + { + break; + } + numHeaders++; + + auto* header = new CutsceneSubCommandEntry_SplineHeader(rawData, rawDataIndex); + rawDataIndex += header->GetRawSize(); + entries.push_back(header); + + totalCommands += header->numEntries; + + for (uint32_t i = 0; i < header->numEntries; i++) + { + auto* entry = new CutsceneSubCommandEntry_SplineCamPoint(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } + + for (uint32_t i = 0; i < header->numEntries; i++) + { + auto* entry = new CutsceneSubCommandEntry_SplineCamPoint(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } + + for (uint32_t i = 0; i < header->numEntries; i++) + { + auto* entry = new CutsceneSubCommandEntry_SplineMiscPoint(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } + } + + auto* footer = new CutsceneSubCommandEntry_SplineFooter(rawData, rawDataIndex); + entries.push_back(footer); + rawDataIndex += footer->GetRawSize(); +} + +std::string CutsceneMMCommand_Spline::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_CAM_SPLINE_LIST(%i)", numEntries); +} + +size_t CutsceneMMCommand_Spline::GetCommandSize() const +{ + // 8 Bytes once for the spline command, 8 Bytes per spline the header, two groups of size 12, 1 + // group of size 8, 4 bytes for the footer. + return 8 + (8 * numHeaders) + ((totalCommands * 2) * 0xC) + (totalCommands * 8) + 4; +} + +/**** TRANSITION GENERAL ****/ + +CutsceneSubCommandEntry_TransitionGeneral::CutsceneSubCommandEntry_TransitionGeneral( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + unk_06 = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x06); + unk_07 = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x07); + unk_08 = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x08); + unk_09 = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x09); + unk_0A = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x0A); + unk_0B = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x0B); +} + +std::string CutsceneSubCommandEntry_TransitionGeneral::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (enumData->transitionGeneralType.find(base) != enumData->transitionGeneralType.end()) + return StringHelper::Sprintf("CS_TRANSITION_GENERAL(%s, %i, %i, %i, %i, %i)", + enumData->transitionGeneralType[base].c_str(), startFrame, + endFrame, unk_06, unk_07, unk_08); + + return StringHelper::Sprintf("CS_TRANSITION_GENERAL(0x%02X, %i, %i, %i, %i, %i)", base, + startFrame, endFrame, unk_06, unk_07, unk_08); +} + +size_t CutsceneSubCommandEntry_TransitionGeneral::GetRawSize() const +{ + return 0x0C; +} + +CutsceneMMCommand_TransitionGeneral::CutsceneMMCommand_TransitionGeneral( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneSubCommandEntry_TransitionGeneral(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneMMCommand_TransitionGeneral::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_TRANSITION_GENERAL_LIST(%i)", numEntries); +} + +CutsceneSubCommandEntry_FadeOutSeq::CutsceneSubCommandEntry_FadeOutSeq( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + unk_08 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 8); +} + +/**** FADE OUT SEQUENCE ****/ + +std::string CutsceneSubCommandEntry_FadeOutSeq::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (enumData->fadeOutSeqPlayer.find(base) != enumData->fadeOutSeqPlayer.end()) + return StringHelper::Sprintf("CS_FADE_OUT_SEQ(%s, %i, %i)", + enumData->fadeOutSeqPlayer[base].c_str(), startFrame, + endFrame); + + return StringHelper::Sprintf("CS_FADE_OUT_SEQ(%i, %i, %i)", base, startFrame, endFrame); +} + +size_t CutsceneSubCommandEntry_FadeOutSeq::GetRawSize() const +{ + return 0x0C; +} + +CutsceneMMCommand_FadeOutSeq::CutsceneMMCommand_FadeOutSeq(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneSubCommandEntry_FadeOutSeq(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneMMCommand_FadeOutSeq::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_FADE_OUT_SEQ_LIST(%i)", numEntries); +} + +/**** NON IMPLEMENTED ****/ + +CutsceneSubCommandEntry_NonImplemented::CutsceneSubCommandEntry_NonImplemented( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ +} + +CutsceneMMCommand_NonImplemented::CutsceneMMCommand_NonImplemented( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneSubCommandEntry_NonImplemented(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +/**** RUMBLE ****/ + +CutsceneMMSubCommandEntry_Rumble::CutsceneMMSubCommandEntry_Rumble( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + intensity = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x06); + decayTimer = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x07); + decayStep = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x08); +} + +std::string CutsceneMMSubCommandEntry_Rumble::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (enumData->rumbleType.find(base) != enumData->rumbleType.end()) + return StringHelper::Sprintf("CS_RUMBLE(%s, %i, %i, 0x%02X, 0x%02X, 0x%02X)", + enumData->rumbleType[base].c_str(), startFrame, endFrame, + intensity, decayTimer, decayStep); + + return StringHelper::Sprintf("CS_RUMBLE(0x%04X, %i, %i, 0x%02X, 0x%02X, 0x%02X)", base, + startFrame, endFrame, intensity, decayTimer, decayStep); +} + +size_t CutsceneMMSubCommandEntry_Rumble::GetRawSize() const +{ + return 0x0C; +} + +CutsceneMMCommand_Rumble::CutsceneMMCommand_Rumble(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneMMSubCommandEntry_Rumble(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneMMCommand_Rumble::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_RUMBLE_LIST(%i)", numEntries); +} + +/**** TEXT ****/ + +CutsceneMMSubCommandEntry_Text::CutsceneMMSubCommandEntry_Text(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + type = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x6); + textId1 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x8); + textId2 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0xA); +} + +std::string CutsceneMMSubCommandEntry_Text::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (type == 0xFFFF) + { + return StringHelper::Sprintf("CS_TEXT_NONE(%i, %i)", startFrame, endFrame); + } + + if (type == 2 && + enumData->ocarinaSongActionId.find(base) != enumData->ocarinaSongActionId.end()) + { + return StringHelper::Sprintf("CS_TEXT_OCARINA_ACTION(%s, %i, %i, 0x%X)", + enumData->ocarinaSongActionId[base].c_str(), startFrame, + endFrame, textId1); + } + + switch (type) + { + case 0: + return StringHelper::Sprintf("CS_TEXT_DEFAULT(0x%X, %i, %i, 0x%X, 0x%X)", base, startFrame, + endFrame, textId1, textId2); + + case 1: + return StringHelper::Sprintf("CS_TEXT_TYPE_1(0x%X, %i, %i, 0x%X, 0x%X)", base, startFrame, + endFrame, textId1, textId2); + + case 3: + return StringHelper::Sprintf("CS_TEXT_TYPE_3(0x%X, %i, %i, 0x%X, 0x%X)", base, startFrame, + endFrame, textId1, textId2); + + case 4: + return StringHelper::Sprintf("CS_TEXT_BOSSES_REMAINS(0x%X, %i, %i, 0x%X)", base, startFrame, + endFrame, textId1); + + case 5: + return StringHelper::Sprintf("CS_TEXT_ALL_NORMAL_MASKS(0x%X, %i, %i, 0x%X)", base, + startFrame, endFrame, textId1); + } + + return nullptr; +} + +size_t CutsceneMMSubCommandEntry_Text::GetRawSize() const +{ + return 0x0C; +} + +CutsceneMMCommand_Text::CutsceneMMCommand_Text(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneMMSubCommandEntry_Text(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneMMCommand_Text::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_TEXT_LIST(%i)", numEntries); +} + +/**** ACTOR CUE ****/ + +CutsceneMMSubCommandEntry_ActorCue::CutsceneMMSubCommandEntry_ActorCue( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + rotX = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x6); + rotY = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x8); + rotZ = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0xA); + startPosX = BitConverter::ToInt32BE(rawData, rawDataIndex + 0xC); + startPosY = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x10); + startPosZ = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x14); + endPosX = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x18); + endPosY = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x1C); + endPosZ = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x20); + normalX = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x24); + normalY = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x28); + normalZ = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x2C); +} + +std::string CutsceneMMSubCommandEntry_ActorCue::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (static_cast(commandID) == CutsceneMM_CommandType::CS_CMD_PLAYER_CUE) + { + return StringHelper::Sprintf("CS_PLAYER_CUE(%s, %i, %i, 0x%04X, 0x%04X, 0x%04X, %i, %i, " + "%i, %i, %i, %i, %.8ef, %.8ef, %.8ef)", + enumData->playerCueId[base].c_str(), startFrame, endFrame, + rotX, rotY, rotZ, startPosX, startPosY, startPosZ, endPosX, + endPosY, endPosZ, normalX, normalY, normalZ); + } + else + { + return StringHelper::Sprintf("CS_ACTOR_CUE(%i, %i, %i, 0x%04X, 0x%04X, 0x%04X, %i, %i, " + "%i, %i, %i, %i, %.8ef, %.8ef, %.8ef)", + base, startFrame, endFrame, rotX, rotY, rotZ, startPosX, + startPosY, startPosZ, endPosX, endPosY, endPosZ, normalX, + normalY, normalZ); + } +} + +size_t CutsceneMMSubCommandEntry_ActorCue::GetRawSize() const +{ + return 0x30; +} + +CutsceneMMCommand_ActorCue::CutsceneMMCommand_ActorCue(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneMMSubCommandEntry_ActorCue(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneMMCommand_ActorCue::GetCommandMacro() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (static_cast(commandID) == CutsceneMM_CommandType::CS_CMD_PLAYER_CUE) + { + return StringHelper::Sprintf("CS_PLAYER_CUE_LIST(%i)", numEntries); + } + + if (enumData->cutsceneCmd.find(commandID) != enumData->cutsceneCmd.end()) + { + return StringHelper::Sprintf("CS_ACTOR_CUE_LIST(%s, %i)", + enumData->cutsceneCmd[commandID].c_str(), numEntries); + } + return StringHelper::Sprintf("CS_ACTOR_CUE_LIST(0x%03X, %i)", commandID, numEntries); +} diff --git a/ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.h b/ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.h new file mode 100644 index 000000000..597f6788b --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/CutsceneMM_Commands.h @@ -0,0 +1,481 @@ +#pragma once + +#include "Cutscene_Common.h" + +// https://github.com/zeldaret/mm/blob/0c7b90cf97f26483c8b6a98ae099a295f61e72ab/include/z64cutscene.h#L294-530 +enum class CutsceneMM_CommandType +{ + /* -2 */ CS_CMD_ACTOR_CUE_POST_PROCESS = -2, + /* -1 */ CS_CAM_STOP, + /* 0x00A */ CS_CMD_TEXT = 10, + /* 0x05A */ CS_CMD_CAMERA_SPLINE = 90, + /* 0x064 */ CS_CMD_ACTOR_CUE_100 = 100, + /* 0x065 */ CS_CMD_ACTOR_CUE_101, + /* 0x066 */ CS_CMD_ACTOR_CUE_102, + /* 0x067 */ CS_CMD_ACTOR_CUE_103, + /* 0x068 */ CS_CMD_ACTOR_CUE_104, + /* 0x069 */ CS_CMD_ACTOR_CUE_105, + /* 0x06A */ CS_CMD_ACTOR_CUE_106, + /* 0x06B */ CS_CMD_ACTOR_CUE_107, + /* 0x06C */ CS_CMD_ACTOR_CUE_108, + /* 0x06D */ CS_CMD_ACTOR_CUE_109, + /* 0x06E */ CS_CMD_ACTOR_CUE_110, + /* 0x06F */ CS_CMD_ACTOR_CUE_111, + /* 0x070 */ CS_CMD_ACTOR_CUE_112, + /* 0x071 */ CS_CMD_ACTOR_CUE_113, + /* 0x072 */ CS_CMD_ACTOR_CUE_114, + /* 0x073 */ CS_CMD_ACTOR_CUE_115, + /* 0x074 */ CS_CMD_ACTOR_CUE_116, + /* 0x075 */ CS_CMD_ACTOR_CUE_117, + /* 0x076 */ CS_CMD_ACTOR_CUE_118, + /* 0x077 */ CS_CMD_ACTOR_CUE_119, + /* 0x078 */ CS_CMD_ACTOR_CUE_120, + /* 0x079 */ CS_CMD_ACTOR_CUE_121, + /* 0x07A */ CS_CMD_ACTOR_CUE_122, + /* 0x07B */ CS_CMD_ACTOR_CUE_123, + /* 0x07C */ CS_CMD_ACTOR_CUE_124, + /* 0x07D */ CS_CMD_ACTOR_CUE_125, + /* 0x07E */ CS_CMD_ACTOR_CUE_126, + /* 0x07F */ CS_CMD_ACTOR_CUE_127, + /* 0x080 */ CS_CMD_ACTOR_CUE_128, + /* 0x081 */ CS_CMD_ACTOR_CUE_129, + /* 0x082 */ CS_CMD_ACTOR_CUE_130, + /* 0x083 */ CS_CMD_ACTOR_CUE_131, + /* 0x084 */ CS_CMD_ACTOR_CUE_132, + /* 0x085 */ CS_CMD_ACTOR_CUE_133, + /* 0x086 */ CS_CMD_ACTOR_CUE_134, + /* 0x087 */ CS_CMD_ACTOR_CUE_135, + /* 0x088 */ CS_CMD_ACTOR_CUE_136, + /* 0x089 */ CS_CMD_ACTOR_CUE_137, + /* 0x08A */ CS_CMD_ACTOR_CUE_138, + /* 0x08B */ CS_CMD_ACTOR_CUE_139, + /* 0x08C */ CS_CMD_ACTOR_CUE_140, + /* 0x08D */ CS_CMD_ACTOR_CUE_141, + /* 0x08E */ CS_CMD_ACTOR_CUE_142, + /* 0x08F */ CS_CMD_ACTOR_CUE_143, + /* 0x090 */ CS_CMD_ACTOR_CUE_144, + /* 0x091 */ CS_CMD_ACTOR_CUE_145, + /* 0x092 */ CS_CMD_ACTOR_CUE_146, + /* 0x093 */ CS_CMD_ACTOR_CUE_147, + /* 0x094 */ CS_CMD_ACTOR_CUE_148, + /* 0x095 */ CS_CMD_ACTOR_CUE_149, + /* 0x096 */ CS_CMD_MISC, + /* 0x097 */ CS_CMD_LIGHT_SETTING, + /* 0x098 */ CS_CMD_TRANSITION, + /* 0x099 */ CS_CMD_MOTION_BLUR, + /* 0x09A */ CS_CMD_GIVE_TATL, + /* 0x09B */ CS_CMD_TRANSITION_GENERAL, + /* 0x09C */ CS_CMD_FADE_OUT_SEQ, + /* 0x09D */ CS_CMD_TIME, + /* 0x0C8 */ CS_CMD_PLAYER_CUE = 200, + /* 0x0C9 */ CS_CMD_ACTOR_CUE_201, + /* 0x0FA */ CS_CMD_UNK_DATA_FA = 0xFA, + /* 0x0FE */ CS_CMD_UNK_DATA_FE = 0xFE, + /* 0x0FF */ CS_CMD_UNK_DATA_FF, + /* 0x100 */ CS_CMD_UNK_DATA_100, + /* 0x101 */ CS_CMD_UNK_DATA_101, + /* 0x102 */ CS_CMD_UNK_DATA_102, + /* 0x103 */ CS_CMD_UNK_DATA_103, + /* 0x104 */ CS_CMD_UNK_DATA_104, + /* 0x105 */ CS_CMD_UNK_DATA_105, + /* 0x108 */ CS_CMD_UNK_DATA_108 = 0x108, + /* 0x109 */ CS_CMD_UNK_DATA_109, + /* 0x12C */ CS_CMD_START_SEQ = 300, + /* 0x12D */ CS_CMD_STOP_SEQ, + /* 0x12E */ CS_CMD_START_AMBIENCE, + /* 0x12F */ CS_CMD_FADE_OUT_AMBIENCE, + /* 0x130 */ CS_CMD_SFX_REVERB_INDEX_2, + /* 0x131 */ CS_CMD_SFX_REVERB_INDEX_1, + /* 0x132 */ CS_CMD_MODIFY_SEQ, + /* 0x15E */ CS_CMD_DESTINATION = 350, + /* 0x15F */ CS_CMD_CHOOSE_CREDITS_SCENES, + /* 0x190 */ CS_CMD_RUMBLE = 400, + /* 0x1C2 */ CS_CMD_ACTOR_CUE_450 = 450, + /* 0x1C3 */ CS_CMD_ACTOR_CUE_451, + /* 0x1C4 */ CS_CMD_ACTOR_CUE_452, + /* 0x1C5 */ CS_CMD_ACTOR_CUE_453, + /* 0x1C6 */ CS_CMD_ACTOR_CUE_454, + /* 0x1C7 */ CS_CMD_ACTOR_CUE_455, + /* 0x1C8 */ CS_CMD_ACTOR_CUE_456, + /* 0x1C9 */ CS_CMD_ACTOR_CUE_457, + /* 0x1CA */ CS_CMD_ACTOR_CUE_458, + /* 0x1CB */ CS_CMD_ACTOR_CUE_459, + /* 0x1CC */ CS_CMD_ACTOR_CUE_460, + /* 0x1CD */ CS_CMD_ACTOR_CUE_461, + /* 0x1CE */ CS_CMD_ACTOR_CUE_462, + /* 0x1CF */ CS_CMD_ACTOR_CUE_463, + /* 0x1D0 */ CS_CMD_ACTOR_CUE_464, + /* 0x1D1 */ CS_CMD_ACTOR_CUE_465, + /* 0x1D2 */ CS_CMD_ACTOR_CUE_466, + /* 0x1D3 */ CS_CMD_ACTOR_CUE_467, + /* 0x1D4 */ CS_CMD_ACTOR_CUE_468, + /* 0x1D5 */ CS_CMD_ACTOR_CUE_469, + /* 0x1D6 */ CS_CMD_ACTOR_CUE_470, + /* 0x1D7 */ CS_CMD_ACTOR_CUE_471, + /* 0x1D8 */ CS_CMD_ACTOR_CUE_472, + /* 0x1D9 */ CS_CMD_ACTOR_CUE_473, + /* 0x1DA */ CS_CMD_ACTOR_CUE_474, + /* 0x1DB */ CS_CMD_ACTOR_CUE_475, + /* 0x1DC */ CS_CMD_ACTOR_CUE_476, + /* 0x1DD */ CS_CMD_ACTOR_CUE_477, + /* 0x1DE */ CS_CMD_ACTOR_CUE_478, + /* 0x1DF */ CS_CMD_ACTOR_CUE_479, + /* 0x1E0 */ CS_CMD_ACTOR_CUE_480, + /* 0x1E1 */ CS_CMD_ACTOR_CUE_481, + /* 0x1E2 */ CS_CMD_ACTOR_CUE_482, + /* 0x1E3 */ CS_CMD_ACTOR_CUE_483, + /* 0x1E4 */ CS_CMD_ACTOR_CUE_484, + /* 0x1E5 */ CS_CMD_ACTOR_CUE_485, + /* 0x1E6 */ CS_CMD_ACTOR_CUE_486, + /* 0x1E7 */ CS_CMD_ACTOR_CUE_487, + /* 0x1E8 */ CS_CMD_ACTOR_CUE_488, + /* 0x1E9 */ CS_CMD_ACTOR_CUE_489, + /* 0x1EA */ CS_CMD_ACTOR_CUE_490, + /* 0x1EB */ CS_CMD_ACTOR_CUE_491, + /* 0x1EC */ CS_CMD_ACTOR_CUE_492, + /* 0x1ED */ CS_CMD_ACTOR_CUE_493, + /* 0x1EE */ CS_CMD_ACTOR_CUE_494, + /* 0x1EF */ CS_CMD_ACTOR_CUE_495, + /* 0x1F0 */ CS_CMD_ACTOR_CUE_496, + /* 0x1F1 */ CS_CMD_ACTOR_CUE_497, + /* 0x1F2 */ CS_CMD_ACTOR_CUE_498, + /* 0x1F3 */ CS_CMD_ACTOR_CUE_499, + /* 0x1F4 */ CS_CMD_ACTOR_CUE_500, + /* 0x1F5 */ CS_CMD_ACTOR_CUE_501, + /* 0x1F6 */ CS_CMD_ACTOR_CUE_502, + /* 0x1F7 */ CS_CMD_ACTOR_CUE_503, + /* 0x1F8 */ CS_CMD_ACTOR_CUE_504, + /* 0x1F9 */ CS_CMD_ACTOR_CUE_SOTCS, + /* 0x1FA */ CS_CMD_ACTOR_CUE_506, + /* 0x1FB */ CS_CMD_ACTOR_CUE_507, + /* 0x1FC */ CS_CMD_ACTOR_CUE_508, + /* 0x1FD */ CS_CMD_ACTOR_CUE_509, + /* 0x1FE */ CS_CMD_ACTOR_CUE_510, + /* 0x1FF */ CS_CMD_ACTOR_CUE_511, + /* 0x200 */ CS_CMD_ACTOR_CUE_512, + /* 0x201 */ CS_CMD_ACTOR_CUE_513, + /* 0x202 */ CS_CMD_ACTOR_CUE_514, + /* 0x203 */ CS_CMD_ACTOR_CUE_515, + /* 0x204 */ CS_CMD_ACTOR_CUE_516, + /* 0x205 */ CS_CMD_ACTOR_CUE_517, + /* 0x206 */ CS_CMD_ACTOR_CUE_518, + /* 0x207 */ CS_CMD_ACTOR_CUE_519, + /* 0x208 */ CS_CMD_ACTOR_CUE_520, + /* 0x209 */ CS_CMD_ACTOR_CUE_521, + /* 0x20A */ CS_CMD_ACTOR_CUE_522, + /* 0x20B */ CS_CMD_ACTOR_CUE_523, + /* 0x20C */ CS_CMD_ACTOR_CUE_524, + /* 0x20D */ CS_CMD_ACTOR_CUE_525, + /* 0x20E */ CS_CMD_ACTOR_CUE_526, + /* 0x20F */ CS_CMD_ACTOR_CUE_527, + /* 0x210 */ CS_CMD_ACTOR_CUE_528, + /* 0x211 */ CS_CMD_ACTOR_CUE_529, + /* 0x212 */ CS_CMD_ACTOR_CUE_530, + /* 0x213 */ CS_CMD_ACTOR_CUE_531, + /* 0x214 */ CS_CMD_ACTOR_CUE_532, + /* 0x215 */ CS_CMD_ACTOR_CUE_533, + /* 0x216 */ CS_CMD_ACTOR_CUE_534, + /* 0x217 */ CS_CMD_ACTOR_CUE_535, + /* 0x218 */ CS_CMD_ACTOR_CUE_536, + /* 0x219 */ CS_CMD_ACTOR_CUE_537, + /* 0x21A */ CS_CMD_ACTOR_CUE_538, + /* 0x21B */ CS_CMD_ACTOR_CUE_539, + /* 0x21C */ CS_CMD_ACTOR_CUE_540, + /* 0x21D */ CS_CMD_ACTOR_CUE_541, + /* 0x21E */ CS_CMD_ACTOR_CUE_542, + /* 0x21F */ CS_CMD_ACTOR_CUE_543, + /* 0x220 */ CS_CMD_ACTOR_CUE_544, + /* 0x221 */ CS_CMD_ACTOR_CUE_545, + /* 0x222 */ CS_CMD_ACTOR_CUE_546, + /* 0x223 */ CS_CMD_ACTOR_CUE_547, + /* 0x224 */ CS_CMD_ACTOR_CUE_548, + /* 0x225 */ CS_CMD_ACTOR_CUE_549, + /* 0x226 */ CS_CMD_ACTOR_CUE_550, + /* 0x227 */ CS_CMD_ACTOR_CUE_551, + /* 0x228 */ CS_CMD_ACTOR_CUE_552, + /* 0x229 */ CS_CMD_ACTOR_CUE_553, + /* 0x22A */ CS_CMD_ACTOR_CUE_554, + /* 0x22B */ CS_CMD_ACTOR_CUE_555, + /* 0x22C */ CS_CMD_ACTOR_CUE_556, + /* 0x22D */ CS_CMD_ACTOR_CUE_557, + /* 0x22E */ CS_CMD_ACTOR_CUE_558, + /* 0x22F */ CS_CMD_ACTOR_CUE_559, + /* 0x230 */ CS_CMD_ACTOR_CUE_560, + /* 0x231 */ CS_CMD_ACTOR_CUE_561, + /* 0x232 */ CS_CMD_ACTOR_CUE_562, + /* 0x233 */ CS_CMD_ACTOR_CUE_563, + /* 0x234 */ CS_CMD_ACTOR_CUE_564, + /* 0x235 */ CS_CMD_ACTOR_CUE_565, + /* 0x236 */ CS_CMD_ACTOR_CUE_566, + /* 0x237 */ CS_CMD_ACTOR_CUE_567, + /* 0x238 */ CS_CMD_ACTOR_CUE_568, + /* 0x239 */ CS_CMD_ACTOR_CUE_569, + /* 0x23A */ CS_CMD_ACTOR_CUE_570, + /* 0x23B */ CS_CMD_ACTOR_CUE_571, + /* 0x23C */ CS_CMD_ACTOR_CUE_572, + /* 0x23D */ CS_CMD_ACTOR_CUE_573, + /* 0x23E */ CS_CMD_ACTOR_CUE_574, + /* 0x23F */ CS_CMD_ACTOR_CUE_575, + /* 0x240 */ CS_CMD_ACTOR_CUE_576, + /* 0x241 */ CS_CMD_ACTOR_CUE_577, + /* 0x242 */ CS_CMD_ACTOR_CUE_578, + /* 0x243 */ CS_CMD_ACTOR_CUE_579, + /* 0x244 */ CS_CMD_ACTOR_CUE_580, + /* 0x245 */ CS_CMD_ACTOR_CUE_581, + /* 0x246 */ CS_CMD_ACTOR_CUE_582, + /* 0x247 */ CS_CMD_ACTOR_CUE_583, + /* 0x248 */ CS_CMD_ACTOR_CUE_584, + /* 0x249 */ CS_CMD_ACTOR_CUE_585, + /* 0x24A */ CS_CMD_ACTOR_CUE_586, + /* 0x24B */ CS_CMD_ACTOR_CUE_587, + /* 0x24C */ CS_CMD_ACTOR_CUE_588, + /* 0x24D */ CS_CMD_ACTOR_CUE_589, + /* 0x24E */ CS_CMD_ACTOR_CUE_590, + /* 0x24F */ CS_CMD_ACTOR_CUE_591, + /* 0x250 */ CS_CMD_ACTOR_CUE_592, + /* 0x251 */ CS_CMD_ACTOR_CUE_593, + /* 0x252 */ CS_CMD_ACTOR_CUE_594, + /* 0x253 */ CS_CMD_ACTOR_CUE_595, + /* 0x254 */ CS_CMD_ACTOR_CUE_596, + /* 0x255 */ CS_CMD_ACTOR_CUE_597, + /* 0x256 */ CS_CMD_ACTOR_CUE_598, + /* 0x257 */ CS_CMD_ACTOR_CUE_599 +}; + +/**** GENERIC ****/ + +class CutsceneMMSubCommandEntry_GenericCmd : public CutsceneSubCommandEntry +{ +public: + CutsceneMM_CommandType commandId; + + CutsceneMMSubCommandEntry_GenericCmd(const std::vector& rawData, offset_t rawDataIndex, + CutsceneMM_CommandType cmdId); + + std::string GetBodySourceCode() const override; +}; + +class CutsceneMMCommand_GenericCmd : public CutsceneCommand +{ +public: + CutsceneMMCommand_GenericCmd(const std::vector& rawData, offset_t rawDataIndex, + CutsceneMM_CommandType cmdId); + + std::string GetCommandMacro() const override; +}; + +/**** CAMERA ****/ + +// TODO: MM cutscene camera command is implemented as a placeholder until we better understand how +// it works + +class CutsceneSubCommandEntry_SplineCamPoint : public CutsceneSubCommandEntry +{ +public: + uint8_t interpType; + uint8_t weight; + uint16_t duration; + + uint16_t posX; + uint16_t posY; + + uint16_t posZ; + uint16_t relTo; + + CutsceneSubCommandEntry_SplineCamPoint(const std::vector& rawData, + offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneSubCommandEntry_SplineMiscPoint : public CutsceneSubCommandEntry +{ +public: + uint16_t unused0; + uint16_t roll; + uint16_t fov; + uint16_t unused1; + + CutsceneSubCommandEntry_SplineMiscPoint(const std::vector& rawData, + offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneSubCommandEntry_SplineFooter : public CutsceneSubCommandEntry +{ +public: + CutsceneSubCommandEntry_SplineFooter(const std::vector& rawData, + offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneSubCommandEntry_SplineHeader : public CutsceneSubCommandEntry +{ +public: + uint16_t numEntries; + uint16_t unused0; + uint16_t unused1; + uint16_t duration; + CutsceneSubCommandEntry_SplineHeader(const std::vector& rawData, + offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneMMCommand_Spline : public CutsceneCommand +{ +public: + uint32_t numHeaders; + uint32_t totalCommands; + CutsceneMMCommand_Spline(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; + size_t GetCommandSize() const override; +}; + +/**** TRANSITION GENERAL ****/ + +class CutsceneSubCommandEntry_TransitionGeneral : public CutsceneSubCommandEntry +{ +public: + uint8_t unk_06; + uint8_t unk_07; + uint8_t unk_08; + uint8_t unk_09; + uint8_t unk_0A; + uint8_t unk_0B; + + CutsceneSubCommandEntry_TransitionGeneral(const std::vector& rawData, + offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneMMCommand_TransitionGeneral : public CutsceneCommand +{ +public: + CutsceneMMCommand_TransitionGeneral(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; + +/**** FADE OUT SEQUENCE ****/ + +class CutsceneSubCommandEntry_FadeOutSeq : public CutsceneSubCommandEntry +{ +public: + uint32_t unk_08; + + CutsceneSubCommandEntry_FadeOutSeq(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneMMCommand_FadeOutSeq : public CutsceneCommand +{ +public: + CutsceneMMCommand_FadeOutSeq(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; + +/**** NON IMPLEMENTED ****/ + +class CutsceneSubCommandEntry_NonImplemented : public CutsceneSubCommandEntry +{ +public: + CutsceneSubCommandEntry_NonImplemented(const std::vector& rawData, + offset_t rawDataIndex); +}; + +class CutsceneMMCommand_NonImplemented : public CutsceneCommand +{ +public: + CutsceneMMCommand_NonImplemented(const std::vector& rawData, offset_t rawDataIndex); +}; + +/**** RUMBLE ****/ + +class CutsceneMMSubCommandEntry_Rumble : public CutsceneSubCommandEntry +{ +public: + uint8_t intensity; + uint8_t decayTimer; + uint8_t decayStep; + + CutsceneMMSubCommandEntry_Rumble(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneMMCommand_Rumble : public CutsceneCommand +{ +public: + CutsceneMMCommand_Rumble(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; + +/**** TEXT ****/ + +class CutsceneMMSubCommandEntry_Text : public CutsceneSubCommandEntry +{ +public: + uint16_t type; + uint16_t textId1; + uint16_t textId2; + + CutsceneMMSubCommandEntry_Text(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneMMCommand_Text : public CutsceneCommand +{ +public: + CutsceneMMCommand_Text(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; + +/**** ACTOR CUE ****/ + +class CutsceneMMSubCommandEntry_ActorCue : public CutsceneSubCommandEntry +{ +public: + uint16_t rotX, rotY, rotZ; + int32_t startPosX, startPosY, startPosZ; + int32_t endPosX, endPosY, endPosZ; + float normalX, normalY, normalZ; + + CutsceneMMSubCommandEntry_ActorCue(const std::vector& rawData, offset_t rawDataIndex); + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneMMCommand_ActorCue : public CutsceneCommand +{ +public: + CutsceneMMCommand_ActorCue(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; diff --git a/ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.cpp b/ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.cpp new file mode 100644 index 000000000..f0577392a --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.cpp @@ -0,0 +1,458 @@ +#include "CutsceneOoT_Commands.h" + +#include +#include +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +/**** GENERIC ****/ + +// Specific for command lists where each entry has size 0x30 bytes +const std::unordered_map csCommandsDesc = { + {CutsceneOoT_CommandType::CS_CMD_MISC, + {"CS_MISC", "(%s, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)"}}, + {CutsceneOoT_CommandType::CS_CMD_LIGHT_SETTING, + {"CS_LIGHT_SETTING", "(0x%02X, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)"}}, + {CutsceneOoT_CommandType::CS_CMD_START_SEQ, + {"CS_START_SEQ", "(%s, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)"}}, + {CutsceneOoT_CommandType::CS_CMD_STOP_SEQ, + {"CS_STOP_SEQ", "(%s, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)"}}, + {CutsceneOoT_CommandType::CS_CMD_FADE_OUT_SEQ, + {"CS_FADE_OUT_SEQ", "(%s, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)"}}, +}; + +CutsceneOoTSubCommandEntry_GenericCmd::CutsceneOoTSubCommandEntry_GenericCmd( + const std::vector& rawData, offset_t rawDataIndex, CutsceneOoT_CommandType cmdId) + : CutsceneSubCommandEntry(rawData, rawDataIndex), commandId(cmdId) +{ + word0 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x0); + word1 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x4); + + unused1 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x8); + unused2 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0xC); + unused3 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x10); + unused4 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x14); + unused5 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x18); + unused6 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x1C); + unused7 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x20); + unused8 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x24); + unused9 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x28); + unused10 = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x2C); +} + +std::string CutsceneOoTSubCommandEntry_GenericCmd::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + const auto& element = csCommandsDesc.find(commandId); + + if (element != csCommandsDesc.end()) + { + bool isIndexInMisc = enumData->miscType.find(base) != enumData->miscType.end(); + bool isIndexInFade = + enumData->fadeOutSeqPlayer.find(base) != enumData->fadeOutSeqPlayer.end(); + bool isIndexInSeqId = enumData->seqId.find(base - 1) != enumData->seqId.end(); + std::string entryFmt = element->second.cmdMacro; + std::string firstArg; + entryFmt += element->second.args; + + if (commandId == CutsceneOoT_CommandType::CS_CMD_MISC && isIndexInMisc) + firstArg = enumData->miscType[base]; + else if (commandId == CutsceneOoT_CommandType::CS_CMD_FADE_OUT_SEQ && isIndexInFade) + firstArg = enumData->fadeOutSeqPlayer[base]; + else if (commandId == CutsceneOoT_CommandType::CS_CMD_START_SEQ && isIndexInSeqId) + firstArg = enumData->seqId[base - 1]; + else if (commandId == CutsceneOoT_CommandType::CS_CMD_STOP_SEQ && isIndexInSeqId) + firstArg = enumData->seqId[base - 1]; + else + { + return StringHelper::Sprintf(entryFmt.c_str(), base - 1, startFrame, endFrame, pad, + unused1, unused2, unused3, unused4, unused5, unused6, + unused7, unused8, unused9, unused10); + } + } + return StringHelper::Sprintf("CS_UNK_DATA(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, " + "0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", + word0, word1, unused1, unused2, unused3, unused4, unused5, unused6, + unused7, unused8, unused9, unused10); +} + +size_t CutsceneOoTSubCommandEntry_GenericCmd::GetRawSize() const +{ + return 0x30; +} + +CutsceneOoTCommand_GenericCmd::CutsceneOoTCommand_GenericCmd(const std::vector& rawData, + offset_t rawDataIndex, + CutsceneOoT_CommandType cmdId) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + commandID = static_cast(cmdId); + entries.reserve(numEntries); + + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneOoTSubCommandEntry_GenericCmd(rawData, rawDataIndex, cmdId); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneOoTCommand_GenericCmd::GetCommandMacro() const +{ + const auto& element = csCommandsDesc.find(static_cast(commandID)); + + if (element != csCommandsDesc.end()) + { + return StringHelper::Sprintf("%s_LIST(%i)", element->second.cmdMacro, numEntries); + } + + return StringHelper::Sprintf("CS_UNK_DATA_LIST(0x%X, %i)", commandID, numEntries); +} + +/**** CAMERA ****/ + +CutsceneOoTCommand_CameraPoint::CutsceneOoTCommand_CameraPoint(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + continueFlag = BitConverter::ToInt8BE(rawData, rawDataIndex + 0); + cameraRoll = BitConverter::ToInt8BE(rawData, rawDataIndex + 1); + nextPointFrame = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + viewAngle = BitConverter::ToFloatBE(rawData, rawDataIndex + 4); + + posX = BitConverter::ToInt16BE(rawData, rawDataIndex + 8); + posY = BitConverter::ToInt16BE(rawData, rawDataIndex + 10); + posZ = BitConverter::ToInt16BE(rawData, rawDataIndex + 12); + + unused = BitConverter::ToInt16BE(rawData, rawDataIndex + 14); +} + +std::string CutsceneOoTCommand_CameraPoint::GetBodySourceCode() const +{ + std::string continueMacro = "CS_CAM_CONTINUE"; + if (continueFlag != 0) + continueMacro = "CS_CAM_STOP"; + + return StringHelper::Sprintf("CS_CAM_POINT(%s, 0x%02X, %i, %ff, %i, %i, %i, 0x%04X)", + continueMacro.c_str(), cameraRoll, nextPointFrame, viewAngle, posX, + posY, posZ, unused); +} + +size_t CutsceneOoTCommand_CameraPoint::GetRawSize() const +{ + return 0x10; +} + +CutsceneOoTCommand_GenericCameraCmd::CutsceneOoTCommand_GenericCameraCmd( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + base = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0); + startFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + endFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); + unused = BitConverter::ToUInt16BE(rawData, rawDataIndex + 6); + + bool shouldContinue = true; + + uint32_t currentPtr = rawDataIndex + 8; + + while (shouldContinue) + { + CutsceneOoTCommand_CameraPoint* camPoint = + new CutsceneOoTCommand_CameraPoint(rawData, currentPtr); + entries.push_back(camPoint); + + if (camPoint->continueFlag == -1) + shouldContinue = false; + + currentPtr += camPoint->GetRawSize(); + } +} + +std::string CutsceneOoTCommand_GenericCameraCmd::GetCommandMacro() const +{ + std::string result; + const char* listStr; + + if (commandID == (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_AT_SPLINE) + { + listStr = "CS_CAM_AT_SPLINE"; + } + else if (commandID == (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_AT_SPLINE_REL_TO_PLAYER) + { + listStr = "CS_CAM_AT_SPLINE_REL_TO_PLAYER"; + } + else if (commandID == (uint32_t)CutsceneOoT_CommandType::CS_CMD_CAM_EYE_SPLINE_REL_TO_PLAYER) + { + listStr = "CS_CAM_EYE_SPLINE_REL_TO_PLAYER"; + } + else + { + listStr = "CS_CAM_EYE_SPLINE"; + } + + result += StringHelper::Sprintf("%s(%i, %i)", listStr, startFrame, endFrame); + + return result; +} + +size_t CutsceneOoTCommand_GenericCameraCmd::GetCommandSize() const +{ + return 0x0C + entries.at(0)->GetRawSize() * entries.size(); +} + +/**** RUMBLE ****/ + +CutsceneOoTSubCommandEntry_Rumble::CutsceneOoTSubCommandEntry_Rumble( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + sourceStrength = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x06); + duration = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x07); + decreaseRate = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x08); + unk_09 = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x09); + unk_0A = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x0A); +} + +std::string CutsceneOoTSubCommandEntry_Rumble::GetBodySourceCode() const +{ + // Note: the first argument is unused + return StringHelper::Sprintf("CS_RUMBLE_CONTROLLER(%i, %i, %i, %i, %i, %i, 0x%02X, 0x%02X)", + base, startFrame, endFrame, sourceStrength, duration, decreaseRate, + unk_09, unk_0A); +} + +size_t CutsceneOoTSubCommandEntry_Rumble::GetRawSize() const +{ + return 0x0C; +} + +CutsceneOoTCommand_Rumble::CutsceneOoTCommand_Rumble(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneOoTSubCommandEntry_Rumble(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneOoTCommand_Rumble::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_RUMBLE_CONTROLLER_LIST(%i)", numEntries); +} + +/**** TEXT ****/ + +CutsceneOoTSubCommandEntry_Text::CutsceneOoTSubCommandEntry_Text( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + type = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x6); + textId1 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x8); + textId2 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0xA); +} + +std::string CutsceneOoTSubCommandEntry_Text::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (type == 0xFFFF) + { + return StringHelper::Sprintf("CS_TEXT_NONE(%i, %i)", startFrame, endFrame); + } + if (type == 2 && + enumData->ocarinaSongActionId.find(base) != enumData->ocarinaSongActionId.end()) + { + return StringHelper::Sprintf("CS_TEXT_OCARINA_ACTION(%s, %i, %i, 0x%X)", + enumData->ocarinaSongActionId[base].c_str(), startFrame, + endFrame, textId1); + } + + if (enumData->textType.find(type) != enumData->textType.end()) + { + return StringHelper::Sprintf("CS_TEXT(0x%X, %i, %i, %s, 0x%X, 0x%X)", base, startFrame, + endFrame, enumData->textType[type].c_str(), textId1, textId2); + } + + return StringHelper::Sprintf("CS_TEXT(0x%X, %i, %i, %i, 0x%X, 0x%X)", base, startFrame, + endFrame, type, textId1, textId2); +} + +size_t CutsceneOoTSubCommandEntry_Text::GetRawSize() const +{ + return 0x0C; +} + +CutsceneOoTCommand_Text::CutsceneOoTCommand_Text(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneOoTSubCommandEntry_Text(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneOoTCommand_Text::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_TEXT_LIST(%i)", numEntries); +} + +/**** ACTOR CUE ****/ + +CutsceneOoTSubCommandEntry_ActorCue::CutsceneOoTSubCommandEntry_ActorCue( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + rotX = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x6); + rotY = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x8); + rotZ = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0xA); + startPosX = BitConverter::ToInt32BE(rawData, rawDataIndex + 0xC); + startPosY = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x10); + startPosZ = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x14); + endPosX = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x18); + endPosY = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x1C); + endPosZ = BitConverter::ToInt32BE(rawData, rawDataIndex + 0x20); + normalX = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x24); + normalY = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x28); + normalZ = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x2C); +} + +std::string CutsceneOoTSubCommandEntry_ActorCue::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (static_cast(commandID) == + CutsceneOoT_CommandType::CS_CMD_PLAYER_CUE) + { + return StringHelper::Sprintf("CS_PLAYER_CUE(%s, %i, %i, 0x%04X, 0x%04X, 0x%04X, %i, %i, " + "%i, %i, %i, %i, %.8ef, %.8ef, %.8ef)", + enumData->playerCueId[base].c_str(), startFrame, endFrame, + rotX, rotY, rotZ, startPosX, startPosY, startPosZ, endPosX, + endPosY, endPosZ, normalX, normalY, normalZ); + } + else + { + return StringHelper::Sprintf("CS_ACTOR_CUE(%i, %i, %i, 0x%04X, 0x%04X, 0x%04X, %i, %i, " + "%i, %i, %i, %i, %.8ef, %.8ef, %.8ef)", + base, startFrame, endFrame, rotX, rotY, rotZ, startPosX, + startPosY, startPosZ, endPosX, endPosY, endPosZ, normalX, + normalY, normalZ); + } +} + +size_t CutsceneOoTSubCommandEntry_ActorCue::GetRawSize() const +{ + return 0x30; +} + +CutsceneOoTCommand_ActorCue::CutsceneOoTCommand_ActorCue(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneOoTSubCommandEntry_ActorCue(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneOoTCommand_ActorCue::GetCommandMacro() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (static_cast(commandID) == + CutsceneOoT_CommandType::CS_CMD_PLAYER_CUE) + { + return StringHelper::Sprintf("CS_PLAYER_CUE_LIST(%i)", entries.size()); + } + + if (enumData->cutsceneCmd.find(commandID) != enumData->cutsceneCmd.end()) + { + return StringHelper::Sprintf("CS_ACTOR_CUE_LIST(%s, %i)", + enumData->cutsceneCmd[commandID].c_str(), entries.size()); + } + + return StringHelper::Sprintf("CS_ACTOR_CUE_LIST(0x%04X, %i)", commandID, entries.size()); +} + +/**** DESTINATION ****/ + +CutsceneOoTCommand_Destination::CutsceneOoTCommand_Destination(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + base = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0); + startFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + endFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); + unknown = BitConverter::ToUInt16BE(rawData, rawDataIndex + 6); // endFrame duplicate +} + +std::string CutsceneOoTCommand_Destination::GenerateSourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (enumData->destination.find(base) != enumData->destination.end()) + { + return StringHelper::Sprintf("CS_DESTINATION(%s, %i, %i),\n", + enumData->destination[base].c_str(), startFrame, endFrame); + } + + return StringHelper::Sprintf("CS_DESTINATION(%i, %i, %i),\n", base, startFrame, endFrame); +} + +size_t CutsceneOoTCommand_Destination::GetCommandSize() const +{ + return 0x10; +} + +/**** TRANSITION ****/ + +CutsceneOoTCommand_Transition::CutsceneOoTCommand_Transition(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + base = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0); + startFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + endFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); +} + +std::string CutsceneOoTCommand_Transition::GenerateSourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (enumData->transitionType.find(base) != enumData->transitionType.end()) + { + return StringHelper::Sprintf("CS_TRANSITION(%s, %i, %i),\n", + enumData->transitionType[base].c_str(), startFrame, endFrame); + } + + return StringHelper::Sprintf("CS_TRANSITION(%i, %i, %i),\n", base, startFrame, endFrame); +} + +size_t CutsceneOoTCommand_Transition::GetCommandSize() const +{ + return 0x10; +} diff --git a/ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.h b/ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.h new file mode 100644 index 000000000..ff9b6797c --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/CutsceneOoT_Commands.h @@ -0,0 +1,314 @@ +#pragma once + +#include "Cutscene_Common.h" + +// https://github.com/zeldaret/oot/blob/7235af2249843fb68740111b70089bad827a4730/include/z64cutscene.h#L35-L165 +enum class CutsceneOoT_CommandType +{ + CS_CMD_CAM_EYE_SPLINE = 0x01, + CS_CMD_CAM_AT_SPLINE, + CS_CMD_MISC, + CS_CMD_LIGHT_SETTING, + CS_CMD_CAM_EYE_SPLINE_REL_TO_PLAYER, + CS_CMD_CAM_AT_SPLINE_REL_TO_PLAYER, + CS_CMD_CAM_EYE, + CS_CMD_CAM_AT, + CS_CMD_RUMBLE_CONTROLLER, + CS_CMD_PLAYER_CUE, + CS_CMD_UNIMPLEMENTED_B, + CS_CMD_UNIMPLEMENTED_D = 0x0D, + CS_CMD_ACTOR_CUE_1_0, + CS_CMD_ACTOR_CUE_0_0, + CS_CMD_ACTOR_CUE_1_1, + CS_CMD_ACTOR_CUE_0_1, + CS_CMD_ACTOR_CUE_0_2, + CS_CMD_TEXT, + CS_CMD_UNIMPLEMENTED_15 = 0x15, + CS_CMD_UNIMPLEMENTED_16, + CS_CMD_ACTOR_CUE_0_3, + CS_CMD_ACTOR_CUE_1_2, + CS_CMD_ACTOR_CUE_2_0, + CS_CMD_UNIMPLEMENTED_1B = 0x1B, + CS_CMD_UNIMPLEMENTED_1C, + CS_CMD_ACTOR_CUE_3_0, + CS_CMD_ACTOR_CUE_4_0, + CS_CMD_ACTOR_CUE_6_0, + CS_CMD_UNIMPLEMENTED_20, + CS_CMD_UNIMPLEMENTED_21, + CS_CMD_ACTOR_CUE_0_4, + CS_CMD_ACTOR_CUE_1_3, + CS_CMD_ACTOR_CUE_2_1, + CS_CMD_ACTOR_CUE_3_1, + CS_CMD_ACTOR_CUE_4_1, + CS_CMD_ACTOR_CUE_0_5, + CS_CMD_ACTOR_CUE_1_4, + CS_CMD_ACTOR_CUE_2_2, + CS_CMD_ACTOR_CUE_3_2, + CS_CMD_ACTOR_CUE_4_2, + CS_CMD_ACTOR_CUE_5_0, + CS_CMD_TRANSITION, + CS_CMD_ACTOR_CUE_0_6, + CS_CMD_ACTOR_CUE_4_3, + CS_CMD_ACTOR_CUE_1_5, + CS_CMD_ACTOR_CUE_7_0, + CS_CMD_ACTOR_CUE_2_3, + CS_CMD_ACTOR_CUE_3_3, + CS_CMD_ACTOR_CUE_6_1, + CS_CMD_ACTOR_CUE_3_4, + CS_CMD_ACTOR_CUE_4_4, + CS_CMD_ACTOR_CUE_5_1, + CS_CMD_ACTOR_CUE_6_2 = 0x39, + CS_CMD_ACTOR_CUE_6_3, + CS_CMD_UNIMPLEMENTED_3B, + CS_CMD_ACTOR_CUE_7_1, + CS_CMD_UNIMPLEMENTED_3D, + CS_CMD_ACTOR_CUE_8_0, + CS_CMD_ACTOR_CUE_3_5, + CS_CMD_ACTOR_CUE_1_6, + CS_CMD_ACTOR_CUE_3_6, + CS_CMD_ACTOR_CUE_3_7, + CS_CMD_ACTOR_CUE_2_4, + CS_CMD_ACTOR_CUE_1_7, + CS_CMD_ACTOR_CUE_2_5, + CS_CMD_ACTOR_CUE_1_8, + CS_CMD_UNIMPLEMENTED_47, + CS_CMD_ACTOR_CUE_2_6, + CS_CMD_UNIMPLEMENTED_49, + CS_CMD_ACTOR_CUE_2_7, + CS_CMD_ACTOR_CUE_3_8, + CS_CMD_ACTOR_CUE_0_7, + CS_CMD_ACTOR_CUE_5_2, + CS_CMD_ACTOR_CUE_1_9, + CS_CMD_ACTOR_CUE_4_5, + CS_CMD_ACTOR_CUE_1_10, + CS_CMD_ACTOR_CUE_2_8, + CS_CMD_ACTOR_CUE_3_9, + CS_CMD_ACTOR_CUE_4_6, + CS_CMD_ACTOR_CUE_5_3, + CS_CMD_ACTOR_CUE_0_8, + CS_CMD_START_SEQ, + CS_CMD_STOP_SEQ, + CS_CMD_ACTOR_CUE_6_4, + CS_CMD_ACTOR_CUE_7_2, + CS_CMD_ACTOR_CUE_5_4, + CS_CMD_ACTOR_CUE_0_9 = 0x5D, + CS_CMD_ACTOR_CUE_1_11, + CS_CMD_ACTOR_CUE_0_10 = 0x69, + CS_CMD_ACTOR_CUE_2_9, + CS_CMD_ACTOR_CUE_0_11, + CS_CMD_ACTOR_CUE_3_10, + CS_CMD_UNIMPLEMENTED_6D, + CS_CMD_ACTOR_CUE_0_12, + CS_CMD_ACTOR_CUE_7_3, + CS_CMD_UNIMPLEMENTED_70, + CS_CMD_UNIMPLEMENTED_71, + CS_CMD_ACTOR_CUE_7_4, + CS_CMD_ACTOR_CUE_6_5, + CS_CMD_ACTOR_CUE_1_12, + CS_CMD_ACTOR_CUE_2_10, + CS_CMD_ACTOR_CUE_1_13, + CS_CMD_ACTOR_CUE_0_13, + CS_CMD_ACTOR_CUE_1_14, + CS_CMD_ACTOR_CUE_2_11, + CS_CMD_ACTOR_CUE_0_14 = 0x7B, + CS_CMD_FADE_OUT_SEQ, + CS_CMD_ACTOR_CUE_1_15, + CS_CMD_ACTOR_CUE_2_12, + CS_CMD_ACTOR_CUE_3_11, + CS_CMD_ACTOR_CUE_4_7, + CS_CMD_ACTOR_CUE_5_5, + CS_CMD_ACTOR_CUE_6_6, + CS_CMD_ACTOR_CUE_1_16, + CS_CMD_ACTOR_CUE_2_13, + CS_CMD_ACTOR_CUE_3_12, + CS_CMD_ACTOR_CUE_7_5, + CS_CMD_ACTOR_CUE_4_8, + CS_CMD_ACTOR_CUE_5_6, + CS_CMD_ACTOR_CUE_6_7, + CS_CMD_ACTOR_CUE_0_15, + CS_CMD_ACTOR_CUE_0_16, + CS_CMD_TIME, + CS_CMD_ACTOR_CUE_1_17, + CS_CMD_ACTOR_CUE_7_6, + CS_CMD_ACTOR_CUE_9_0, + CS_CMD_ACTOR_CUE_0_17, + CS_CMD_DESTINATION = 0x03E8, + CS_CMD_END = 0xFFFF +}; + +/**** GENERIC ****/ + +class CutsceneOoTSubCommandEntry_GenericCmd : public CutsceneSubCommandEntry +{ +public: + CutsceneOoT_CommandType commandId; + + uint32_t word0 = 0; + uint32_t word1 = 0; + + uint32_t unused1 = 0; + uint32_t unused2 = 0; + uint32_t unused3 = 0; + uint32_t unused4 = 0; + uint32_t unused5 = 0; + uint32_t unused6 = 0; + uint32_t unused7 = 0; + uint32_t unused8 = 0; + uint32_t unused9 = 0; + uint32_t unused10 = 0; + + CutsceneOoTSubCommandEntry_GenericCmd(const std::vector& rawData, + offset_t rawDataIndex, CutsceneOoT_CommandType cmdId); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneOoTCommand_GenericCmd : public CutsceneCommand +{ +public: + CutsceneOoTCommand_GenericCmd(const std::vector& rawData, offset_t rawDataIndex, + CutsceneOoT_CommandType cmdId); + + std::string GetCommandMacro() const override; +}; + +/**** CAMERA ****/ + +class CutsceneOoTCommand_CameraPoint : public CutsceneSubCommandEntry +{ +public: + int8_t continueFlag; + int8_t cameraRoll; + int16_t nextPointFrame; + float viewAngle; + int16_t posX, posY, posZ; + int16_t unused; + + CutsceneOoTCommand_CameraPoint(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneOoTCommand_GenericCameraCmd : public CutsceneCommand +{ +public: + uint16_t base; + uint16_t startFrame; + uint16_t endFrame; + uint16_t unused; + + CutsceneOoTCommand_GenericCameraCmd(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; + + size_t GetCommandSize() const override; +}; + +/**** TRANSITION ****/ + +class CutsceneOoTCommand_Transition : public CutsceneCommand +{ +public: + uint16_t base; + uint16_t startFrame; + uint16_t endFrame; + + CutsceneOoTCommand_Transition(const std::vector& rawData, offset_t rawDataIndex); + + std::string GenerateSourceCode() const override; + size_t GetCommandSize() const override; +}; + +/**** RUMBLE ****/ + +class CutsceneOoTSubCommandEntry_Rumble : public CutsceneSubCommandEntry +{ +public: + uint8_t sourceStrength; + uint8_t duration; + uint8_t decreaseRate; + uint8_t unk_09; + uint8_t unk_0A; + + CutsceneOoTSubCommandEntry_Rumble(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneOoTCommand_Rumble : public CutsceneCommand +{ +public: + CutsceneOoTCommand_Rumble(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; + +/**** TEXT ****/ + +class CutsceneOoTSubCommandEntry_Text : public CutsceneSubCommandEntry +{ +public: + uint16_t type; + uint16_t textId1; + uint16_t textId2; + + CutsceneOoTSubCommandEntry_Text(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneOoTCommand_Text : public CutsceneCommand +{ +public: + CutsceneOoTCommand_Text(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; + +/**** ACTOR CUE ****/ + +class CutsceneOoTSubCommandEntry_ActorCue : public CutsceneSubCommandEntry +{ +public: + uint16_t rotX, rotY, rotZ; + int32_t startPosX, startPosY, startPosZ; + int32_t endPosX, endPosY, endPosZ; + float normalX, normalY, normalZ; + + CutsceneOoTSubCommandEntry_ActorCue(const std::vector& rawData, offset_t rawDataIndex); + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneOoTCommand_ActorCue : public CutsceneCommand +{ +public: + CutsceneOoTCommand_ActorCue(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; + +/**** DESTINATION ****/ + +class CutsceneOoTCommand_Destination : public CutsceneCommand +{ +public: + uint16_t base; + uint16_t startFrame; + uint16_t endFrame; + uint16_t unknown; + + CutsceneOoTCommand_Destination(const std::vector& rawData, offset_t rawDataIndex); + + std::string GenerateSourceCode() const override; + size_t GetCommandSize() const override; +}; diff --git a/ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.cpp b/ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.cpp new file mode 100644 index 000000000..a5ea609af --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.cpp @@ -0,0 +1,128 @@ +#include "Cutscene_Common.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +/* CutsceneSubCommandEntry */ + +CutsceneSubCommandEntry::CutsceneSubCommandEntry(const std::vector& rawData, + offset_t rawDataIndex) +{ + base = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0); + startFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + endFrame = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); + pad = BitConverter::ToUInt16BE(rawData, rawDataIndex + 6); +} + +std::string CutsceneSubCommandEntry::GetBodySourceCode() const +{ + return StringHelper::Sprintf("CMD_HH(0x%04X, 0x%04X), CMD_HH(0x%04X, 0x%04X)", base, startFrame, + endFrame, pad); +} + +size_t CutsceneSubCommandEntry::GetRawSize() const +{ + return 0x08; +} + +/* CutsceneCommand */ + +CutsceneCommand::CutsceneCommand(const std::vector& rawData, offset_t rawDataIndex) +{ + numEntries = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0); +} + +CutsceneCommand::~CutsceneCommand() +{ + for (auto entry : entries) + { + delete entry; + } +} + +std::string CutsceneCommand::GetCommandMacro() const +{ + return StringHelper::Sprintf("CMD_W(0x%08X), CMD_W(0x%08X)", commandID, numEntries); +} + +std::string CutsceneCommand::GenerateSourceCode() const +{ + std::string result; + + result += GetCommandMacro(); + result += ",\n"; + + for (auto& entry : entries) + { + result += " "; + result += entry->GetBodySourceCode(); + result += ",\n"; + } + + return result; +} + +size_t CutsceneCommand::GetCommandSize() const +{ + size_t size = 0; + if (entries.size() > 0) + { + size = entries.at(0)->GetRawSize() * entries.size(); + } + else + { + size = 0x08 * numEntries; + } + return 0x08 + size; +} + +void CutsceneCommand::SetCommandID(uint32_t nCommandID) +{ + commandID = nCommandID; + + for (auto& entry : entries) + { + entry->commandID = commandID; + } +} + +/*** TIME ****/ + +CutsceneSubCommandEntry_SetTime::CutsceneSubCommandEntry_SetTime( + const std::vector& rawData, offset_t rawDataIndex) + : CutsceneSubCommandEntry(rawData, rawDataIndex) +{ + hour = BitConverter::ToUInt8BE(rawData, rawDataIndex + 6); + minute = BitConverter::ToUInt8BE(rawData, rawDataIndex + 7); +} + +std::string CutsceneSubCommandEntry_SetTime::GetBodySourceCode() const +{ + // Note: Both OoT and MM have the first argument unused + return StringHelper::Sprintf("CS_TIME(%i, %i, %i, %i, %i)", base, startFrame, endFrame, hour, + minute); +} + +size_t CutsceneSubCommandEntry_SetTime::GetRawSize() const +{ + return 0x0C; +} + +CutsceneCommand_Time::CutsceneCommand_Time(const std::vector& rawData, + offset_t rawDataIndex) + : CutsceneCommand(rawData, rawDataIndex) +{ + rawDataIndex += 4; + + entries.reserve(numEntries); + for (size_t i = 0; i < numEntries; i++) + { + auto* entry = new CutsceneSubCommandEntry_SetTime(rawData, rawDataIndex); + entries.push_back(entry); + rawDataIndex += entry->GetRawSize(); + } +} + +std::string CutsceneCommand_Time::GetCommandMacro() const +{ + return StringHelper::Sprintf("CS_TIME_LIST(%i)", numEntries); +} diff --git a/ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.h b/ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.h new file mode 100644 index 000000000..9d8dcf63d --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/Cutscene_Common.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include "Declaration.h" + +typedef struct CsCommandListDescriptor +{ + const char* cmdMacro; + const char* args; +} CsCommandListDescriptor; + +class CutsceneSubCommandEntry +{ +public: + uint16_t base; + uint16_t startFrame; + uint16_t endFrame; + uint16_t pad; + + uint32_t commandID; + + CutsceneSubCommandEntry(const std::vector& rawData, offset_t rawDataIndex); + virtual ~CutsceneSubCommandEntry() = default; + + virtual std::string GetBodySourceCode() const; + + virtual size_t GetRawSize() const; +}; + +class CutsceneCommand +{ +public: + uint32_t commandID; + uint32_t commandIndex; + + uint32_t numEntries; + std::vector entries; + + CutsceneCommand(const std::vector& rawData, offset_t rawDataIndex); + virtual ~CutsceneCommand(); + + virtual std::string GetCommandMacro() const; + virtual std::string GenerateSourceCode() const; + virtual size_t GetCommandSize() const; + + virtual void SetCommandID(uint32_t nCommandID); +}; + +/**** TIME ****/ + +class CutsceneSubCommandEntry_SetTime : public CutsceneSubCommandEntry +{ +public: + uint8_t hour; + uint8_t minute; + + CutsceneSubCommandEntry_SetTime(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetBodySourceCode() const override; + + size_t GetRawSize() const override; +}; + +class CutsceneCommand_Time : public CutsceneCommand +{ +public: + CutsceneCommand_Time(const std::vector& rawData, offset_t rawDataIndex); + + std::string GetCommandMacro() const override; +}; diff --git a/ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.cpp b/ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.cpp new file mode 100644 index 000000000..430cc665c --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.cpp @@ -0,0 +1,354 @@ +#include "SkinLimbStructs.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZDisplayList.h" +#include "ZFile.h" + +/* SkinVertex */ + +SkinVertex::SkinVertex(ZFile* nParent) : ZResource(nParent) +{ +} + +void SkinVertex::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + index = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x00); + s = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x02); + t = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x04); + normX = BitConverter::ToInt8BE(rawData, rawDataIndex + 0x06); + normY = BitConverter::ToInt8BE(rawData, rawDataIndex + 0x07); + normZ = BitConverter::ToInt8BE(rawData, rawDataIndex + 0x08); + alpha = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x09); +} + +std::string SkinVertex::GetBodySourceCode() const +{ + return StringHelper::Sprintf("0x%02X, %i, %i, %i, %i, %i, 0x%02X", index, s, t, normX, normY, + normZ, alpha); +} + +std::string SkinVertex::GetSourceTypeName() const +{ + return "SkinVertex"; +} + +ZResourceType SkinVertex::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +size_t SkinVertex::GetRawDataSize() const +{ + return 0x0A; +} + +/* SkinTransformation */ + +SkinTransformation::SkinTransformation(ZFile* nParent) : ZResource(nParent) +{ +} + +void SkinTransformation::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + limbIndex = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x00); + x = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x02); + y = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x04); + z = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x06); + scale = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x08); +} + +std::string SkinTransformation::GetBodySourceCode() const +{ + return StringHelper::Sprintf("0x%02X, %i, %i, %i, 0x%02X", limbIndex, x, y, z, scale); +} + +std::string SkinTransformation::GetSourceTypeName() const +{ + return "SkinTransformation"; +} + +ZResourceType SkinTransformation::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +size_t SkinTransformation::GetRawDataSize() const +{ + return 0x0A; +} + +/* SkinLimbModif */ + +SkinLimbModif::SkinLimbModif(ZFile* nParent) : ZResource(nParent) +{ +} + +void SkinLimbModif::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + vtxCount = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x00); + transformCount = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x02); + unk_4 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x04); + skinVertices = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x08); + limbTransformations = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x0C); + + if (skinVertices != 0 && GETSEGNUM(skinVertices) == parent->segment) + { + uint32_t unk_8_Offset = Seg2Filespace(skinVertices, parent->baseAddress); + + skinVertices_arr.reserve(vtxCount); + for (size_t i = 0; i < vtxCount; i++) + { + SkinVertex skinVertices_data(parent); + skinVertices_data.ExtractFromFile(unk_8_Offset); + skinVertices_arr.push_back(skinVertices_data); + + unk_8_Offset += skinVertices_data.GetRawDataSize(); + } + } + + if (limbTransformations != 0 && GETSEGNUM(skinVertices) == parent->segment) + { + uint32_t unk_C_Offset = Seg2Filespace(limbTransformations, parent->baseAddress); + + limbTransformations_arr.reserve(transformCount); + for (size_t i = 0; i < transformCount; i++) + { + SkinTransformation limbTransformations_data(parent); + limbTransformations_data.ExtractFromFile(unk_C_Offset); + limbTransformations_arr.push_back(limbTransformations_data); + + unk_C_Offset += limbTransformations_data.GetRawDataSize(); + } + } +} + +void SkinLimbModif::DeclareReferences(const std::string& prefix) +{ + std::string varPrefix = prefix; + if (name != "") + varPrefix = name; + + if (skinVertices != 0 && GETSEGNUM(skinVertices) == parent->segment) + { + const auto& res = skinVertices_arr.at(0); + std::string unk_8_Str = res.GetDefaultName(varPrefix); + + size_t arrayItemCnt = skinVertices_arr.size(); + std::string entryStr = ""; + + for (size_t i = 0; i < arrayItemCnt; i++) + { + auto& child = skinVertices_arr[i]; + child.DeclareReferences(varPrefix); + entryStr += StringHelper::Sprintf("\t{ %s },", child.GetBodySourceCode().c_str()); + + if (i < arrayItemCnt - 1) + entryStr += "\n"; + } + + uint32_t skinVertices_Offset = Seg2Filespace(skinVertices, parent->baseAddress); + Declaration* decl = parent->GetDeclaration(skinVertices_Offset); + if (decl == nullptr) + { + parent->AddDeclarationArray(skinVertices_Offset, res.GetDeclarationAlignment(), + arrayItemCnt * res.GetRawDataSize(), + res.GetSourceTypeName(), unk_8_Str, arrayItemCnt, entryStr); + } + else + decl->declBody = entryStr; + } + + if (limbTransformations != 0 && GETSEGNUM(limbTransformations) == parent->segment) + { + const auto& res = limbTransformations_arr.at(0); + std::string unk_C_Str = res.GetDefaultName(varPrefix); + + size_t arrayItemCnt = limbTransformations_arr.size(); + std::string entryStr = ""; + + for (size_t i = 0; i < arrayItemCnt; i++) + { + auto& child = limbTransformations_arr[i]; + child.DeclareReferences(varPrefix); + entryStr += StringHelper::Sprintf("\t{ %s },", child.GetBodySourceCode().c_str()); + + if (i < arrayItemCnt - 1) + entryStr += "\n"; + } + + uint32_t unk_C_Offset = Seg2Filespace(limbTransformations, parent->baseAddress); + Declaration* decl = parent->GetDeclaration(unk_C_Offset); + if (decl == nullptr) + { + parent->AddDeclarationArray(unk_C_Offset, res.GetDeclarationAlignment(), + arrayItemCnt * res.GetRawDataSize(), + res.GetSourceTypeName(), unk_C_Str, arrayItemCnt, entryStr); + } + else + decl->declBody = entryStr; + } +} + +std::string SkinLimbModif::GetBodySourceCode() const +{ + std::string skinVertices_Str; + std::string unk_C_Str; + Globals::Instance->GetSegmentedPtrName(skinVertices, parent, "SkinVertex", skinVertices_Str, parent->workerID); + Globals::Instance->GetSegmentedPtrName(limbTransformations, parent, "SkinTransformation", + unk_C_Str, parent->workerID); + + std::string entryStr = StringHelper::Sprintf("\n\t\tARRAY_COUNTU(%s), ARRAY_COUNTU(%s),\n", + skinVertices_Str.c_str(), unk_C_Str.c_str()); + entryStr += StringHelper::Sprintf("\t\t%i, %s, %s\n\t", unk_4, skinVertices_Str.c_str(), + unk_C_Str.c_str()); + + return entryStr; +} + +std::string SkinLimbModif::GetSourceTypeName() const +{ + return "SkinLimbModif"; +} + +ZResourceType SkinLimbModif::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +size_t SkinLimbModif::GetRawDataSize() const +{ + return 0x10; +} + +/* SkinAnimatedLimbData */ + +SkinAnimatedLimbData::SkinAnimatedLimbData(ZFile* nParent) : ZResource(nParent) +{ +} + +void SkinAnimatedLimbData::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + totalVtxCount = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x00); + limbModifCount = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x02); + limbModifications = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x04); + dlist = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x08); + + if (limbModifications != 0 && GETSEGNUM(limbModifications) == parent->segment) + { + uint32_t limbModifications_Offset = Seg2Filespace(limbModifications, parent->baseAddress); + + limbModifications_arr.reserve(limbModifCount); + for (size_t i = 0; i < limbModifCount; i++) + { + SkinLimbModif limbModifications_data(parent); + limbModifications_data.ExtractFromFile(limbModifications_Offset); + limbModifications_arr.push_back(limbModifications_data); + + limbModifications_Offset += limbModifications_data.GetRawDataSize(); + } + } +} + +void SkinAnimatedLimbData::DeclareReferences(const std::string& prefix) +{ + std::string varPrefix = prefix; + if (name != "") + varPrefix = name; + + ZResource::DeclareReferences(varPrefix); + + if (limbModifications != SEGMENTED_NULL && GETSEGNUM(limbModifications) == parent->segment) + { + const auto& res = limbModifications_arr.at(0); + std::string limbModifications_Str = res.GetDefaultName(varPrefix); + + size_t arrayItemCnt = limbModifications_arr.size(); + std::string entryStr = ""; + + for (size_t i = 0; i < arrayItemCnt; i++) + { + auto& child = limbModifications_arr[i]; + child.DeclareReferences(varPrefix); + entryStr += StringHelper::Sprintf("\t{ %s },", child.GetBodySourceCode().c_str()); + + if (i < arrayItemCnt - 1) + entryStr += "\n"; + } + + uint32_t limbModifications_Offset = Seg2Filespace(limbModifications, parent->baseAddress); + Declaration* decl = parent->GetDeclaration(limbModifications_Offset); + if (decl == nullptr) + { + parent->AddDeclarationArray(limbModifications_Offset, res.GetDeclarationAlignment(), + arrayItemCnt * res.GetRawDataSize(), + res.GetSourceTypeName(), limbModifications_Str, + arrayItemCnt, entryStr); + } + else + decl->declBody = entryStr; + } + + if (dlist != SEGMENTED_NULL && GETSEGNUM(dlist) == parent->segment) + { + uint32_t dlist_Offset = Seg2Filespace(dlist, parent->baseAddress); + + int32_t dlistLength = ZDisplayList::GetDListLength( + parent->GetRawData(), dlist_Offset, + Globals::Instance->game == ZGame::OOT_SW97 ? DListType::F3DEX : DListType::F3DZEX); + ZDisplayList* dlist_data = new ZDisplayList(parent); + dlist_data->ExtractFromBinary(dlist_Offset, dlistLength); + + std::string dListStr = + StringHelper::Sprintf("%sSkinLimbDL_%06X", varPrefix.c_str(), dlist_Offset); + dlist_data->SetName(dListStr); + dlist_data->DeclareVar(varPrefix, ""); + dlist_data->DeclareReferences(varPrefix); + parent->AddResource(dlist_data); + } +} + +std::string SkinAnimatedLimbData::GetBodySourceCode() const +{ + std::string limbModifications_Str; + std::string dlist_Str; + Globals::Instance->GetSegmentedPtrName(limbModifications, parent, "SkinLimbModif", + limbModifications_Str, parent->workerID); + Globals::Instance->GetSegmentedPtrName(dlist, parent, "Gfx", dlist_Str, parent->workerID); + + std::string entryStr = "\n"; + entryStr += StringHelper::Sprintf("\t%i, ARRAY_COUNTU(%s),\n", totalVtxCount, + limbModifications_Str.c_str()); + entryStr += + StringHelper::Sprintf("\t%s, %s\n", limbModifications_Str.c_str(), dlist_Str.c_str()); + + return entryStr; +} + +std::string SkinAnimatedLimbData::GetSourceTypeName() const +{ + return "SkinAnimatedLimbData"; +} + +ZResourceType SkinAnimatedLimbData::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +size_t SkinAnimatedLimbData::GetRawDataSize() const +{ + return 0x0C; +} diff --git a/ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.h b/ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.h new file mode 100644 index 000000000..0bc35a25b --- /dev/null +++ b/ZAPDTR/ZAPD/OtherStructs/SkinLimbStructs.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include + +#include "ZResource.h" +#include "ZDisplayList.h" + +enum class ZLimbSkinType +{ + SkinType_Null, // SkinLimb segment = NULL + SkinType_Animated = 4, // SkinLimb segment = SkinAnimatedLimbData* + SkinType_Normal = 11, // SkinLimb segment = Gfx* +}; + +class SkinVertex : public ZResource +{ +public: + SkinVertex(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +public: + uint16_t index; + int16_t s; + int16_t t; + int8_t normX; + int8_t normY; + int8_t normZ; + uint8_t alpha; +}; + +class SkinTransformation : public ZResource +{ +public: + SkinTransformation(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +public: + uint8_t limbIndex; + int16_t x; + int16_t y; + int16_t z; + uint8_t scale; +}; + +class SkinLimbModif : public ZResource +{ +public: + SkinLimbModif(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + + uint16_t vtxCount; // Number of vertices in this modif entry + uint16_t transformCount; // Length of limbTransformations + uint16_t unk_4; // 0 or 1, used as an index for limbTransformations + segptr_t skinVertices; // SkinVertex* + segptr_t limbTransformations; // SkinTransformation* + + std::vector skinVertices_arr; + std::vector limbTransformations_arr; +}; + +class SkinAnimatedLimbData : public ZResource +{ +public: + SkinAnimatedLimbData(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +public: + uint16_t totalVtxCount; + uint16_t limbModifCount; // Length of limbModifications + segptr_t limbModifications; // SkinLimbModif* + segptr_t dlist; // Gfx* + + std::vector limbModifications_arr; + // ZDisplayList* unk_8_dlist = nullptr; +}; diff --git a/ZAPDTR/ZAPD/OutputFormatter.cpp b/ZAPDTR/ZAPD/OutputFormatter.cpp new file mode 100644 index 000000000..362ef98fc --- /dev/null +++ b/ZAPDTR/ZAPD/OutputFormatter.cpp @@ -0,0 +1,123 @@ +#include "OutputFormatter.h" +#include + +void OutputFormatter::Flush() +{ + //if (!Globals::Instance->otrMode) // OTRTODO: MULTITHREADING + { + if (col > lineLimit && !Globals::Instance->otrMode) + { + str.append(1, '\n'); + str.append(currentIndent, ' '); + + uint32_t newCol = currentIndent + (wordP - word); + + for (uint32_t i = 0; i < wordNests; i++) + nestIndent[nest - i] -= col - newCol; + + col = newCol; + } + else + { + str.append(space, spaceP - space); + } + spaceP = space; + + str.append(word, wordP - word); + wordP = word; + wordNests = 0; + } +} + +int OutputFormatter::Write(const char* buf, int count) +{ + // OTRTODO + //if (!Globals::Instance->singleThreaded) + //return 0; + + for (int i = 0; i < count; i++) + { + char c = buf[i]; + + if (c == ' ' || c == '\t' || c == '\n') + { + if (wordP - word != 0) + { + Flush(); + } + + if (c == '\n') + { + col = 0; + *spaceP++ = c; + } + else if (c == '\t') + { + int n = tabSize - (col % tabSize); + col += n; + for (int j = 0; j < n; j++) + *spaceP++ = ' '; + } + else + { + col++; + *spaceP++ = c; + } + + currentIndent = nestIndent[nest]; + } + else + { + col++; + + if (c == '(') + { + nest++; + nestIndent[nest] = col; + wordNests++; + } + else if (c == ')') + { + if (nest > 0) + nest--; + if (wordNests > 0) + wordNests--; + } + + *wordP++ = c; + } + } + + return count; +} + +int OutputFormatter::Write(const std::string& buf) +{ + return Write(buf.data(), buf.size()); +} + +thread_local OutputFormatter* OutputFormatter::Instance; + +int OutputFormatter::WriteStatic(const char* buf, int count) +{ + return Instance->Write(buf, count); +} + +int (*OutputFormatter::StaticWriter())(const char* buf, int count) +{ + Instance = this; + return &WriteStatic; +} + +OutputFormatter::OutputFormatter(uint32_t tabSize, uint32_t indentation, uint32_t lineLimit) + : tabSize{tabSize}, lineLimit{lineLimit}, col{0}, nest{0}, nestIndent{indentation}, + currentIndent{indentation}, wordNests(0), wordP{word}, spaceP{space} +{ +} + +std::string OutputFormatter::GetOutput() +{ + Flush(); + + return std::move(str); +} diff --git a/ZAPDTR/ZAPD/OutputFormatter.h b/ZAPDTR/ZAPD/OutputFormatter.h new file mode 100644 index 000000000..2fa7a78d8 --- /dev/null +++ b/ZAPDTR/ZAPD/OutputFormatter.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include +#include + +class OutputFormatter +{ +private: + const uint32_t tabSize; + const uint32_t lineLimit; + + uint32_t col; + uint32_t nest; + uint32_t nestIndent[8]; + uint32_t currentIndent; + uint32_t wordNests; + + char word[128]; + char space[128]; + char* wordP; + char* spaceP; + + std::string str; + + void Flush(); + + static thread_local OutputFormatter* Instance; + static int WriteStatic(const char* buf, int count); + +public: + OutputFormatter(uint32_t tabSize = 4, uint32_t indentation = 4, uint32_t lineLimit = 120); + + int (*StaticWriter())(const char* buf, int count); // Must be `int` due to libgfxd + + int Write(const char* buf, int count); + int Write(const std::string& buf); + + std::string GetOutput(); +}; diff --git a/ZAPDTR/ZAPD/Overlays/ZOverlay.cpp b/ZAPDTR/ZAPD/Overlays/ZOverlay.cpp new file mode 100644 index 000000000..168ed2374 --- /dev/null +++ b/ZAPDTR/ZAPD/Overlays/ZOverlay.cpp @@ -0,0 +1,354 @@ +#if 0 +#include "ZOverlay.h" + +#include +#include +#include "Globals.h" +#include "Utils/Directory.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" + +using namespace ELFIO; + +const char* RelocationEntry::GetSectionName() const +{ + switch (sectionType) + { + case SectionType::Text: + return ".text"; + case SectionType::Data: + return ".data"; + case SectionType::RoData: + return ".rodata"; + case SectionType::Bss: + return ".bss"; + case SectionType::ERROR: + return ".ERROR"; + } + assert(!"Oh no :c"); +} + +const char* RelocationEntry::GetRelocTypeName() const +{ + switch (relocationType) + { + case RelocationType::R_MIPS_32: + return "R_MIPS_32"; + case RelocationType::R_MIPS_26: + return "R_MIPS_26"; + case RelocationType::R_MIPS_HI16: + return "R_MIPS_HI16"; + case RelocationType::R_MIPS_LO16: + return "R_MIPS_LO16"; + } + assert(!"Oh no :c"); +} + +ZOverlay::ZOverlay() +{ + name = ""; + entries = std::vector(); +} + +ZOverlay::ZOverlay(const std::string& nName) : ZOverlay() +{ + name = nName; +} + +ZOverlay::~ZOverlay() +{ + for (auto entry : entries) + if (entry) + delete entry; + entries.clear(); +} + +static const std::unordered_set sRelSections = { + ".rel.text", + ".rel.data", + ".rel.rodata", +}; +static const std::unordered_set sSections = { + ".text", ".data", ".symtab", ".rodata", ".rodata.str1.4", ".rodata.cst4", ".rodata.cst8", +}; + +ZOverlay* ZOverlay::FromBuild(fs::path buildPath, fs::path cfgFolderPath) +{ + std::string cfgText = DiskFile::ReadAllText(cfgFolderPath / "overlay.cfg"); + std::vector cfgLines = StringHelper::Split(cfgText, "\n"); + + ZOverlay* ovl = new ZOverlay(StringHelper::Strip(cfgLines[0], "\r")); + ovl->cfgLines = cfgLines; + + int32_t sectionOffs[5] = {0}; + std::vector textRelocs; + std::vector dataRelocs; + std::vector rodataRelocs; + + // get the elf files + std::vector readers; + for (size_t i = 1; i < cfgLines.size(); i++) + { + std::string elfPath = + (buildPath / (cfgLines[i].substr(0, cfgLines[i].size() - 2) + ".o")).string(); + elfio* reader = new elfio(); + + if (!reader->load(elfPath)) + { + // not all files were compiled + for (auto r : readers) + delete r; + readers.clear(); + + delete ovl; + return nullptr; + } + + readers.push_back(reader); + } + + for (size_t curReaderId = 0; curReaderId < readers.size(); curReaderId++) + { + auto& curReader = readers[curReaderId]; + + Elf_Half sec_num = curReader->sections.size(); + for (int32_t i = 0; i < sec_num; i++) + { + section* pSec = curReader->sections[i]; + + if (pSec->get_type() != SHT_REL || + sRelSections.find(pSec->get_name()) == sRelSections.end()) + { + continue; + } + + symbol_section_accessor currentSymbols(*curReader, + curReader->sections[(Elf_Half)pSec->get_link()]); + + SectionType sectionType = GetSectionTypeFromStr(pSec->get_name()); + + if (sectionType == SectionType::ERROR) + { + HANDLE_WARNING(WarningType::Always, "one of the section types returned ERROR", ""); + } + + relocation_section_accessor relocs(*curReader, pSec); + for (Elf_Xword j = 0; j < relocs.get_entries_num(); j++) + { + Elf64_Addr offset = 0; + Elf_Word symbol = 0; + Elf_Word type = 0; + { + Elf_Sxword addend = 0; + relocs.get_entry(j, offset, symbol, type, addend); + } + + std::string curSymName; + Elf_Half curSymShndx = SHN_UNDEF; + { + Elf64_Addr value; + Elf_Xword size; + unsigned char bind; + unsigned char type; + unsigned char other; + currentSymbols.get_symbol(symbol, curSymName, value, size, bind, type, + curSymShndx, other); + } + + // check symbols outside the elf but within the overlay + if (curSymShndx == SHN_UNDEF) + { + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + { + printf("Symbol '%s' doesn't exist in current .o file (%s). Searching...\n", + curSymName.c_str(), cfgLines[curReaderId + 1].c_str()); + } + + for (size_t readerId = 0; readerId < readers.size(); readerId++) + { + auto& reader = readers[readerId]; + + if (curSymShndx != SHN_UNDEF) + break; + + if (reader == curReader) + continue; + + auto sectionData = reader->sections[(Elf_Half)pSec->get_link()]; + curSymShndx = + ovl->FindSymbolInSection(curSymName, sectionData, *reader, readerId); + if (curSymShndx != SHN_UNDEF) + break; + + if (Globals::Instance->gccCompat) + { + // Symbol wasn't found, try checking every section + Elf_Half sec_num = reader->sections.size(); + for (int32_t otherSectionIdx = 0; otherSectionIdx < sec_num; + otherSectionIdx++) + { + if (curSymShndx != SHN_UNDEF) + { + break; + } + + auto sectionDataIter = reader->sections[otherSectionIdx]; + + curSymShndx = ovl->FindSymbolInSection(curSymName, sectionDataIter, + *reader, readerId); + } + } + } + } + + if (curSymShndx != SHN_UNDEF) + { + RelocationType typeConverted = (RelocationType)type; + offset += sectionOffs[static_cast(sectionType)]; + + RelocationEntry* reloc = + new RelocationEntry(sectionType, typeConverted, offset); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + { + printf(".word 0x%08X # %s %s 0x%04X\n", reloc->CalcRelocationWord(), + reloc->GetSectionName(), reloc->GetRelocTypeName(), reloc->offset); + } + + // this is to keep the correct reloc entry order + if (sectionType == SectionType::Text) + textRelocs.push_back(reloc); + if (sectionType == SectionType::Data) + dataRelocs.push_back(reloc); + if (sectionType == SectionType::RoData) + rodataRelocs.push_back(reloc); + } + } + } + + // increase section offsets + for (int32_t i = 0; i < sec_num; i++) + { + section* pSec = curReader->sections[i]; + if (pSec->get_type() == SHT_PROGBITS && + sSections.find(pSec->get_name()) != sSections.end()) + { + SectionType sectionType = GetSectionTypeFromStr(pSec->get_name()); + sectionOffs[static_cast(sectionType)] += pSec->get_size(); + } + } + } + + ovl->entries.reserve(textRelocs.size() + dataRelocs.size() + rodataRelocs.size()); + ovl->entries.insert(ovl->entries.end(), textRelocs.begin(), textRelocs.end()); + ovl->entries.insert(ovl->entries.end(), dataRelocs.begin(), dataRelocs.end()); + ovl->entries.insert(ovl->entries.end(), rodataRelocs.begin(), rodataRelocs.end()); + + for (auto r : readers) + delete r; + readers.clear(); + + return ovl; +} + +std::string ZOverlay::GetSourceOutputCode([[maybe_unused]] const std::string& prefix) +{ + std::string output; + + output += ".section .ovl\n"; + + output += StringHelper::Sprintf("# %sOverlayInfo\n", name.c_str()); + output += StringHelper::Sprintf(".word _%sSegmentTextSize\n", name.c_str()); + output += StringHelper::Sprintf(".word _%sSegmentDataSize\n", name.c_str()); + output += StringHelper::Sprintf(".word _%sSegmentRoDataSize\n", name.c_str()); + output += StringHelper::Sprintf(".word _%sSegmentBssSize\n", name.c_str()); + output += "\n"; + + output += StringHelper::Sprintf(".word %i # reloc_count\n", entries.size()); + + for (size_t i = 0; i < entries.size(); i++) + { + RelocationEntry* reloc = entries[i]; + output += StringHelper::Sprintf(".word 0x%08X # %s %s 0x%04X\n", + reloc->CalcRelocationWord(), reloc->GetSectionName(), + reloc->GetRelocTypeName(), reloc->offset); + } + + size_t offset = (entries.size() * 4) + 20; + + while (offset % 16 != 12) + { + output += ".word 0\n"; + offset += 4; + } + + output += "\n"; + output += + StringHelper::Sprintf(".word 0x%08X # %sOverlayInfoOffset\n", offset + 4, name.c_str()); + return output; +} + +SectionType ZOverlay::GetSectionTypeFromStr(const std::string& sectionName) +{ + if (sectionName == ".rel.text" || sectionName == ".text") + return SectionType::Text; + else if (sectionName == ".rel.data" || sectionName == ".data") + return SectionType::Data; + else if (sectionName == ".rel.rodata" || sectionName == ".rodata" || + sectionName == ".rodata.str1.4" || sectionName == ".rodata.cst4") + return SectionType::RoData; + else if (sectionName == ".rel.bss" || sectionName == ".bss") + return SectionType::Bss; + + return SectionType::ERROR; +} + +ELFIO::Elf_Half ZOverlay::FindSymbolInSection(const std::string& curSymName, + ELFIO::section* sectionData, ELFIO::elfio& reader, + size_t readerId) +{ + if (sectionData == nullptr) + return SHN_UNDEF; + + auto sectionDataName = sectionData->get_name(); + if (sSections.find(sectionDataName) == sSections.end()) + return SHN_UNDEF; + +#ifdef DEVELOPMENT + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + { + printf("\t File '%s' section: %s \n", cfgLines[readerId + 1].c_str(), + sectionDataName.c_str()); + } +#endif + + symbol_section_accessor symbols(reader, sectionData); + + Elf_Xword symbolNum = symbols.get_symbols_num(); + for (Elf_Xword symIdx = 0; symIdx < symbolNum; symIdx++) + { + Elf_Half shndx = SHN_UNDEF; + Elf64_Addr value; + std::string name; + Elf_Xword size; + unsigned char bind; + unsigned char type; + unsigned char other; + + symbols.get_symbol(symIdx, name, value, size, bind, type, shndx, other); + + if (name == curSymName) + { + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + { + printf("\t Symbol '%s' found in '%s' '%s' \n", curSymName.c_str(), + cfgLines[readerId + 1].c_str(), sectionDataName.c_str()); + } + return shndx; + } + } + return SHN_UNDEF; +} +#endif \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Overlays/ZOverlay.h b/ZAPDTR/ZAPD/Overlays/ZOverlay.h new file mode 100644 index 000000000..c1fadddca --- /dev/null +++ b/ZAPDTR/ZAPD/Overlays/ZOverlay.h @@ -0,0 +1,78 @@ +#pragma once + +#if 0 + +#include "Utils/Directory.h" +#include "ZResource.h" +#include "elfio/elfio.hpp" +#include "tinyxml2.h" + +enum class SectionType +{ + Text = 1, + Data = 2, + RoData = 3, + Bss = 4, + ERROR = 255 +}; + +enum class RelocationType +{ + R_MIPS_32 = 2, + R_MIPS_26 = 4, + R_MIPS_HI16 = 5, + R_MIPS_LO16 = 6, +}; + +class RelocationEntry +{ +public: + SectionType sectionType; + RelocationType relocationType; + int32_t offset; + + RelocationEntry(SectionType nSecType, RelocationType nRelType, int32_t nOffset) + { + sectionType = nSecType; + relocationType = nRelType; + offset = nOffset; + } + + uint32_t CalcRelocationWord() + { + uint32_t relocationWord = 0; + + relocationWord |= static_cast(sectionType) << 30; + relocationWord |= static_cast(relocationType) << 24; + relocationWord |= offset; + + return relocationWord; + } + + const char* GetSectionName() const; + const char* GetRelocTypeName() const; +}; + +class ZOverlay +{ +public: + std::string name; + + ZOverlay(const std::string& nName); + ~ZOverlay(); + static ZOverlay* FromBuild(fs::path buildPath, fs::path cfgFolderPath); + std::string GetSourceOutputCode(const std::string& prefix); + +private: + std::vector entries; + std::vector cfgLines; + + ZOverlay(); + + static SectionType GetSectionTypeFromStr(const std::string& sectionName); + // static std::string GetOverlayNameFromElf(ELFIO::elfio& reader); + + ELFIO::Elf_Half FindSymbolInSection(const std::string& curSymName, ELFIO::section* sectionData, + ELFIO::elfio& reader, size_t readerId); +}; +#endif \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/BinaryReader.cpp b/ZAPDTR/ZAPD/Utils/BinaryReader.cpp new file mode 100644 index 000000000..2fdff37ee --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/BinaryReader.cpp @@ -0,0 +1,178 @@ +#include "BinaryReader.h" +#include +#include +#include "Stream.h" + +BinaryReader::BinaryReader(Stream* nStream) +{ + stream.reset(nStream); +} + +BinaryReader::BinaryReader(std::shared_ptr nStream) +{ + stream = nStream; +} + +void BinaryReader::Close() +{ + stream->Close(); +} + +void BinaryReader::SetEndianness(Endianness endianness) +{ + this->endianness = endianness; +} + +Endianness BinaryReader::GetEndianness() const +{ + return endianness; +} + +void BinaryReader::Seek(uint32_t offset, SeekOffsetType seekType) +{ + stream->Seek(offset, seekType); +} + +uint32_t BinaryReader::GetBaseAddress() +{ + return stream->GetBaseAddress(); +} + +void BinaryReader::Read(int32_t length) +{ + stream->Read(length); +} + +void BinaryReader::Read(char* buffer, int32_t length) +{ + stream->Read(buffer, length); +} + +char BinaryReader::ReadChar() +{ + return (char)stream->ReadByte(); +} + +int8_t BinaryReader::ReadByte() +{ + return stream->ReadByte(); +} + +uint8_t BinaryReader::ReadUByte() +{ + return (uint8_t)stream->ReadByte(); +} + +int16_t BinaryReader::ReadInt16() +{ + int16_t result = 0; + + stream->Read((char*)&result, sizeof(int16_t)); + + if (endianness != Endianness::Native) + result = BSWAP16(result); + + return result; +} + +int32_t BinaryReader::ReadInt32() +{ + int32_t result = 0; + + stream->Read((char*)&result, sizeof(int32_t)); + + if (endianness != Endianness::Native) + result = BSWAP32(result); + + return result; +} + +uint16_t BinaryReader::ReadUInt16() +{ + uint16_t result = 0; + + stream->Read((char*)&result, sizeof(uint16_t)); + + if (endianness != Endianness::Native) + result = BSWAP16(result); + + return result; +} + +uint32_t BinaryReader::ReadUInt32() +{ + uint32_t result = 0; + + stream->Read((char*)&result, sizeof(uint32_t)); + + if (endianness != Endianness::Native) + result = BSWAP32(result); + + return result; +} + +uint64_t BinaryReader::ReadUInt64() +{ + uint64_t result = 0; + + stream->Read((char*)&result, sizeof(uint64_t)); + + if (endianness != Endianness::Native) + result = BSWAP64(result); + + return result; +} + +float BinaryReader::ReadSingle() +{ + float result = NAN; + + stream->Read((char*)&result, sizeof(float)); + + if (endianness != Endianness::Native) + { + float tmp; + char* dst = (char*)&tmp; + char* src = (char*)&result; + dst[3] = src[0]; dst[2] = src[1]; dst[1] = src[2]; dst[0] = src[3]; + result = tmp; + } + + if (std::isnan(result)) + throw std::runtime_error("BinaryReader::ReadSingle(): Error reading stream"); + + return result; +} + +double BinaryReader::ReadDouble() +{ + double result = NAN; + + stream->Read((char*)&result, sizeof(double)); + + if (endianness != Endianness::Native) + { + double tmp; + char* dst = (char*)&tmp; + char* src = (char*)&result; + dst[7] = src[0]; dst[6] = src[1]; dst[5] = src[2]; dst[4] = src[3]; + dst[3] = src[4]; dst[2] = src[5]; dst[1] = src[6]; dst[0] = src[7]; + result = tmp; + } + + if (std::isnan(result)) + throw std::runtime_error("BinaryReader::ReadDouble(): Error reading stream"); + + return result; +} + +std::string BinaryReader::ReadString() +{ + std::string res; + int numChars = ReadInt32(); + + for (int i = 0; i < numChars; i++) + res += ReadChar(); + + return res; +} \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/BinaryReader.h b/ZAPDTR/ZAPD/Utils/BinaryReader.h new file mode 100644 index 000000000..f4b37c99d --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/BinaryReader.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include "BitConverter.h" +#include "Stream.h" + +class BinaryReader +{ +public: + BinaryReader(Stream* nStream); + BinaryReader(std::shared_ptr nStream); + + void Close(); + + void SetEndianness(Endianness endianness); + Endianness GetEndianness() const; + + void Seek(uint32_t offset, SeekOffsetType seekType); + uint32_t GetBaseAddress(); + + void Read(int32_t length); + void Read(char* buffer, int32_t length); + char ReadChar(); + int8_t ReadByte(); + int16_t ReadInt16(); + int32_t ReadInt32(); + uint8_t ReadUByte(); + uint16_t ReadUInt16(); + uint32_t ReadUInt32(); + uint64_t ReadUInt64(); + float ReadSingle(); + double ReadDouble(); + std::string ReadString(); + +protected: + std::shared_ptr stream; + Endianness endianness = Endianness::Native; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/BinaryWriter.cpp b/ZAPDTR/ZAPD/Utils/BinaryWriter.cpp new file mode 100644 index 000000000..34bcad0f3 --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/BinaryWriter.cpp @@ -0,0 +1,148 @@ +#include "BinaryWriter.h" + +BinaryWriter::BinaryWriter(Stream* nStream) +{ + stream.reset(nStream); +} + +BinaryWriter::BinaryWriter(std::shared_ptr nStream) +{ + stream = nStream; +} + +void BinaryWriter::SetEndianness(Endianness endianness) +{ + this->endianness = endianness; +} + +void BinaryWriter::Close() +{ + stream->Close(); +} + +std::shared_ptr BinaryWriter::GetStream() +{ + return stream; +} + +uint64_t BinaryWriter::GetBaseAddress() +{ + return stream->GetBaseAddress(); +} + +uint64_t BinaryWriter::GetLength() +{ + return stream->GetLength(); +} + +void BinaryWriter::Seek(int32_t offset, SeekOffsetType seekType) +{ + stream->Seek(offset, seekType); +} + +void BinaryWriter::Write(int8_t value) +{ + stream->Write((char*)&value, sizeof(int8_t)); +} + +void BinaryWriter::Write(uint8_t value) +{ + stream->Write((char*)&value, sizeof(uint8_t)); +} + +void BinaryWriter::Write(int16_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP16(value); + + stream->Write((char*)&value, sizeof(int16_t)); +} + +void BinaryWriter::Write(uint16_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP16(value); + + stream->Write((char*)&value, sizeof(uint16_t)); +} + +void BinaryWriter::Write(int32_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP32(value); + + stream->Write((char*)&value, sizeof(int32_t)); +} + +void BinaryWriter::Write(int32_t valueA, int32_t valueB) +{ + Write(valueA); + Write(valueB); +} + +void BinaryWriter::Write(uint32_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP32(value); + + stream->Write((char*)&value, sizeof(uint32_t)); +} + +void BinaryWriter::Write(int64_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP64(value); + + stream->Write((char*)&value, sizeof(int64_t)); +} + +void BinaryWriter::Write(uint64_t value) +{ + if (endianness != Endianness::Native) + value = BSWAP64(value); + + stream->Write((char*)&value, sizeof(uint64_t)); +} + +void BinaryWriter::Write(float value) +{ + if (endianness != Endianness::Native) + { + float tmp; + char* dst = (char*)&tmp; + char* src = (char*)&value; + dst[3] = src[0]; dst[2] = src[1]; dst[1] = src[2]; dst[0] = src[3]; + value = tmp; + } + + stream->Write((char*)&value, sizeof(float)); +} + +void BinaryWriter::Write(double value) +{ + if (endianness != Endianness::Native) + { + double tmp; + char* dst = (char*)&tmp; + char* src = (char*)&value; + dst[7] = src[0]; dst[6] = src[1]; dst[5] = src[2]; dst[4] = src[3]; + dst[3] = src[4]; dst[2] = src[5]; dst[1] = src[6]; dst[0] = src[7]; + value = tmp; + } + + stream->Write((char*)&value, sizeof(double)); +} + +void BinaryWriter::Write(const std::string& str) +{ + int strLen = str.size(); + Write(strLen); + + for (char c : str) + stream->WriteByte(c); +} + +void BinaryWriter::Write(char* srcBuffer, size_t length) +{ + stream->Write(srcBuffer, length); +} diff --git a/ZAPDTR/ZAPD/Utils/BinaryWriter.h b/ZAPDTR/ZAPD/Utils/BinaryWriter.h new file mode 100644 index 000000000..67c8fcd1e --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/BinaryWriter.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include "BitConverter.h" +#include "Stream.h" + +class BinaryWriter +{ +public: + BinaryWriter(Stream* nStream); + BinaryWriter(std::shared_ptr nStream); + + void SetEndianness(Endianness endianness); + + std::shared_ptr GetStream(); + uint64_t GetBaseAddress(); + uint64_t GetLength(); + void Seek(int32_t offset, SeekOffsetType seekType); + void Close(); + + void Write(int8_t value); + void Write(uint8_t value); + void Write(int16_t value); + void Write(uint16_t value); + void Write(int32_t value); + void Write(int32_t valueA, int32_t valueB); + void Write(uint32_t value); + void Write(int64_t value); + void Write(uint64_t value); + void Write(float value); + void Write(double value); + void Write(const std::string& str); + void Write(char* srcBuffer, size_t length); + +protected: + std::shared_ptr stream; + Endianness endianness = Endianness::Native; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/BitConverter.h b/ZAPDTR/ZAPD/Utils/BitConverter.h new file mode 100644 index 000000000..c90b3dfbd --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/BitConverter.h @@ -0,0 +1,209 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef _MSC_VER +#define BSWAP16 _byteswap_ushort +#define BSWAP32 _byteswap_ulong +#define BSWAP64 _byteswap_uint64 +#else +#define BSWAP16 __builtin_bswap16 +#define BSWAP32 __builtin_bswap32 +#define BSWAP64 __builtin_bswap64 +#endif + +enum class Endianness +{ + Little = 0, + Big = 1, + +#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || defined(__BIG_ENDIAN__) + Native = Big, +#else + Native = Little, +#endif +}; + +class BitConverter +{ +public: + static inline int8_t ToInt8BE(const uint8_t* data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline int8_t ToInt8BE(const std::vector& data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline uint8_t ToUInt8BE(const uint8_t* data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline uint8_t ToUInt8BE(const std::vector& data, int32_t offset) + { + return (uint8_t)data[offset + 0]; + } + + static inline int16_t ToInt16BE(const uint8_t* data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline int16_t ToInt16BE(const std::vector& data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline uint16_t ToUInt16BE(const uint8_t* data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline uint16_t ToUInt16BE(const std::vector& data, int32_t offset) + { + return ((uint16_t)data[offset + 0] << 8) + (uint16_t)data[offset + 1]; + } + + static inline int32_t ToInt32BE(const uint8_t* data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline int32_t ToInt32BE(const std::vector& data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline uint32_t ToUInt32BE(const uint8_t* data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline uint32_t ToUInt32BE(const std::vector& data, int32_t offset) + { + return ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + } + + static inline int64_t ToInt64BE(const uint8_t* data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline int64_t ToInt64BE(const std::vector& data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline uint64_t ToUInt64BE(const uint8_t* data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline uint64_t ToUInt64BE(const std::vector& data, int32_t offset) + { + return ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + } + + static inline float ToFloatBE(const uint8_t* data, int32_t offset) + { + float value; + uint32_t floatData = ((uint32_t)data[offset + 0] << 24) + + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + static_assert(sizeof(uint32_t) == sizeof(float), "expected 32-bit float"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + static inline float ToFloatBE(const std::vector& data, int32_t offset) + { + float value; + uint32_t floatData = ((uint32_t)data[offset + 0] << 24) + + ((uint32_t)data[offset + 1] << 16) + + ((uint32_t)data[offset + 2] << 8) + (uint32_t)data[offset + 3]; + static_assert(sizeof(uint32_t) == sizeof(float), "expected 32-bit float"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + static inline double ToDoubleBE(const uint8_t* data, int32_t offset) + { + double value; + uint64_t floatData = + ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + static_assert(sizeof(uint64_t) == sizeof(double), "expected 64-bit double"); + // Checks if the float format on the platform the ZAPD binary is running on supports the + // same float format as the object file. + static_assert(std::numeric_limits::is_iec559, + "expected IEC559 floats on host machine"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + static inline double ToDoubleBE(const std::vector& data, int32_t offset) + { + double value; + uint64_t floatData = + ((uint64_t)data[offset + 0] << 56) + ((uint64_t)data[offset + 1] << 48) + + ((uint64_t)data[offset + 2] << 40) + ((uint64_t)data[offset + 3] << 32) + + ((uint64_t)data[offset + 4] << 24) + ((uint64_t)data[offset + 5] << 16) + + ((uint64_t)data[offset + 6] << 8) + ((uint64_t)data[offset + 7]); + static_assert(sizeof(uint64_t) == sizeof(double), "expected 64-bit double"); + // Checks if the float format on the platform the ZAPD binary is running on supports the + // same float format as the object file. + static_assert(std::numeric_limits::is_iec559, + "expected IEC559 doubles on host machine"); + std::memcpy(&value, &floatData, sizeof(value)); + return value; + } + + // Rewrites the rom data in-place to be in BigEndian/z64 format + static inline void RomToBigEndian(uint8_t* rom, size_t romSize) { + if (romSize <= 0) { + return; + } + + // Use the first byte to determine byte order + uint8_t firstByte = rom[0]; + + switch (firstByte) { + case 0x37: // v64 + for (size_t pos = 0; pos < (romSize / 2); pos++) { + ((uint16_t*)rom)[pos] = ToUInt16BE(rom, pos * 2); + } + break; + case 0x40: // n64 + for (size_t pos = 0; pos < (romSize / 4); pos++) { + ((uint32_t*)rom)[pos] = ToUInt32BE(rom, pos * 4); + } + break; + case 0x80: // z64 + break; // Already BE, no need to swap + } + } +}; diff --git a/ZAPDTR/ZAPD/Utils/Directory.h b/ZAPDTR/ZAPD/Utils/Directory.h new file mode 100644 index 000000000..ea792d8da --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/Directory.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include "StringHelper.h" +#include + +#if __has_include() +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif + +#undef GetCurrentDirectory +#undef CreateDirectory + +class Directory +{ +public: + #ifndef PATH_HACK + static std::string GetCurrentDirectory() { return fs::current_path().string(); } + #endif + + static bool Exists(const fs::path& path) { return fs::exists(path); } + + // Stupid hack because of Windows.h + static void MakeDirectory(const std::string& path) + { + CreateDirectory(path); + } + + static void CreateDirectory(const std::string& path) + { + try + { + fs::create_directories(path); + } + catch (...) + { + } + } + + static std::vector ListFiles(const std::string& dir) + { + std::vector lst; + + if (Exists(dir)) + { + for (auto& p : fs::recursive_directory_iterator(dir)) + { + if (!p.is_directory()) + lst.push_back(p.path().generic_string()); + } + } + + return lst; + } +}; diff --git a/ZAPDTR/ZAPD/Utils/DiskFile.h b/ZAPDTR/ZAPD/Utils/DiskFile.h new file mode 100644 index 000000000..d3ec03cf9 --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/DiskFile.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "Path.h" +#include "Utils/StringHelper.h" +#include "Utils/Directory.h" + + +class DiskFile +{ +public: + static bool Exists(const fs::path& filePath) + { + std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate); + return file.good(); + } + + static std::vector ReadAllBytes(const fs::path& filePath) + { + std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate); + + if (!file) + return std::vector(); + + int32_t fileSize = (int32_t)file.tellg(); + file.seekg(0); + char* data = new char[fileSize]; + file.read(data, fileSize); + std::vector result = std::vector(data, data + fileSize); + delete[] data; + + return result; + }; + + static std::string ReadAllText(const fs::path& filePath) + { + std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate); + int32_t fileSize = (int32_t)file.tellg(); + file.seekg(0); + char* data = new char[fileSize + 1]; + memset(data, 0, fileSize + 1); + file.read(data, fileSize); + std::string str = std::string((const char*)data); + delete[] data; + + return str; + }; + + static std::vector ReadAllLines(const fs::path& filePath) + { + std::string text = ReadAllText(filePath); + std::vector lines = StringHelper::Split(text, "\n"); + + return lines; + }; + + static void WriteAllBytes(const fs::path& filePath, const std::vector& data) + { + std::ofstream file(filePath, std::ios::binary); + file.write((char*)data.data(), data.size()); + }; + + static void WriteAllBytes(const std::string& filePath, const std::vector& data) + { + if (!Directory::Exists(Path::GetDirectoryName(filePath))) { + Directory::MakeDirectory(Path::GetDirectoryName(filePath).string()); + } + + std::ofstream file(filePath, std::ios::binary); + file.write((char*)data.data(), data.size()); + }; + + static void WriteAllBytes(const std::string& filePath, const char* data, int dataSize) + { + std::ofstream file(filePath, std::ios::binary); + file.write((char*)data, dataSize); + }; + + static void WriteAllText(const fs::path& filePath, const std::string& text) + { + if (!Directory::Exists(Path::GetDirectoryName(filePath))) { + Directory::MakeDirectory(Path::GetDirectoryName(filePath).string()); + } + std::ofstream file(filePath, std::ios::out); + file.write(text.c_str(), text.size()); + } +}; diff --git a/ZAPDTR/ZAPD/Utils/MemoryStream.cpp b/ZAPDTR/ZAPD/Utils/MemoryStream.cpp new file mode 100644 index 000000000..1c70c007f --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/MemoryStream.cpp @@ -0,0 +1,97 @@ +#include "MemoryStream.h" +#include + +#ifndef _MSC_VER +#define memcpy_s(dest, destSize, source, sourceSize) memcpy(dest, source, destSize) +#endif + +MemoryStream::MemoryStream() +{ + buffer = std::vector(); + //buffer.reserve(1024 * 16); + bufferSize = 0; + baseAddress = 0; +} + +MemoryStream::MemoryStream(char* nBuffer, size_t nBufferSize) : MemoryStream() +{ + buffer = std::vector(nBuffer, nBuffer + nBufferSize); + bufferSize = nBufferSize; + baseAddress = 0; +} + +MemoryStream::~MemoryStream() +{ +} + +uint64_t MemoryStream::GetLength() +{ + return buffer.size(); +} + +void MemoryStream::Seek(int32_t offset, SeekOffsetType seekType) +{ + if (seekType == SeekOffsetType::Start) + baseAddress = offset; + else if (seekType == SeekOffsetType::Current) + baseAddress += offset; + else if (seekType == SeekOffsetType::End) + baseAddress = bufferSize - 1 - offset; +} + +std::unique_ptr MemoryStream::Read(size_t length) +{ + std::unique_ptr result = std::make_unique(length); + + memcpy_s(result.get(), length, &buffer[baseAddress], length); + baseAddress += length; + + return result; +} + +void MemoryStream::Read(const char* dest, size_t length) +{ + memcpy_s((void*)dest, length, &buffer[baseAddress], length); + baseAddress += length; +} + +int8_t MemoryStream::ReadByte() +{ + return buffer[baseAddress++]; +} + +void MemoryStream::Write(char* srcBuffer, size_t length) +{ + if (baseAddress + length >= buffer.size()) + { + buffer.resize(baseAddress + length); + bufferSize += length; + } + + memcpy_s(&buffer[baseAddress], length, srcBuffer, length); + baseAddress += length; +} + +void MemoryStream::WriteByte(int8_t value) +{ + if (baseAddress >= buffer.size()) + { + buffer.resize(baseAddress + 1); + bufferSize = baseAddress; + } + + buffer[baseAddress++] = value; +} + +std::vector MemoryStream::ToVector() +{ + return buffer; +} + +void MemoryStream::Flush() +{ +} + +void MemoryStream::Close() +{ +} \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/MemoryStream.h b/ZAPDTR/ZAPD/Utils/MemoryStream.h new file mode 100644 index 000000000..5a17bb0c3 --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/MemoryStream.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "Stream.h" + +class MemoryStream : public Stream +{ +public: + MemoryStream(); + MemoryStream(char* nBuffer, size_t nBufferSize); + ~MemoryStream(); + + uint64_t GetLength() override; + + void Seek(int32_t offset, SeekOffsetType seekType) override; + + std::unique_ptr Read(size_t length) override; + void Read(const char* dest, size_t length) override; + int8_t ReadByte() override; + + void Write(char* srcBuffer, size_t length) override; + void WriteByte(int8_t value) override; + + std::vector ToVector(); + + void Flush() override; + void Close() override; + +protected: + std::vector buffer; + std::size_t bufferSize; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/Path.h b/ZAPDTR/ZAPD/Utils/Path.h new file mode 100644 index 000000000..0f7ef2743 --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/Path.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include "Utils/StringHelper.h" + +#if __has_include() +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif + +class Path +{ +public: + static std::string GetFileName(const fs::path& input) + { + // https://en.cppreference.com/w/cpp/filesystem/path/filename + return input.filename().string(); + }; + + static std::string GetFileNameWithoutExtension(const fs::path& input) + { + // https://en.cppreference.com/w/cpp/filesystem/path/stem + return input.stem().string(); + }; + + static std::string GetFileNameExtension(const std::string& input) + { + return input.substr(input.find_last_of("."), input.length()); + }; + + static fs::path GetPath(const std::string& input) + { + std::vector split = StringHelper::Split(input, "/"); + fs::path output; + + for (std::string str : split) + { + if (str.find_last_of(".") == std::string::npos) + output /= str; + } + + return output; + }; + + static fs::path GetDirectoryName(const fs::path& path) { return path.parent_path(); }; +}; diff --git a/ZAPDTR/ZAPD/Utils/Stream.h b/ZAPDTR/ZAPD/Utils/Stream.h new file mode 100644 index 000000000..e72d794b6 --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/Stream.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +enum class SeekOffsetType +{ + Start, + Current, + End +}; + +class Stream +{ +public: + virtual ~Stream() = default; + virtual uint64_t GetLength() = 0; + uint64_t GetBaseAddress() { return baseAddress; } + + virtual void Seek(int32_t offset, SeekOffsetType seekType) = 0; + + virtual std::unique_ptr Read(size_t length) = 0; + virtual void Read(const char* dest, size_t length) = 0; + virtual int8_t ReadByte() = 0; + + virtual void Write(char* destBuffer, size_t length) = 0; + virtual void WriteByte(int8_t value) = 0; + + virtual void Flush() = 0; + virtual void Close() = 0; + +protected: + uint64_t baseAddress; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/StringHelper.cpp b/ZAPDTR/ZAPD/Utils/StringHelper.cpp new file mode 100644 index 000000000..c55580ef1 --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/StringHelper.cpp @@ -0,0 +1,191 @@ +#include "StringHelper.h" + +#if (_MSC_VER) +#pragma optimize("2", on) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef _MSC_VER +#define vsprintf_s vsprintf +#endif + +std::vector StringHelper::Split(std::string s, const std::string& delimiter) +{ + size_t pos_start = 0, pos_end, delim_len = delimiter.length(); + std::string token; + std::vector res; + + while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + res.push_back(token); + } + + res.push_back(s.substr(pos_start)); + return res; +} + +std::vector StringHelper::Split(std::string_view s, const std::string& delimiter) +{ + size_t pos_start = 0, pos_end, delim_len = delimiter.length(); + std::string_view token; + std::vector res; + + while ((pos_end = s.find(delimiter, pos_start)) != std::string_view::npos) + { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + res.push_back(token); + } + + res.push_back(s.substr(pos_start)); + return res; +} + +std::string StringHelper::Strip(std::string s, const std::string& delimiter) +{ + size_t pos = 0; + std::string token; + + while ((pos = s.find(delimiter)) != std::string::npos) + { + token = s.substr(0, pos); + s.erase(pos, pos + delimiter.length()); + } + + return s; +} + +std::string StringHelper::Replace(std::string str, const std::string& from, + const std::string& to) +{ + size_t start_pos = str.find(from); + + while (start_pos != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos = str.find(from); + } + + return str; +} + +void StringHelper::ReplaceOriginal(std::string& str, const std::string& from, const std::string& to) +{ + size_t start_pos = str.find(from); + + while (start_pos != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos = str.find(from); + } +} + +bool StringHelper::StartsWith(const std::string& s, const std::string& input) +{ +#if __cplusplus >= 202002L + return s.starts_with(input.c_str()); +#else + return s.rfind(input, 0) == 0; +#endif +} + +bool StringHelper::Contains(const std::string& s, const std::string& input) +{ + return s.find(input) != std::string::npos; +} + +bool StringHelper::EndsWith(const std::string& s, const std::string& input) +{ + size_t inputLen = strlen(input.c_str()); + return s.rfind(input) == (s.size() - inputLen); +} + +std::string StringHelper::Sprintf(const char* format, ...) +{ + char buffer[32768]; + // char buffer[2048]; + std::string output; + va_list va; + + va_start(va, format); + vsprintf_s(buffer, format, va); + va_end(va); + + output = buffer; + return output; +} + +std::string StringHelper::Implode(std::vector& elements, + const char* const separator) +{ + return ""; + + // return std::accumulate(std::begin(elements), std::end(elements), std::string(), + //[separator](std::string& ss, std::string& s) { + // return ss.empty() ? s : ss + separator + s; + //}); +} + +int64_t StringHelper::StrToL(const std::string& str, int32_t base) +{ + return std::strtoull(str.c_str(), nullptr, base); +} + +std::string StringHelper::BoolStr(bool b) +{ + return b ? "true" : "false"; +} + +bool StringHelper::HasOnlyDigits(const std::string& str) +{ + return std::all_of(str.begin(), str.end(), ::isdigit); +} + +// Validate a hex string based on the c89 standard +// https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Integer-Constants +bool StringHelper::IsValidHex(std::string_view str) +{ + if (str.length() < 3) + { + return false; + } + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + { + return std::all_of(str.begin() + 2, str.end(), ::isxdigit); + } + return false; +} + + +bool StringHelper::IsValidHex(const std::string& str) +{ + return IsValidHex(std::string_view(str.c_str())); +} + +bool StringHelper::IsValidOffset(std::string_view str) +{ + if (str.length() == 1) + { + // 0 is a valid offset + return isdigit(str[0]); + } + return IsValidHex(str); +} + +bool StringHelper::IsValidOffset(const std::string& str) +{ + if (str.length() == 1) + { + // 0 is a valid offset + return isdigit(str[0]); + } + return IsValidHex(str); +} + + +bool StringHelper::IEquals(const std::string& a, const std::string& b) +{ + return std::equal(a.begin(), a.end(), b.begin(), b.end(), + [](char a, char b) { return tolower(a) == tolower(b); }); +} diff --git a/ZAPDTR/ZAPD/Utils/StringHelper.h b/ZAPDTR/ZAPD/Utils/StringHelper.h new file mode 100644 index 000000000..0fc58d35a --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/StringHelper.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +class StringHelper +{ +public: + static std::vector Split(std::string s, const std::string& delimiter); + static std::vector Split(std::string_view s, const std::string& delimiter); + static std::string Strip(std::string s, const std::string& delimiter); + static std::string Replace(std::string str, const std::string& from, const std::string& to); + static void ReplaceOriginal(std::string& str, const std::string& from, const std::string& to); + static bool StartsWith(const std::string& s, const std::string& input); + static bool Contains(const std::string& s, const std::string& input); + static bool EndsWith(const std::string& s, const std::string& input); + static std::string Sprintf(const char* format, ...); + static std::string Implode(std::vector& elements, const char* const separator); + static int64_t StrToL(const std::string& str, int32_t base = 10); + static std::string BoolStr(bool b); + static bool HasOnlyDigits(const std::string& str); + static bool IsValidHex(std::string_view str); + static bool IsValidHex(const std::string& str); + static bool IsValidOffset(std::string_view str); + static bool IsValidOffset(const std::string& str); + static bool IEquals(const std::string& a, const std::string& b); +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/Utils/vt.h b/ZAPDTR/ZAPD/Utils/vt.h new file mode 100644 index 000000000..23f424442 --- /dev/null +++ b/ZAPDTR/ZAPD/Utils/vt.h @@ -0,0 +1,45 @@ +#ifndef VT_H +#define VT_H + +// clang-format off +#define VT_COLOR_BLACK 0 +#define VT_COLOR_RED 1 +#define VT_COLOR_GREEN 2 +#define VT_COLOR_YELLOW 3 +#define VT_COLOR_BLUE 4 +#define VT_COLOR_PURPLE 5 +#define VT_COLOR_CYAN 6 +#define VT_COLOR_WHITE 7 +#define VT_COLOR_LIGHTGRAY 8 +#define VT_COLOR_DARKGRAY 9 + +#define VT_COLOR_FOREGROUND 3 +#define VT_COLOR_BACKGROUND 4 +// clang-format on + +#define VT_COLOR_EXPAND0(type, color) #type #color +#define VT_COLOR_EXPAND1(type, color) VT_COLOR_EXPAND0(type, color) +#define VT_COLOR(type, color) VT_COLOR_EXPAND1(VT_COLOR_##type, VT_COLOR_##color) + +#define VT_ESC "\x1b" +#define VT_CSI "[" +#define VT_CUP(x, y) VT_ESC VT_CSI y ";" x "H" +#define VT_ED(n) VT_ESC VT_CSI #n "J" +#define VT_SGR(n) VT_ESC VT_CSI n "m" + +// Add more macros if necessary +#define VT_COL(back, fore) VT_SGR(VT_COLOR(BACKGROUND, back) ";" VT_COLOR(FOREGROUND, fore)) +#define VT_FGCOL(color) VT_SGR(VT_COLOR(FOREGROUND, color)) +#define VT_BGCOL(color) VT_SGR(VT_COLOR(BACKGROUND, color)) + +// Bold +#define VT_BOLD "1" + +// Bold color support +#define VT_BOLD_FGCOL(color) VT_SGR(VT_BOLD ";" VT_COLOR(FOREGROUND, color)) +#define VT_BOLD_BGCOL(color) VT_SGR(VT_BOLD ";" VT_COLOR(BACKGROUND, color)) + +#define VT_RST VT_SGR("") +#define VT_CLS VT_ED(2) + +#endif diff --git a/ZAPDTR/ZAPD/WarningHandler.cpp b/ZAPDTR/ZAPD/WarningHandler.cpp new file mode 100644 index 000000000..f416a5b80 --- /dev/null +++ b/ZAPDTR/ZAPD/WarningHandler.cpp @@ -0,0 +1,455 @@ +/** + * ZAPD Warning- and Error-handling system + * ======================================= + * + * This provides a common standard way to write ZAPD warnings/errors, which should be used for all + * such. It will pretty-print them in a uniform way, with styles defined in the header. + * + * Warnings/errors should be constructed using the macros given in the header; there are now plenty + * of examples in the codebase of how to do this. Their purposes are noted above each category in + * the header. Each warning has a type, one of the ones in warningStringToInitMap, or + * WarningType::Always, which is used for warnings that cannot be disabled and do not display a + * type. + * + * Currently there are three levels of alert a warning can have: + * - Off (does not display anything) + * - Warn (print a warning but continue processing) + * - Err (behave like an error, i.e. print and throw an exception to crash ZAPD when occurs) + * + * Flag use: + * - -Wfoo enables warnings of type foo + * - -Wno-foo disables warnings of type foo + * - -Werror=foo escalates foo to behave like an error + * - -Weverything enables all warnings + * - -Werror escalates all enabled warnings to errors + * + * Errors do not have types, and will always throw an exception; they cannot be disabled. + * + * Format + * === + * Each printed warning/error contains the same three sections: + * - Preamble: automatically generated; the content varies depending on category. It will print the + * file and function that the warning is from, and information about the files being processed + * or extracted. + * - Header: begins with 'warning: ' or 'error:', should contain essential information about the + * warning/error, ends with the warning type if applicable. Printed with emphasis to make it + * stand out. Does not start with a capital letter or end with a '.' + * - Body (optional): indented, should contain further diagnostic information useful for identifying + * and fixing the warning/error. Can be a sentence with captialisation and '.' on the end. + * + * Please think of what the end user will find most useful when writing the header and body, and try + * to keep it brief without sacrificing important information! Also remember that if the user is + * only looking at stderr, they will normally have no other context. + * + * Warning vs error + * === + * The principle that we have operated on so far is + * - issue a warning if ZAPD will still be able to produce a valid, compilable C file that will + * match + * - if this cannot happen, use an error. + * but at the end of the day, it is up to the programmer's discretion what it should be possible to + * disable. + * + * Documentation + * === + * Remember that all warnings also need to be documented in the README.md. The help is generated + * automatically. + */ +#include "WarningHandler.h" + +#include +#include "Globals.h" +#include "Utils/StringHelper.h" + +typedef struct +{ + WarningType type; + WarningLevel defaultLevel; + std::string description; +} WarningInfoInit; + +typedef struct +{ + WarningLevel level; + std::string name; + std::string description; +} WarningInfo; + +/** + * Master list of all default warning types and features + * + * To add a warning type, fill in a new row of this map. Think carefully about what its default + * level should be, and try and make the description both brief and informative: it is used in the + * help message, so again, think about what the end user needs to know. + */ +// clang-format off +static const std::unordered_map warningStringToInitMap = { + {"deprecated", {WarningType::Deprecated, +#ifdef DEPRECATION_ON + WarningLevel::Warn, +#else + WarningLevel::Off, +#endif + "Deprecated features"}}, + {"unaccounted", {WarningType::Unaccounted, WarningLevel::Off, "Large blocks of unaccounted"}}, + {"missing-offsets", {WarningType::MissingOffsets, WarningLevel::Warn, "Offset attribute missing in XML tag"}}, + {"intersection", {WarningType::Intersection, WarningLevel::Warn, "Two assets intersect"}}, + {"missing-attribute", {WarningType::MissingAttribute, WarningLevel::Warn, "Required attribute missing in XML tag"}}, + {"invalid-attribute-value", {WarningType::InvalidAttributeValue, WarningLevel::Err, "Attribute declared in XML is wrong"}}, + {"unknown-attribute", {WarningType::UnknownAttribute, WarningLevel::Warn, "Unknown attribute in XML entry tag"}}, + {"invalid-xml", {WarningType::InvalidXML, WarningLevel::Err, "XML has syntax errors"}}, + {"invalid-jpeg", {WarningType::InvalidJPEG, WarningLevel::Err, "JPEG file does not conform to the game's format requirements"}}, + {"invalid-png", {WarningType::InvalidPNG, WarningLevel::Err, "Issues arising when processing PNG data"}}, + {"invalid-extracted-data", {WarningType::InvalidExtractedData, WarningLevel::Err, "Extracted data does not have correct form"}}, + {"missing-segment", {WarningType::MissingSegment, WarningLevel::Warn, "Segment not given in File tag in XML"}}, + {"hardcoded-generic-pointer", {WarningType::HardcodedGenericPointer, WarningLevel::Off, "A generic segmented pointer must be produced"}}, + {"hardcoded-pointer", {WarningType::HardcodedPointer, WarningLevel::Warn, "ZAPD lacks the info to make a symbol, so must output a hardcoded pointer"}}, + {"not-implemented", {WarningType::NotImplemented, WarningLevel::Warn, "ZAPD does not currently support this feature"}}, +}; + +/** + * Map constructed at runtime to contain the warning features as set by the user using -W flags. + */ +static std::unordered_map warningTypeToInfoMap; + +void WarningHandler::ConstructTypeToInfoMap() { + for (auto& entry : warningStringToInitMap) { + warningTypeToInfoMap[entry.second.type] = {entry.second.defaultLevel, entry.first, entry.second.description}; + } + warningTypeToInfoMap[WarningType::Always] = {WarningLevel::Warn, "always", "you shouldn't be reading this"}; + assert(warningTypeToInfoMap.size() == static_cast(WarningType::Max)); +} + +/** + * Initialises the main warning type map and reads flags passed to set each warning type's level. + */ +void WarningHandler::Init(int argc, char* argv[]) { + ConstructTypeToInfoMap(); + + bool werror = false; + for (int i = 1; i < argc; i++) { + // If it doesn't start with "-W" skip it. + if (argv[i][0] != '-' || argv[i][1] != 'W' || argv[i][2] == '\0') { + continue; + } + + WarningLevel warningTypeOn = WarningLevel::Warn; + size_t startingIndex = 2; + + // "-Wno-" + if (argv[i][2] == 'n' && argv[i][3] == 'o' && argv[i][4] == '-' && argv[i][5] != '\0') { + warningTypeOn = WarningLevel::Off; + startingIndex = 5; + } + + // Read starting after the "-W" or "-Wno-" + std::string_view currentArgv = &argv[i][startingIndex]; + + if (currentArgv == "error") { + werror = warningTypeOn != WarningLevel::Off; + } else if (currentArgv == "everything") { + for (auto& it: warningTypeToInfoMap) { + if (it.second.level <= WarningLevel::Warn) { + it.second.level = warningTypeOn; + } + } + } else { + // "-Werror=" / "-Wno-error=" parser + if (currentArgv.rfind("error=", 0) == 0) { + // Read starting after the "error=" part + currentArgv = &argv[i][startingIndex + 6]; + warningTypeOn = warningTypeOn != WarningLevel::Off ? WarningLevel::Err : WarningLevel::Warn; + } + + auto it = warningStringToInitMap.find(std::string(currentArgv)); + if (it != warningStringToInitMap.end()) { + warningTypeToInfoMap[it->second.type].level = warningTypeOn; + } + else { + HANDLE_WARNING(WarningType::Always, StringHelper::Sprintf("unknown warning flag '%s'", argv[i]), ""); + } + } + } + + if (werror) { + for (auto& it: warningTypeToInfoMap) { + if (it.second.level >= WarningLevel::Warn) { + it.second.level = WarningLevel::Err; + } + } + } +} + +bool WarningHandler::IsWarningEnabled(WarningType warnType) { + assert(static_cast(warnType) >= 0 && warnType < WarningType::Max); + + return warningTypeToInfoMap.at(warnType).level != WarningLevel::Off; +} + +bool WarningHandler::WasElevatedToError(WarningType warnType) { + assert(static_cast(warnType) >= 0 && warnType < WarningType::Max); + + if (!IsWarningEnabled(warnType)) { + return false; + } + + return warningTypeToInfoMap.at(warnType).level >= WarningLevel::Err; +} + +/** + * Print file/line/function info for debugging + */ +void WarningHandler::FunctionPreamble(const char* filename, int32_t line, const char* function) { + bool forcePrint = false; + +#ifdef DEVELOPMENT + forcePrint = true; +#endif + + fprintf(stderr, "\n"); + + if (forcePrint || Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) { + fprintf(stderr, "%s:%i: in function <%s>:\n", filename, line, function); + } +} + +/** + * Print the information about the file(s) being processed (XML for extraction, png etc. for building) + */ +void WarningHandler::ProcessedFilePreamble() { + if (Globals::Instance->inputPath != "") { + fprintf(stderr, "When processing file %s: ", Globals::Instance->inputPath.c_str()); + } +} + +/** + * Print information about the binary file being extracted + */ +void WarningHandler::ExtractedFilePreamble(const ZFile *parent, const ZResource* res, const uint32_t offset) { + fprintf(stderr, "in input binary file %s, ", parent->GetName().c_str()); + if (res != nullptr) { + fprintf(stderr, "resource '%s' at ", res->GetName().c_str()); + } + if (offset != static_cast(-1)) { + fprintf(stderr, "offset 0x%06X:", offset); + } + fprintf(stderr, "\n\t"); +} + +/** + * Construct the rest of the message, after warning:/error. The message is filled in one character at a time, with indents added after newlines + */ +std::string WarningHandler::ConstructMessage(std::string message, const std::string& header, const std::string& body) { + message.reserve(message.size() + header.size() + body.size() + 10 * (sizeof(HANG_INDT) - 1)); + message += StringHelper::Sprintf(HILITE("%s"), header.c_str()); + message += "\n"; + + if (body == "") { + return message; + } + + message += HANG_INDT; + for (const char* ptr = body.c_str(); *ptr != '\0'; ptr++) { + message += *ptr; + if (*ptr == '\n') { + message += HANG_INDT; + } + } + message += "\n"; + + return message; +} + +/* Error module functions */ + +void WarningHandler::PrintErrorAndThrow(const std::string& header, const std::string& body) { + std::string errorMsg = ERR_FMT("error: "); + throw std::runtime_error(ConstructMessage(errorMsg, header, body)); +} + +/* Error types, to be used via the macros */ + +void WarningHandler::ErrorType(WarningType warnType, const std::string& header, const std::string& body) { + std::string headerMsg = header; + + for (const auto& iter: warningStringToInitMap) { + if (iter.second.type == warnType) { + headerMsg += StringHelper::Sprintf(" [%s]", iter.first.c_str()); + } + } + + PrintErrorAndThrow(headerMsg, body); +} + +void WarningHandler::Error_Plain(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) { + FunctionPreamble(filename, line, function); + + ErrorType(warnType, header, body); +} + +void WarningHandler::Error_Process(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) { + FunctionPreamble(filename, line, function); + ProcessedFilePreamble(); + + ErrorType(warnType, header, body); +} + +void WarningHandler::Error_Resource(const char* filename, int32_t line, const char* function, WarningType warnType, const ZFile *parent, const ZResource* res, const uint32_t offset, const std::string& header, const std::string& body) { + assert(parent != nullptr); + + FunctionPreamble(filename, line, function); + ProcessedFilePreamble(); + ExtractedFilePreamble(parent, res, offset); + + ErrorType(warnType, header, body); +} + +/* Warning module functions */ + +void WarningHandler::PrintWarningBody(const std::string& header, const std::string& body) { + std::string errorMsg = WARN_FMT("warning: "); + fprintf(stderr, "%s", ConstructMessage(errorMsg, header, body).c_str()); +} + +void WarningHandler::WarningTypeAndChooseEscalate(WarningType warnType, const std::string& header, const std::string& body) { + std::string headerMsg = header; + + for (const auto& iter: warningStringToInitMap) { + if (iter.second.type == warnType) { + headerMsg += StringHelper::Sprintf(" [-W%s]", iter.first.c_str()); + } + } + + if (WasElevatedToError(warnType)) { + PrintErrorAndThrow(headerMsg, body); + } else { + PrintWarningBody(headerMsg, body); + } +} + + +/* Warning types, to be used via the macros */ + +void WarningHandler::Warning_Plain(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) { + if (!IsWarningEnabled(warnType)) { + return; + } + + FunctionPreamble(filename, line, function); + + WarningTypeAndChooseEscalate(warnType, header, body); +} + +void WarningHandler::Warning_Process(const char* filename, int32_t line, const char* function, WarningType warnType, const std::string& header, const std::string& body) { + if (!IsWarningEnabled(warnType)) { + return; + } + + FunctionPreamble(filename, line, function); + ProcessedFilePreamble(); + + WarningTypeAndChooseEscalate(warnType, header, body); +} + +void WarningHandler::Warning_Resource(const char* filename, int32_t line, const char* function, WarningType warnType, const ZFile *parent, const ZResource* res, const uint32_t offset, const std::string& header, const std::string& body) { + assert(parent != nullptr); + + if (!IsWarningEnabled(warnType)) { + return; + } + + FunctionPreamble(filename, line, function); + ProcessedFilePreamble(); + ExtractedFilePreamble(parent, res, offset); + + WarningTypeAndChooseEscalate(warnType, header, body); +} + + +/* Help-related functions */ + +#include + +/** + * Print each warning name, default status, and description using the init map + */ +void WarningHandler::PrintHelp() { + std::set sortedKeys; + WarningInfoInit warningInfo; + uint32_t columnWidth = 25; + std::string dt; + + // Sort keys through the magic of `set`, to print in alphabetical order + for (auto& it : warningStringToInitMap) { + sortedKeys.insert(it.first); + } + + printf("\nWarning types ( * means enabled by default)\n"); + for (auto& key : sortedKeys) { + warningInfo = warningStringToInitMap.at(key); + if (warningInfo.defaultLevel <= WarningLevel::Warn) { + dt = "-W"; + dt += key; + if (warningInfo.defaultLevel == WarningLevel::Warn) { + dt += " *"; + } + printf(HELP_DT_INDT "%-*s", columnWidth, dt.c_str()); + + if (dt.length() + 2 > columnWidth) { + printf("\n" HELP_DT_INDT "%-*s", columnWidth, ""); + } + printf("%s\n", warningInfo.description.c_str()); + } + } + + printf("\nDefault errors\n"); + for (auto& key : sortedKeys) { + if (warningInfo.defaultLevel > WarningLevel::Warn) { + dt = "-W"; + dt += key; + printf(HELP_DT_INDT "%-*s", columnWidth, dt.c_str()); + + if (dt.length() + 2 > columnWidth) { + printf("\n" HELP_DT_INDT "%*s", columnWidth, ""); + } + printf("%s\n", warningInfo.description.c_str()); + } + } + + printf("\n"); + printf("Other\n" HELP_DT_INDT "-Weverything will enable all existing warnings.\n" HELP_DT_INDT "-Werror will promote all warnings to errors.\n"); + + printf("\n"); + printf("Warnings can be disabled using -Wno-... instead of -W...; -Weverything will override any -Wno-... flags passed before it.\n"); +} + +/** + * Print which warnings are currently enabled + */ +void WarningHandler::PrintWarningsDebugInfo() +{ + std::string dt; + + printf("Warnings status:\n"); + for (auto& it: warningTypeToInfoMap) { + dt = it.second.name; + dt += ": "; + + printf(HELP_DT_INDT "%-25s", dt.c_str()); + switch (it.second.level) + { + case WarningLevel::Off: + printf(VT_FGCOL(LIGHTGRAY) "Off" VT_RST); + break; + case WarningLevel::Warn: + printf(VT_FGCOL(YELLOW) "Warn" VT_RST); + break; + case WarningLevel::Err: + printf(VT_FGCOL(RED) "Err" VT_RST); + break; + + } + printf("\n"); + } + printf("\n"); +} diff --git a/ZAPDTR/ZAPD/WarningHandler.h b/ZAPDTR/ZAPD/WarningHandler.h new file mode 100644 index 000000000..f99330042 --- /dev/null +++ b/ZAPDTR/ZAPD/WarningHandler.h @@ -0,0 +1,146 @@ +#pragma once + +#include +#include +#include +#include + +#include "Utils/vt.h" +#include "ZFile.h" + +#ifdef _MSC_VER +#define __PRETTY_FUNCTION__ __FUNCSIG__ +#elif not defined(__GNUC__) +#define __PRETTY_FUNCTION__ __func__ +#endif + +// ======================================= +/* Formatting macros */ + +// TODO: move this somewhere else so it can be used by other help +#define HELP_DT_INDT " " + +/* Macros for formatting warnings/errors */ +#define VT_HILITE VT_BOLD_FGCOL(WHITE) +#define VT_WARN VT_BOLD_FGCOL(PURPLE) +#define VT_ERR VT_BOLD_FGCOL(RED) + +#define HILITE(string) (VT_HILITE string VT_RST) +#define WARN_FMT(string) (VT_WARN string VT_RST) +#define ERR_FMT(string) (VT_ERR string VT_RST) + +// Maybe make WARN_LF instead +// Currently 8 spaces +#define WARN_INDT " " +// Currently 16 spaces +#define HANG_INDT " " + +// ======================================= +/* Warning and error macros */ +// TODO: better names + +// General-purpose, plain style (only prints function,file,line in the preamble) +#define HANDLE_ERROR(warningType, header, body) \ + WarningHandler::Error_Plain(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, body) +#define HANDLE_WARNING(warningType, header, body) \ + WarningHandler::Warning_Plain(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, \ + body) + +// For processing XMLs or textures/blobs (preamble contains function,file,line; processed file) +#define HANDLE_ERROR_PROCESS(warningType, header, body) \ + WarningHandler::Error_Process(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, \ + body) +#define HANDLE_WARNING_PROCESS(warningType, header, body) \ + WarningHandler::Warning_Process(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, header, \ + body) + +// For ZResource-related stuff (preamble contains function,file,line; processed file; extracted file +// and offset) +#define HANDLE_ERROR_RESOURCE(warningType, parent, resource, offset, header, body) \ + WarningHandler::Error_Resource(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, parent, \ + resource, offset, header, body) +#define HANDLE_WARNING_RESOURCE(warningType, parent, resource, offset, header, body) \ + WarningHandler::Warning_Resource(__FILE__, __LINE__, __PRETTY_FUNCTION__, warningType, parent, \ + resource, offset, header, body) + +// ======================================= + +enum class WarningType +{ + Always, // Warnings of this type are always printed, cannot be disabled. + Deprecated, + Unaccounted, + MissingOffsets, + Intersection, + MissingAttribute, + InvalidAttributeValue, + UnknownAttribute, + InvalidXML, + InvalidJPEG, + InvalidPNG, + InvalidExtractedData, + MissingSegment, + HardcodedPointer, + HardcodedGenericPointer, + NotImplemented, + Max, +}; + +enum class WarningLevel +{ + Off, + Warn, + Err, +}; + +class WarningHandler +{ +public: + static void ConstructTypeToInfoMap(); + + static void Init(int argc, char* argv[]); + + static bool IsWarningEnabled(WarningType warnType); + static bool WasElevatedToError(WarningType warnType); + + static void FunctionPreamble(const char* filename, int32_t line, const char* function); + static void ProcessedFilePreamble(); + static void ExtractedFilePreamble(const ZFile* parent, const ZResource* res, + const uint32_t offset); + static std::string ConstructMessage(std::string message, const std::string& header, + const std::string& body); + + [[noreturn]] static void PrintErrorAndThrow(const std::string& header, const std::string& body); + static void PrintWarningBody(const std::string& header, const std::string& body); + + [[noreturn]] static void ErrorType(WarningType warnType, const std::string& header, + const std::string& body); + [[noreturn]] static void Error_Plain(const char* filename, int32_t line, const char* function, + WarningType warnType, const std::string& header, + const std::string& body); + [[noreturn]] static void Error_Process(const char* filename, int32_t line, const char* function, + WarningType warnType, const std::string& header, + const std::string& body); + [[noreturn]] static void Error_Resource(const char* filename, int32_t line, + const char* function, WarningType warnType, + const ZFile* parent, const ZResource* res, + const uint32_t offset, const std::string& header, + const std::string& body); + + static void WarningTypeAndChooseEscalate(WarningType warnType, const std::string& header, + const std::string& body); + + static void Warning_Plain(const char* filename, int32_t line, const char* function, + WarningType warnType, const std::string& header, + const std::string& body); + static void Warning_Process(const char* filename, int32_t line, const char* function, + WarningType warnType, const std::string& header, + const std::string& body); + static void Warning_Resource(const char* filename, int32_t line, const char* function, + WarningType warnType, const ZFile* parent, const ZResource* res, + const uint32_t offset, const std::string& header, + const std::string& body); + + static void PrintHelp(); + static void PrintWarningsDebugInfo(); +}; diff --git a/ZAPDTR/ZAPD/ZActorList.cpp b/ZAPDTR/ZAPD/ZActorList.cpp new file mode 100644 index 000000000..14aab0da8 --- /dev/null +++ b/ZAPDTR/ZAPD/ZActorList.cpp @@ -0,0 +1,194 @@ +#include "ZActorList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "WarningHandler.h" +#include "ZFile.h" +#include "ZRoom/ZNames.h" + +REGISTER_ZFILENODE(ActorList, ZActorList); + +ZActorList::ZActorList(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("Count"); +} + +void ZActorList::ExtractFromBinary(uint32_t nRawDataIndex, uint8_t nNumActors) +{ + rawDataIndex = nRawDataIndex; + numActors = nNumActors; + + // Don't parse raw data of external files + if (parent->GetMode() == ZFileMode::ExternalFile) + return; + + ParseRawData(); +} + +void ZActorList::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + numActors = StringHelper::StrToL(registeredAttributes.at("Count").value); + + if (numActors < 1) + { + HANDLE_ERROR_RESOURCE( + WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + StringHelper::Sprintf("invalid value '%d' found for 'NumPaths' attribute", numActors), + "Should be at least '1'"); + } +} + +void ZActorList::ParseRawData() +{ + ZResource::ParseRawData(); + + offset_t currentPtr = rawDataIndex; + size_t largestlength = 0; + + for (size_t i = 0; i < numActors; i++) + { + ActorSpawnEntry entry(parent->GetRawData(), currentPtr); + + currentPtr += entry.GetRawDataSize(); + actors.push_back(entry); + + size_t actorNameLength = ZNames::GetActorName(entry.GetActorId()).size(); + if (actorNameLength > largestlength) + largestlength = actorNameLength; + } + + for (auto& entry : actors) + { + entry.SetLargestActorName(largestlength); + } +} + +Declaration* ZActorList::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), name, GetActorListArraySize(), bodyStr); + decl->staticConf = staticConf; + + return decl; +} + +std::string ZActorList::GetBodySourceCode() const +{ + std::string declaration; + + size_t index = 0; + for (auto& entry : actors) + { + declaration += StringHelper::Sprintf("\t{ %s },", entry.GetBodySourceCode().c_str()); + + if (index < actors.size() - 1) + declaration += "\n"; + + index++; + } + + return declaration; +} + +std::string ZActorList::GetSourceTypeName() const +{ + return actors.front().GetSourceTypeName(); +} + +ZResourceType ZActorList::GetResourceType() const +{ + return ZResourceType::ActorList; +} + +size_t ZActorList::GetRawDataSize() const +{ + return actors.size() * actors.front().GetRawDataSize(); +} + +size_t ZActorList::GetActorListArraySize() const +{ + size_t actorCount = 0; + + // Doing an else-if here so we only do the loop when the game is SW97. + // Actor 0x22 is removed from SW97, so we need to ensure that we don't increment the actor count + // for it. + if (Globals::Instance->game == ZGame::OOT_SW97) + { + actorCount = 0; + + for (const auto& entry : actors) + if (entry.GetActorId() != 0x22) + actorCount++; + } + else + { + actorCount = actors.size(); + } + + return actorCount; +} + +/* ActorSpawnEntry */ + +ActorSpawnEntry::ActorSpawnEntry(const std::vector& rawData, uint32_t rawDataIndex) +{ + actorNum = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + posX = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + posY = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + posZ = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + rotX = BitConverter::ToUInt16BE(rawData, rawDataIndex + 8); + rotY = BitConverter::ToUInt16BE(rawData, rawDataIndex + 10); + rotZ = BitConverter::ToUInt16BE(rawData, rawDataIndex + 12); + params = BitConverter::ToInt16BE(rawData, rawDataIndex + 14); +} + +std::string ActorSpawnEntry::GetBodySourceCode() const +{ + std::string body; + + std::string actorNameFmt = StringHelper::Sprintf("%%-%zus ", largestActorName + 1); + body = + StringHelper::Sprintf(actorNameFmt.c_str(), (ZNames::GetActorName(actorNum) + ",").c_str()); + + body += StringHelper::Sprintf("{ %6i, %6i, %6i }, ", posX, posY, posZ); + if (Globals::Instance->game == ZGame::MM_RETAIL) + body += StringHelper::Sprintf("{ SPAWN_ROT_FLAGS(%#5hX, 0x%04X)" + ", SPAWN_ROT_FLAGS(%#5hX, 0x%04X)" + ", SPAWN_ROT_FLAGS(%#5hX, 0x%04X) }, ", + (rotX >> 7) & 0b111111111, rotX & 0b1111111, + (rotY >> 7) & 0b111111111, rotY & 0b1111111, + (rotZ >> 7) & 0b111111111, rotZ & 0b1111111); + else + body += StringHelper::Sprintf("{ %#6hX, %#6hX, %#6hX }, ", rotX, rotY, rotZ); + body += StringHelper::Sprintf("0x%04X", params); + + return body; +} + +std::string ActorSpawnEntry::GetSourceTypeName() const +{ + return "ActorEntry"; +} + +size_t ActorSpawnEntry::GetRawDataSize() const +{ + return 16; +} + +uint16_t ActorSpawnEntry::GetActorId() const +{ + return actorNum; +} + +void ActorSpawnEntry::SetLargestActorName(size_t nameSize) +{ + largestActorName = nameSize; +} diff --git a/ZAPDTR/ZAPD/ZActorList.h b/ZAPDTR/ZAPD/ZActorList.h new file mode 100644 index 000000000..4ce58aa57 --- /dev/null +++ b/ZAPDTR/ZAPD/ZActorList.h @@ -0,0 +1,52 @@ +#pragma once + +#include "ZResource.h" + +class ActorSpawnEntry +{ +public: + uint16_t actorNum; + int16_t posX; + int16_t posY; + int16_t posZ; + uint16_t rotX; + uint16_t rotY; + uint16_t rotZ; + uint16_t params; + size_t largestActorName = 16; + + ActorSpawnEntry(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetBodySourceCode() const; + + std::string GetSourceTypeName() const; + size_t GetRawDataSize() const; + + uint16_t GetActorId() const; + void SetLargestActorName(size_t nameSize); +}; + +class ZActorList : public ZResource +{ +public: + std::vector actors; + uint32_t numActors = 0; + + ZActorList(ZFile* nParent); + + void ExtractFromBinary(offset_t nRawDataIndex, uint8_t nNumActors); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +protected: + size_t GetActorListArraySize() const; +}; diff --git a/ZAPDTR/ZAPD/ZAnimation.cpp b/ZAPDTR/ZAPD/ZAnimation.cpp new file mode 100644 index 000000000..c00fa4b56 --- /dev/null +++ b/ZAPDTR/ZAPD/ZAnimation.cpp @@ -0,0 +1,580 @@ +#include "ZAnimation.h" + +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Animation, ZNormalAnimation); +REGISTER_ZFILENODE(PlayerAnimation, ZLinkAnimation); +REGISTER_ZFILENODE(CurveAnimation, ZCurveAnimation); +REGISTER_ZFILENODE(LegacyAnimation, ZLegacyAnimation); + +ZAnimation::ZAnimation(ZFile* nParent) : ZResource(nParent) +{ + frameCount = 0; + genOTRDef = true; +} + +void ZAnimation::ParseRawData() +{ + ZResource::ParseRawData(); + + frameCount = BitConverter::ToInt16BE(parent->GetRawData(), rawDataIndex + 0); +} + +/* +std::string ZAnimation::GetSourceOutputHeader(const std::string& prefix) +{ + if (Globals::Instance->otrMode) + { + std::string str = ""; + str += StringHelper::Sprintf("#define %s \"__OTR__%s/%s\"", name.c_str(), parent->GetOutName().c_str(), name.c_str()); + + return str; + } + else + return ZResource::GetSourceOutputHeader(prefix); +} +*/ + +ZResourceType ZAnimation::GetResourceType() const +{ + return ZResourceType::Animation; +} + +/* ZNormalAnimation */ + +ZNormalAnimation::ZNormalAnimation(ZFile* nParent) : ZAnimation(nParent) +{ +} + +size_t ZNormalAnimation::GetRawDataSize() const +{ + return 16; +} + +std::string ZNormalAnimation::GetSourceTypeName() const +{ + return "AnimationHeader"; +} + +void ZNormalAnimation::ParseRawData() +{ + ZAnimation::ParseRawData(); + + auto& data = parent->GetRawData(); + + rotationValuesSeg = BitConverter::ToInt32BE(data, rawDataIndex + 4); + rotationIndicesSeg = BitConverter::ToInt32BE(data, rawDataIndex + 8); + limit = BitConverter::ToInt16BE(data, rawDataIndex + 12); + + rotationValuesOffset = Seg2Filespace(rotationValuesSeg, parent->baseAddress); + rotationIndicesOffset = Seg2Filespace(rotationIndicesSeg, parent->baseAddress); + + uint32_t currentPtr = rotationValuesOffset; + + // Read the Rotation Values + for (uint32_t i = 0; i < ((rotationIndicesOffset - rotationValuesOffset) / 2); i++) + { + rotationValues.push_back(BitConverter::ToInt16BE(data, currentPtr)); + currentPtr += 2; + } + + currentPtr = rotationIndicesOffset; + + // Read the Rotation Indices + for (uint32_t i = 0; i < ((rawDataIndex - rotationIndicesOffset) / 6); i++) + { + rotationIndices.push_back(RotationIndex(BitConverter::ToInt16BE(data, currentPtr), + BitConverter::ToInt16BE(data, currentPtr + 2), + BitConverter::ToInt16BE(data, currentPtr + 4))); + currentPtr += 6; + } +} + +void ZNormalAnimation::DeclareReferences(const std::string& prefix) +{ + std::string defaultPrefix = prefix.c_str(); + if (name != "") + defaultPrefix = name; + + // replace g prefix with s for local variables + if (defaultPrefix.at(0) == 'g') + defaultPrefix.replace(0, 1, "s"); + + std::string indicesStr = ""; + std::string valuesStr = " "; + const uint8_t lineLength = 14; + const uint8_t offset = 0; + + if (!Globals::Instance->otrMode) + { + for (size_t i = 0; i < rotationValues.size(); i++) + { + valuesStr += StringHelper::Sprintf("0x%04X, ", rotationValues[i]); + + if ((i - offset + 1) % lineLength == 0) + valuesStr += "\n "; + } + } + + parent->AddDeclarationArray(rotationValuesOffset, DeclarationAlignment::Align4, + rotationValues.size() * 2, "s16", + StringHelper::Sprintf("%sFrameData", defaultPrefix.c_str()), + rotationValues.size(), valuesStr); + + if (!Globals::Instance->otrMode) + { + for (size_t i = 0; i < rotationIndices.size(); i++) + { + indicesStr += StringHelper::Sprintf(" { 0x%04X, 0x%04X, 0x%04X },", rotationIndices[i].x, + rotationIndices[i].y, rotationIndices[i].z); + + if (i != (rotationIndices.size() - 1)) + indicesStr += "\n"; + } + } + + parent->AddDeclarationArray(rotationIndicesOffset, DeclarationAlignment::Align4, + rotationIndices.size() * 6, "JointIndex", + StringHelper::Sprintf("%sJointIndices", defaultPrefix.c_str()), + rotationIndices.size(), indicesStr); +} + +std::string ZNormalAnimation::GetBodySourceCode() const +{ + std::string frameDataName; + Globals::Instance->GetSegmentedPtrName(rotationValuesSeg, parent, "s16", frameDataName, + parent->workerID); + std::string jointIndicesName; + Globals::Instance->GetSegmentedPtrName(rotationIndicesSeg, parent, "JointIndex", + jointIndicesName, parent->workerID); + + std::string headerStr = + StringHelper::Sprintf("\n\t{ %i }, %s,\n", frameCount, frameDataName.c_str()); + headerStr += StringHelper::Sprintf("\t%s, %i\n", jointIndicesName.c_str(), limit); + + return headerStr; +} + +/* ZLinkAnimation */ + +ZLinkAnimation::ZLinkAnimation(ZFile* nParent) : ZAnimation(nParent) +{ + segmentAddress = 0; +} + +size_t ZLinkAnimation::GetRawDataSize() const +{ + return 8; +} + +std::string ZLinkAnimation::GetSourceTypeName() const +{ + if (Globals::Instance->game == ZGame::MM_RETAIL) + return "PlayerAnimationHeader"; + else + return "LinkAnimationHeader"; +} + +void ZLinkAnimation::ParseRawData() +{ + ZAnimation::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + segmentAddress = BitConverter::ToInt32BE(rawData, rawDataIndex + 4); +} + +std::string ZLinkAnimation::GetBodySourceCode() const +{ + std::string segSymbol; + Globals::Instance->GetSegmentedPtrName(segmentAddress, parent, "", segSymbol, parent->workerID); + + return StringHelper::Sprintf("\n\t{ %i }, %s\n", frameCount, segSymbol.c_str()); +} + +/* ZCurveAnimation */ + +CurveInterpKnot::CurveInterpKnot(ZFile* parent, const std::vector& rawData, + uint32_t fileOffset) + : parent(parent) +{ + unk_00 = BitConverter::ToUInt16BE(rawData, fileOffset + 0); + unk_02 = BitConverter::ToUInt16BE(rawData, fileOffset + 2); + unk_04 = BitConverter::ToInt16BE(rawData, fileOffset + 4); + unk_06 = BitConverter::ToInt16BE(rawData, fileOffset + 6); + unk_08 = BitConverter::ToFloatBE(rawData, fileOffset + 8); +} + +CurveInterpKnot::CurveInterpKnot(ZFile* parent, const std::vector& rawData, + uint32_t fileOffset, size_t index) + : CurveInterpKnot(parent, rawData, fileOffset + index * GetRawDataSize()) +{ +} + +std::string CurveInterpKnot::GetBody([[maybe_unused]] const std::string& prefix) const +{ + return StringHelper::Sprintf("0x%04X, 0x%04X, %i, %i, %ff", unk_00, unk_02, unk_04, unk_06, + unk_08); +} + +size_t CurveInterpKnot::GetRawDataSize() const +{ + return 0x0C; +} + +std::string CurveInterpKnot::GetSourceTypeName() +{ + return "CurveInterpKnot"; +} + +ZCurveAnimation::ZCurveAnimation(ZFile* nParent) : ZAnimation(nParent) +{ + RegisterOptionalAttribute("SkelOffset"); +} + +void ZCurveAnimation::ParseXML(tinyxml2::XMLElement* reader) +{ + ZAnimation::ParseXML(reader); + + std::string skelOffsetXml = registeredAttributes.at("SkelOffset").value; + if (skelOffsetXml == "") + { + HANDLE_ERROR_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex, + "missing 'SkelOffset' attribute in ", + "You need to provide the offset of the curve skeleton."); + } + skelOffset = StringHelper::StrToL(skelOffsetXml, 0); +} + +void ZCurveAnimation::ParseRawData() +{ + ZAnimation::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + refIndex = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0); + transformData = BitConverter::ToUInt32BE(rawData, rawDataIndex + 4); + copyValues = BitConverter::ToUInt32BE(rawData, rawDataIndex + 8); + unk_0C = BitConverter::ToInt16BE(rawData, rawDataIndex + 12); + unk_10 = BitConverter::ToInt16BE(rawData, rawDataIndex + 14); + + uint32_t limbCountAddress = Seg2Filespace(skelOffset, parent->baseAddress) + 4; + limbCount = BitConverter::ToUInt8BE(rawData, limbCountAddress); + + size_t transformDataSize = 0; + size_t copyValuesSize = 0; + if (refIndex != 0) + { + uint32_t refIndexOffset = Seg2Filespace(refIndex, parent->baseAddress); + for (size_t i = 0; i < 3 * 3 * limbCount; i++) + { + uint8_t ref = BitConverter::ToUInt8BE(rawData, refIndexOffset + i); + if (ref == 0) + copyValuesSize++; + else + transformDataSize += ref; + + refIndexArr.emplace_back(ref); + } + } + + if (transformData != 0) + { + uint32_t transformDataOffset = Seg2Filespace(transformData, parent->baseAddress); + + for (size_t i = 0; i < transformDataSize; i++) + transformDataArr.emplace_back(parent, rawData, transformDataOffset, i); + } + + if (copyValues != 0) + { + uint32_t copyValuesOffset = Seg2Filespace(copyValues, parent->baseAddress); + + for (size_t i = 0; i < copyValuesSize; i++) + copyValuesArr.emplace_back(BitConverter::ToInt16BE(rawData, copyValuesOffset + i * 2)); + } +} + +void ZCurveAnimation::DeclareReferences(const std::string& prefix) +{ + if (refIndex != 0) + { + uint32_t refIndexOffset = Seg2Filespace(refIndex, parent->baseAddress); + std::string refIndexStr = + StringHelper::Sprintf("%sCurveAnime_%s_%06X", prefix.c_str(), "Ref", refIndexOffset); + + std::string entryStr = " "; + uint16_t arrayItemCnt = refIndexArr.size(); + + size_t i = 0; + for (auto& child : refIndexArr) + { + entryStr += StringHelper::Sprintf("0x%02X, %s", child, (i++ % 8 == 7) ? "\n " : ""); + } + + Declaration* decl = parent->GetDeclaration(refIndexOffset); + if (decl == nullptr) + { + parent->AddDeclarationArray(refIndexOffset, DeclarationAlignment::Align4, + arrayItemCnt * 1, "u8", refIndexStr, arrayItemCnt, + entryStr); + } + else + { + decl->declBody = entryStr; + } + } + + if (transformData != 0) + { + uint32_t transformDataOffset = Seg2Filespace(transformData, parent->baseAddress); + std::string transformDataStr = StringHelper::Sprintf( + "%sCurveAnime_%s_%06X", prefix.c_str(), + transformDataArr.at(0).GetSourceTypeName().c_str(), transformDataOffset); + + std::string entryStr; + uint16_t arrayItemCnt = transformDataArr.size(); + + size_t i = 0; + for (auto& child : transformDataArr) + { + entryStr += StringHelper::Sprintf(" { %s },%s", child.GetBody(prefix).c_str(), + (++i < arrayItemCnt) ? "\n" : ""); + } + + Declaration* decl = parent->GetDeclaration(transformDataOffset); + if (decl == nullptr) + { + parent->AddDeclarationArray(transformDataOffset, DeclarationAlignment::Align4, + arrayItemCnt * transformDataArr.at(0).GetRawDataSize(), + transformDataArr.at(0).GetSourceTypeName(), + transformDataStr, arrayItemCnt, entryStr); + } + else + { + decl->declBody = entryStr; + } + } + + if (copyValues != 0) + { + uint32_t copyValuesOffset = Seg2Filespace(copyValues, parent->baseAddress); + std::string copyValuesStr = + StringHelper::Sprintf("%sCurveAnime_%s_%06X", prefix.c_str(), "Copy", copyValuesOffset); + + std::string entryStr = " "; + uint16_t arrayItemCnt = copyValuesArr.size(); + + size_t i = 0; + for (auto& child : copyValuesArr) + { + entryStr += StringHelper::Sprintf("% 6i, %s", child, (i++ % 8 == 7) ? "\n " : ""); + } + + Declaration* decl = parent->GetDeclaration(copyValuesOffset); + if (decl == nullptr) + { + parent->AddDeclarationArray(copyValuesOffset, DeclarationAlignment::Align4, + arrayItemCnt * 2, "s16", copyValuesStr, arrayItemCnt, + entryStr); + } + else + { + decl->declBody = entryStr; + } + } +} + +std::string ZCurveAnimation::GetBodySourceCode() const +{ + std::string refIndexStr; + Globals::Instance->GetSegmentedPtrName(refIndex, parent, "u8", refIndexStr, parent->workerID); + std::string transformDataStr; + Globals::Instance->GetSegmentedPtrName(transformData, parent, "CurveInterpKnot", + transformDataStr, parent->workerID); + std::string copyValuesStr; + Globals::Instance->GetSegmentedPtrName(copyValues, parent, "s16", copyValuesStr, + parent->workerID); + + return StringHelper::Sprintf("\n\t%s,\n\t%s,\n\t%s,\n\t%i, %i\n", refIndexStr.c_str(), + transformDataStr.c_str(), copyValuesStr.c_str(), unk_0C, unk_10); +} + +size_t ZCurveAnimation::GetRawDataSize() const +{ + return 0x10; +} + +DeclarationAlignment ZCurveAnimation::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align4; +} + +std::string ZCurveAnimation::GetSourceTypeName() const +{ + return "CurveAnimationHeader"; +} + +/* ZLegacyAnimation */ + +ZLegacyAnimation::ZLegacyAnimation(ZFile* nParent) : ZAnimation(nParent) +{ +} + +void ZLegacyAnimation::ParseRawData() +{ + ZAnimation::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + limbCount = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x02); + frameData = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x04); + jointKey = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x08); + + if (GETSEGNUM(frameData) == parent->segment && GETSEGNUM(jointKey) == parent->segment) + { + uint32_t frameDataOffset = Seg2Filespace(frameData, parent->baseAddress); + uint32_t jointKeyOffset = Seg2Filespace(jointKey, parent->baseAddress); + + uint32_t ptr = frameDataOffset; + for (size_t i = 0; i < (jointKeyOffset - frameDataOffset) / 2; i++) + { + frameDataArray.push_back(BitConverter::ToUInt16BE(rawData, ptr)); + ptr += 2; + } + + ptr = jointKeyOffset; + for (int32_t i = 0; i < limbCount + 1; i++) + { + LegacyJointKey key(parent); + key.ExtractFromFile(ptr); + + jointKeyArray.push_back(key); + ptr += key.GetRawDataSize(); + } + } +} + +void ZLegacyAnimation::DeclareReferences(const std::string& prefix) +{ + std::string varPrefix = prefix; + if (name != "") + varPrefix = name; + + ZAnimation::DeclareReferences(varPrefix); + + if (!frameDataArray.empty()) + { + uint32_t frameDataOffset = Seg2Filespace(frameData, parent->baseAddress); + if (GETSEGNUM(frameData) == parent->segment && !parent->HasDeclaration(frameDataOffset)) + { + std::string frameDataBody = "\t"; + + for (size_t i = 0; i < frameDataArray.size(); i++) + { + frameDataBody += StringHelper::Sprintf("0x%04X, ", frameDataArray[i]); + + if (i % 8 == 7 && i + 1 < frameDataArray.size()) + frameDataBody += "\n\t"; + } + + std::string frameDataName = StringHelper::Sprintf("%sFrameData", varPrefix.c_str()); + parent->AddDeclarationArray(frameDataOffset, DeclarationAlignment::Align4, + frameDataArray.size() * 2, "s16", frameDataName, + frameDataArray.size(), frameDataBody); + } + } + + if (!jointKeyArray.empty()) + { + uint32_t jointKeyOffset = Seg2Filespace(jointKey, parent->baseAddress); + if (GETSEGNUM(jointKey) == parent->segment && !parent->HasDeclaration(jointKeyOffset)) + { + const auto res = jointKeyArray.at(0); + std::string jointKeyBody; + + for (size_t i = 0; i < jointKeyArray.size(); i++) + { + jointKeyBody += StringHelper::Sprintf("\t{ %s },", + jointKeyArray[i].GetBodySourceCode().c_str()); + + if (i + 1 < jointKeyArray.size()) + jointKeyBody += "\n"; + } + + std::string jointKeyName = StringHelper::Sprintf("%sJointKey", varPrefix.c_str()); + parent->AddDeclarationArray(jointKeyOffset, DeclarationAlignment::Align4, + jointKeyArray.size() * res.GetRawDataSize(), + res.GetSourceTypeName(), jointKeyName, jointKeyArray.size(), + jointKeyBody); + } + } +} + +std::string ZLegacyAnimation::GetBodySourceCode() const +{ + std::string body = "\n"; + + std::string frameDataName; + std::string jointKeyName; + Globals::Instance->GetSegmentedPtrName(frameData, parent, "s16", frameDataName, parent->workerID); + Globals::Instance->GetSegmentedPtrName(jointKey, parent, "LegacyJointKey", jointKeyName, parent->workerID); + + body += StringHelper::Sprintf("\t%i, %i,\n", frameCount, limbCount); + body += StringHelper::Sprintf("\t%s,\n", frameDataName.c_str()); + body += StringHelper::Sprintf("\t%s\n", jointKeyName.c_str()); + + return body; +} + +std::string ZLegacyAnimation::GetSourceTypeName() const +{ + return "LegacyAnimationHeader"; +} + +size_t ZLegacyAnimation::GetRawDataSize() const +{ + return 0x0C; +} + +LegacyJointKey::LegacyJointKey(ZFile* nParent) : ZResource(nParent) +{ +} + +void LegacyJointKey::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + xMax = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x00); + x = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x02); + yMax = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x04); + y = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x06); + zMax = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x08); + z = BitConverter::ToInt16BE(rawData, rawDataIndex + 0x0A); +} + +std::string LegacyJointKey::GetBodySourceCode() const +{ + return StringHelper::Sprintf("%6i, %6i, %6i, %6i, %6i, %6i", xMax, x, yMax, y, zMax, z); +} + +std::string LegacyJointKey::GetSourceTypeName() const +{ + return "LegacyJointKey"; +} + +ZResourceType LegacyJointKey::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +size_t LegacyJointKey::GetRawDataSize() const +{ + return 0x0C; +} diff --git a/ZAPDTR/ZAPD/ZAnimation.h b/ZAPDTR/ZAPD/ZAnimation.h new file mode 100644 index 000000000..a24903727 --- /dev/null +++ b/ZAPDTR/ZAPD/ZAnimation.h @@ -0,0 +1,179 @@ +#pragma once + +#include +#include +#include +#include "ZResource.h" +#include "ZSkeleton.h" +#include "tinyxml2.h" + +struct RotationIndex +{ + // uint16_t transX, transY, transZ; + uint16_t x, y, z; + + RotationIndex(uint16_t nX, uint16_t nY, uint16_t nZ) : x(nX), y(nY), z(nZ) {} +}; + +class ZAnimation : public ZResource +{ +public: + int16_t frameCount; + + ZAnimation(ZFile* nParent); + + //std::string GetSourceOutputHeader(const std::string& prefix) override; + ZResourceType GetResourceType() const override; + +protected: + void ParseRawData() override; +}; + +class ZNormalAnimation : public ZAnimation +{ +public: + std::vector rotationValues; + std::vector rotationIndices; + segptr_t rotationValuesSeg = 0; + segptr_t rotationIndicesSeg = 0; + offset_t rotationValuesOffset = 0; + offset_t rotationIndicesOffset = 0; + int16_t limit = 0; + + ZNormalAnimation(ZFile* nParent); + + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + size_t GetRawDataSize() const override; + std::string GetSourceTypeName() const override; + + void ParseRawData() override; +}; + +class ZLinkAnimation : public ZAnimation +{ +public: + segptr_t segmentAddress; + + ZLinkAnimation(ZFile* nParent); + + std::string GetBodySourceCode() const override; + + size_t GetRawDataSize() const override; + std::string GetSourceTypeName() const override; + + void ParseRawData() override; +}; + +class CurveInterpKnot +{ +public: + ZFile* parent; + + ///* 0x0000 */ u16 unk_00; // appears to be flags + uint16_t unk_00; + ///* 0x0002 */ s16 unk_02; + int16_t unk_02; + ///* 0x0004 */ s16 unk_04; + int16_t unk_04; + ///* 0x0006 */ s16 unk_06; + int16_t unk_06; + ///* 0x0008 */ f32 unk_08; + float unk_08; + +public: + CurveInterpKnot() = default; + CurveInterpKnot(ZFile* parent, const std::vector& rawData, uint32_t fileOffset); + CurveInterpKnot(ZFile* parent, const std::vector& rawData, uint32_t fileOffset, + size_t index); + + [[nodiscard]] std::string GetBody(const std::string& prefix) const; + + size_t GetRawDataSize() const; + std::string GetSourceTypeName(); +}; + +class ZCurveAnimation : public ZAnimation +{ +public: + segptr_t skelOffset = 0; + + ///* 0x0000 */ u8* refIndex; + segptr_t refIndex = 0; + ///* 0x0004 */ CurveInterpKnot* transformData; + segptr_t transformData = 0; + ///* 0x0008 */ s16* copyValues; + segptr_t copyValues = 0; + ///* 0x000C */ s16 unk_0C; + int16_t unk_0C; + ///* 0x000E */ s16 unk_10; + int16_t unk_10; + + uint8_t limbCount = 0; + + std::vector refIndexArr; + std::vector transformDataArr; + std::vector copyValuesArr; + +public: + ZCurveAnimation(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; + + std::string GetSourceTypeName() const override; +}; +// CurveAnimationHeader + +/* ZLegacyAnimation */ + +class LegacyJointKey : public ZResource +{ +public: + LegacyJointKey(ZFile* nParent); + + void ParseRawData() override; + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +protected: + int16_t xMax, x; + int16_t yMax, y; + int16_t zMax, z; +}; + +class ZLegacyAnimation : public ZAnimation +{ +public: + ZLegacyAnimation(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + + size_t GetRawDataSize() const override; + +protected: + int16_t limbCount; + segptr_t frameData; // s16* + segptr_t jointKey; // LegacyJointKey* + + std::vector frameDataArray; + std::vector jointKeyArray; +}; diff --git a/ZAPDTR/ZAPD/ZArray.cpp b/ZAPDTR/ZAPD/ZArray.cpp new file mode 100644 index 000000000..417f15457 --- /dev/null +++ b/ZAPDTR/ZAPD/ZArray.cpp @@ -0,0 +1,154 @@ +#include "ZArray.h" + +#include + +#include "Globals.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Array, ZArray); + +ZArray::ZArray(ZFile* nParent) : ZResource(nParent) +{ + canHaveInner = true; + genOTRDef = true; + RegisterRequiredAttribute("Count"); +} + +ZArray::~ZArray() +{ + for (auto res : resList) + delete res; +} + +void ZArray::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + arrayCnt = reader->IntAttribute("Count", 0); + if (arrayCnt <= 0) + { + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + "invalid value found for 'Count' attribute", ""); + } + + tinyxml2::XMLElement* child = reader->FirstChildElement(); + if (child == nullptr) + { + HANDLE_ERROR_RESOURCE(WarningType::InvalidXML, parent, this, rawDataIndex, + " needs one sub-element", ""); + } + + childName = child->Name(); + + auto nodeMap = ZFile::GetNodeMap(); + size_t childIndex = rawDataIndex; + resList.reserve(arrayCnt); + for (size_t i = 0; i < arrayCnt; i++) + { + ZResource* res = nodeMap->at(childName)(parent); + if (!res->DoesSupportArray()) + { + std::string errorHeader = StringHelper::Sprintf( + "resource <%s> does not support being wrapped in an ", childName.c_str()); + HANDLE_ERROR_RESOURCE(WarningType::InvalidXML, parent, this, rawDataIndex, errorHeader, + ""); + } + res->parent = parent; + res->SetInnerNode(true); + res->ExtractWithXML(child, childIndex); + + childIndex += res->GetRawDataSize(); + resList.push_back(res); + } +} + +Declaration* ZArray::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + ZResource* res = resList.at(0); + Declaration* decl; + if (res->IsExternalResource()) + { + auto filepath = Globals::Instance->outputPath / name; + std::string includePath = StringHelper::Sprintf("%s.%s.inc", filepath.string().c_str(), + res->GetExternalExtension().c_str()); + decl = parent->AddDeclarationIncludeArray(rawDataIndex, includePath, GetRawDataSize(), + GetSourceTypeName(), name, arrayCnt); + decl->declBody = bodyStr; + decl->isExternal = true; + } + else + { + decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), name, arrayCnt, bodyStr); + } + + decl->staticConf = staticConf; + return decl; +} + +std::string ZArray::GetBodySourceCode() const +{ + std::string output; + + for (size_t i = 0; i < arrayCnt; i++) + { + const auto& res = resList[i]; + output += "\t"; + + switch (res->GetResourceType()) + { + case ZResourceType::Pointer: + case ZResourceType::Scalar: + case ZResourceType::Vertex: + case ZResourceType::CollisionPoly: + case ZResourceType::SurfaceType: + case ZResourceType::Waterbox: + output += resList.at(i)->GetBodySourceCode(); + break; + + default: + output += StringHelper::Sprintf("{ %s }", resList.at(i)->GetBodySourceCode().c_str()); + break; + } + + if (i < arrayCnt - 1 || res->IsExternalResource()) + output += ",\n"; + } + + return output; +} + +size_t ZArray::GetRawDataSize() const +{ + size_t size = 0; + for (const auto res : resList) + size += res->GetRawDataSize(); + return size; +} + +std::string ZArray::GetSourceTypeName() const +{ + return resList.at(0)->GetSourceTypeName(); +} + +ZResourceType ZArray::GetResourceType() const +{ + return ZResourceType::Array; +} + +DeclarationAlignment ZArray::GetDeclarationAlignment() const +{ + if (resList.size() == 0) + { + return DeclarationAlignment::Align4; + } + return resList.at(0)->GetDeclarationAlignment(); +} diff --git a/ZAPDTR/ZAPD/ZArray.h b/ZAPDTR/ZAPD/ZArray.h new file mode 100644 index 000000000..e6594fadc --- /dev/null +++ b/ZAPDTR/ZAPD/ZArray.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include "ZResource.h" +#include "tinyxml2.h" + +class ZArray : public ZResource +{ +public: + ZArray(ZFile* nParent); + ~ZArray(); + + void ParseXML(tinyxml2::XMLElement* reader) override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + size_t GetRawDataSize() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + DeclarationAlignment GetDeclarationAlignment() const override; + + size_t arrayCnt; + std::vector resList; +protected: + std::string childName; +}; diff --git a/ZAPDTR/ZAPD/ZAudio.cpp b/ZAPDTR/ZAPD/ZAudio.cpp new file mode 100644 index 000000000..03c1efa99 --- /dev/null +++ b/ZAPDTR/ZAPD/ZAudio.cpp @@ -0,0 +1,416 @@ +#include "ZAudio.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Audio, ZAudio); + +ZAudio::ZAudio(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("SoundFontTableOffset"); + RegisterRequiredAttribute("SequenceTableOffset"); + RegisterRequiredAttribute("SampleBankTableOffset"); + RegisterRequiredAttribute("SequenceFontTableOffset"); +} + +void ZAudio::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + auto t = reader->Name(); + + auto child = reader->FirstChildElement(); + + while (child != nullptr) + { + if (strcmp(child->Value(), "Sequences") == 0) + { + auto seqChild = child->FirstChildElement(); + + while (seqChild != nullptr) + { + if (strcmp(seqChild->Value(), "Sequence") == 0) + { + seqNames.push_back(seqChild->Attribute("Name")); + } + + seqChild = seqChild->NextSiblingElement(); + } + } + + if (strcmp(child->Value(), "Samples") == 0) + { + int bankId = child->IntAttribute("Bank", 0); + auto sampChild = child->FirstChildElement(); + + while (sampChild != nullptr) + { + if (strcmp(sampChild->Value(), "Sample") == 0) + { + uint32_t atOffset = sampChild->UnsignedAttribute("Offset"); + + sampleOffsets[bankId][atOffset] = sampChild->Attribute("Name"); + } + + sampChild = sampChild->NextSiblingElement(); + } + } + + if (strcmp(child->Value(), "Soundfont") == 0) + { + auto name = child->Attribute("Name"); + auto index = child->IntAttribute("Index", 0); + soundFontNames[index] = name; + } + + child = child->NextSiblingElement(); + } +} + +std::vector ZAudio::ParseEnvelopeData(const std::vector& audioBank, const std::vector& audioTable, int envelopeOffset, int baseOffset) +{ + std::vector result; + + while (true) + { + AdsrEnvelope* env = new AdsrEnvelope(); + + env->delay = BitConverter::ToInt16BE(audioBank, envelopeOffset + 0); + env->arg = BitConverter::ToInt16BE(audioBank, envelopeOffset + 2); + + envelopeOffset += 4; + + result.push_back(env); + + if (env->delay < 0) + break; + } + + return result; +} + +SoundFontEntry* ZAudio::ParseSoundFontEntry(const std::vector& audioBank, + const std::vector& audioTable, + AudioTableEntry audioSampleBankEntry, int bankIndex, + int soundFontOffset, + int baseOffset) +{ + SoundFontEntry* soundFont = new SoundFontEntry(); + + int sampleOffset = BitConverter::ToInt32BE(audioBank, soundFontOffset + 0) + baseOffset; + + if (sampleOffset == 0) + return nullptr; + + soundFont->sampleEntry = ParseSampleEntry( + audioBank, audioTable, audioSampleBankEntry, bankIndex, + sampleOffset, baseOffset); + soundFont->tuning = BitConverter::ToFloatBE(audioBank, soundFontOffset + 4); + + return soundFont; +} + +SampleEntry* ZAudio::ParseSampleEntry(const std::vector& audioBank, + const std::vector& audioTable, + AudioTableEntry audioSampleBankEntry, int bankIndex, + int sampleOffset, + int baseOffset) +{ + int sampleDataOffset = BitConverter::ToInt32BE(audioBank, sampleOffset + 4) + audioSampleBankEntry.ptr; + int sampleSize = BitConverter::ToInt32BE(audioBank, sampleOffset + 0) & 0x00FFFFFF; + int loopOffset = BitConverter::ToInt32BE(audioBank, sampleOffset + 8) + baseOffset; + int bookOffset = BitConverter::ToInt32BE(audioBank, sampleOffset + 12) + baseOffset; + + if (samples.find(sampleDataOffset) == samples.end()) + { + SampleEntry* sample = new SampleEntry(); + + sample->bankId = bankIndex; + + + sample->data = std::vector(sampleSize); + memcpy(sample->data.data(), audioTable.data() + sampleDataOffset, sampleSize); + + uint32_t origField = (BitConverter::ToUInt32BE(audioBank, sampleOffset + 0)); + sample->codec = (origField >> 28) & 0x0F; + sample->medium = (origField >> 24) & 0x03; + sample->unk_bit26 = (origField >> 22) & 0x01; + sample->unk_bit25 = (origField >> 21) & 0x01; + + sample->loop.start = BitConverter::ToInt32BE(audioBank, loopOffset + 0); + sample->loop.end = BitConverter::ToInt32BE(audioBank, loopOffset + 4); + sample->loop.count = BitConverter::ToInt32BE(audioBank, loopOffset + 8); + + if (sample->loop.count != 0) + { + for (int i = 0; i < 16; i++) + { + // TODO can loop.states be an array of size 16? + int16_t state = BitConverter::ToInt16BE(audioBank, loopOffset + 16 + (i * 2)); + sample->loop.states.push_back(state); + } + } + + sample->book.order = BitConverter::ToInt32BE(audioBank, bookOffset + 0); + sample->book.npredictors = BitConverter::ToInt32BE(audioBank, bookOffset + 4); + + int32_t numBooks = sample->book.npredictors * sample->book.order * 8; + sample->book.books.reserve(numBooks); + for (int i = 0; i < numBooks; i++) + { + sample->book.books.push_back( + BitConverter::ToInt16BE(audioBank, bookOffset + 8 + (i * 2))); + } + + sample->sampleDataOffset = sampleDataOffset; + + sample->sampleLoopOffset = 0; + + + sample->fileName = sampleOffsets[bankIndex][sampleDataOffset]; + + samples[sampleDataOffset] = sample; + + return sample; + } + else + { + return samples[sampleDataOffset]; + } +} + +std::vector ZAudio::ParseAudioTable(const std::vector& codeData, int baseOffset) +{ + std::vector entries; + + int numEntries = BitConverter::ToInt16BE(codeData, baseOffset + 0); + int romAddr = BitConverter::ToInt16BE(codeData, baseOffset + 4); + + int currentOffset = baseOffset + 16; + entries.reserve(numEntries); + for (int i = 0; i < numEntries; i++) + { + AudioTableEntry entry; + + entry.ptr = BitConverter::ToInt32BE(codeData, currentOffset + 0); + entry.size = BitConverter::ToInt32BE(codeData, currentOffset + 4); + entry.medium = codeData[currentOffset + 8]; + entry.cachePolicy = codeData[currentOffset + 9]; + entry.data1 = BitConverter::ToInt16BE(codeData, currentOffset + 10); + entry.data2 = BitConverter::ToInt16BE(codeData, currentOffset + 12); + entry.data3 = BitConverter::ToInt16BE(codeData, currentOffset + 14); + + entries.push_back(entry); + + currentOffset += 16; + } + + return entries; +} + +void ZAudio::ParseSoundFont(const std::vector& codeData, const std::vector& audioTable, + const std::vector& audioSampleBank, + AudioTableEntry& entry) +{ + int ptr = entry.ptr; + int size = entry.size; + int sampleBankId1 = (entry.data1 >> 8) & 0xFF; + int sampleBankId2 = (entry.data1) & 0xFF; + int numInstruments = (entry.data2 >> 8) & 0xFF; + int numDrums = entry.data2 & 0xFF; + int numSfx = entry.data3; + + entry.drums.reserve(numDrums); + entry.soundEffects.reserve(numSfx); + entry.instruments.reserve(numInstruments); + + int currentOffset = BitConverter::ToInt32BE(codeData, ptr) + ptr; + for (int i = 0; i < numDrums; i++) + { + DrumEntry drum = {0}; + + int samplePtr = BitConverter::ToInt32BE(codeData, currentOffset); + + if (samplePtr != 0) + { + samplePtr += ptr; + + drum.sample = ParseSampleEntry(codeData, audioTable, audioSampleBank[sampleBankId1], sampleBankId1, + BitConverter::ToInt32BE(codeData, samplePtr + 4) + ptr, ptr); + + drum.releaseRate = codeData[samplePtr + 0]; + drum.pan = codeData[samplePtr + 1]; + drum.loaded = codeData[samplePtr + 2]; + drum.tuning = BitConverter::ToFloatBE(codeData, samplePtr + 8); + drum.env = ParseEnvelopeData(codeData, audioTable, BitConverter::ToInt32BE(codeData, samplePtr + 12) + ptr, ptr); + } + entry.drums.push_back(drum); + + + currentOffset += 4; + } + + currentOffset = BitConverter::ToInt32BE(codeData, ptr + 4) + ptr; + for (int i = 0; i < numSfx; i++) + { + SoundFontEntry* sfx; + sfx = ParseSoundFontEntry(codeData, audioTable, audioSampleBank[sampleBankId1], sampleBankId1, + currentOffset, ptr); + + //if (sfx != nullptr) + entry.soundEffects.push_back(sfx); + + currentOffset += 8; + } + + for (int i = 0; i < numInstruments; i++) + { + InstrumentEntry instrument; + + currentOffset = BitConverter::ToInt32BE(codeData, ptr + 8 + (i * 4)); + + instrument.isValidInstrument = currentOffset != 0; + + if (currentOffset != 0) + { + currentOffset += ptr; + + instrument.loaded = codeData[currentOffset + 0]; + instrument.normalRangeLo = codeData[currentOffset + 1]; + instrument.normalRangeHi = codeData[currentOffset + 2]; + instrument.releaseRate = codeData[currentOffset + 3]; + instrument.env = ParseEnvelopeData(codeData, audioTable, BitConverter::ToInt32BE(codeData, currentOffset + 4) + ptr, ptr); + + if (BitConverter::ToInt32BE(codeData, currentOffset + 8) != 0) + instrument.lowNotesSound = ParseSoundFontEntry( + codeData, audioTable, audioSampleBank[sampleBankId1], sampleBankId1, currentOffset + 8, ptr); + + if (BitConverter::ToInt32BE(codeData, currentOffset + 16) != 0) + instrument.normalNotesSound = ParseSoundFontEntry( + codeData, audioTable, audioSampleBank[sampleBankId1], sampleBankId1, currentOffset + 16, ptr); + + if (BitConverter::ToInt32BE(codeData, currentOffset + 24) != 0 && + instrument.normalRangeHi != 0x7F) + instrument.highNotesSound = ParseSoundFontEntry( + codeData, audioTable, audioSampleBank[sampleBankId1], sampleBankId1, currentOffset + 24, ptr); + } + // Interesting audio bug if you put this next line in the if block + entry.instruments.push_back(instrument); + + } +} + +void ZAudio::ParseRawData() +{ + ZResource::ParseRawData(); + + std::vector codeData; + std::vector audioTableData; + std::vector audioBankData; + std::vector audioSeqData; + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + codeData = Globals::Instance->GetBaseromFile("code"); + else + codeData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "code"); + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + audioTableData = Globals::Instance->GetBaseromFile("Audiotable"); + else + audioTableData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "Audiotable"); + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + audioBankData = Globals::Instance->GetBaseromFile("Audiobank"); + else + audioBankData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "Audiobank"); + + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + audioSeqData = Globals::Instance->GetBaseromFile("Audioseq"); + else + audioSeqData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + + "Audioseq"); + + // TABLE PARSING + + // MQ DBG ROM + //int gSoundFontTableOffset = 0x138270; + //int gSequenceTableOffset = 0x1386A0; + //int gSampleBankTableOffset = 0x138D90; + //int gSequenceFontTableOffset = 0x1384E0; + + // NTSC 1.0 + //int gSoundFontTableOffset = 0x1026A0; + //int gSequenceTableOffset = 0x102AD0; + //int gSampleBankTableOffset = 0x1031C0; + //int gSequenceFontTableOffset = 0x102910; + + int gSoundFontTableOffset = StringHelper::StrToL(registeredAttributes.at("SoundFontTableOffset").value, 16); + int gSequenceTableOffset = StringHelper::StrToL(registeredAttributes.at("SequenceTableOffset").value, 16); + int gSampleBankTableOffset = StringHelper::StrToL(registeredAttributes.at("SampleBankTableOffset").value, 16); + int gSequenceFontTableOffset = StringHelper::StrToL(registeredAttributes.at("SequenceFontTableOffset").value, 16); + + soundFontTable = ParseAudioTable(codeData, gSoundFontTableOffset); + sequenceTable = ParseAudioTable(codeData, gSequenceTableOffset); + sampleBankTable = ParseAudioTable(codeData, gSampleBankTableOffset); + + fontIndices.reserve(sequenceTable.size()); + sequences.reserve(sequenceTable.size()); + + // SEQEUNCE FONT TABLE PARSING + for (int i = 0; i < sequenceTable.size(); i++) + { + uint16_t idx = BitConverter::ToUInt16BE(codeData, gSequenceFontTableOffset + (i * 2)); + uint8_t numFonts = codeData[gSequenceFontTableOffset + (idx++)]; + std::vector fontIds; + + for (int j = 0; j < numFonts; j++) + { + uint8_t fontId = codeData[gSequenceFontTableOffset + (idx++)]; + fontIds.push_back(fontId); + } + + fontIndices.push_back(fontIds); + } + + + // SAMPLE/FONT PARSING + for (auto& sft : soundFontTable) + { + ParseSoundFont(audioBankData, audioTableData, sampleBankTable, sft); + } + + // SEQUENCE PARSING + for (int i = 0; i < sequenceTable.size(); i++) + { + int seqDestIdx = i; + + if (sequenceTable[i].size == 0) + seqDestIdx = sequenceTable[i].ptr; + + std::vector seqVec = std::vector(sequenceTable[seqDestIdx].size); + memcpy(seqVec.data(), audioSeqData.data() + sequenceTable[seqDestIdx].ptr, + sequenceTable[seqDestIdx].size); + + sequences.push_back(seqVec); + } +} + +std::string ZAudio::GetSourceTypeName() const +{ + return "u8"; +} + +size_t ZAudio::GetRawDataSize() const +{ + return 1; +} + +ZResourceType ZAudio::GetResourceType() const +{ + return ZResourceType::Audio; +} diff --git a/ZAPDTR/ZAPD/ZAudio.h b/ZAPDTR/ZAPD/ZAudio.h new file mode 100644 index 000000000..0c6c0875c --- /dev/null +++ b/ZAPDTR/ZAPD/ZAudio.h @@ -0,0 +1,130 @@ +#pragma once + +#include "ZResource.h" +#include "tinyxml2.h" + +struct AdsrEnvelope +{ + int16_t delay; + int16_t arg; +}; + +struct AdpcmBook +{ + /* 0x00 */ int32_t order; + /* 0x04 */ int32_t npredictors; + /* 0x08 */ std::vector books; // size 8 * order * npredictors. 8-byte aligned +}; + +struct AdpcmLoop +{ + /* 0x00 */ uint32_t start; + /* 0x04 */ uint32_t end; + /* 0x08 */ uint32_t count; + /* 0x10 */ std::vector states; +}; + +struct SampleEntry +{ + std::string fileName; + uint8_t bankId; + uint32_t sampleDataOffset; + uint32_t sampleLoopOffset = 0xFFFFFFFF; + uint8_t codec; + uint8_t medium; + uint8_t unk_bit26; + uint8_t unk_bit25; + + std::vector data; + AdpcmLoop loop; + AdpcmBook book; +}; + +struct SoundFontEntry +{ + SampleEntry* sampleEntry = nullptr; + float tuning; +}; + +struct DrumEntry +{ + uint8_t releaseRate; + uint8_t pan; + uint8_t loaded; + uint32_t offset; + float tuning; + std::vector env; + SampleEntry* sample = nullptr; +}; + +struct InstrumentEntry +{ + bool isValidInstrument; + uint8_t loaded; + uint8_t normalRangeLo; + uint8_t normalRangeHi; + uint8_t releaseRate; + std::vector env; + SoundFontEntry* lowNotesSound = nullptr; + SoundFontEntry* normalNotesSound = nullptr; + SoundFontEntry* highNotesSound = nullptr; +}; + +struct AudioTableEntry +{ + uint32_t ptr; + uint32_t size; + uint8_t medium; + uint8_t cachePolicy; + uint16_t data1; + uint16_t data2; + uint16_t data3; + + std::vector drums; + std::vector soundEffects; + std::vector instruments; +}; + +class ZAudio : public ZResource +{ +public: + std::vector soundFontTable; + std::vector sequenceTable; + std::vector sampleBankTable; + std::vector> sequences; + std::map samples; + std::vector> fontIndices; + std::vector seqNames; + std::map soundFontNames; + + // First Key = Bank ID, Sec Key = Sample Data Offset. + std::map> sampleOffsets; + + ZAudio(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + + std::vector ParseEnvelopeData(const std::vector& audioBank, const std::vector& audioTable, + int envelopeOffset, int baseOffset); + + SoundFontEntry* ParseSoundFontEntry(const std::vector& audioBank, + const std::vector& audioTable, + AudioTableEntry audioSampleBankEntry, int bankIndex, + int soundFontOffset, + int baseOffset); + + SampleEntry* ParseSampleEntry(const std::vector& audioBank, const std::vector& audioTable, + AudioTableEntry audioSampleBankEntry, int bankIndex, + int sampleOffset, int baseOffset); + + std::vector ParseAudioTable(const std::vector& codeData, int baseOffset); + void ParseSoundFont(const std::vector& codeData, const std::vector& audioTable, + const std::vector& audioSampleBank, AudioTableEntry& entry); + + void ParseRawData() override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZAudioDecode.cpp b/ZAPDTR/ZAPD/ZAudioDecode.cpp new file mode 100644 index 000000000..29b1c31d5 --- /dev/null +++ b/ZAPDTR/ZAPD/ZAudioDecode.cpp @@ -0,0 +1,670 @@ +/** + * Bruteforcing decoder for converting ADPCM-encoded AIFC into AIFF, in a way + * that roundtrips with vadpcm_enc. + */ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include +//#include + +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef float f32; +typedef double f64; + +#ifdef _MSC_VER +#define __builtin_bswap16 _byteswap_ushort +#define __builtin_bswap32 _byteswap_ulong +#endif + +#define bswap16(x) __builtin_bswap16(x) +#define bswap32(x) __builtin_bswap32(x) +#define BSWAP16(x) x = __builtin_bswap16(x) +#define BSWAP32(x) x = __builtin_bswap32(x) +#define BSWAP16_MANY(x, n) \ + for (s32 _i = 0; _i < n; _i++) \ + BSWAP16((x)[_i]) + +#define NORETURN __attribute__((noreturn)) +#define UNUSED __attribute__((unused)) + +typedef struct +{ + u32 ckID; + u32 ckSize; +} ChunkHeader; + +typedef struct +{ + u32 ckID; + u32 ckSize; + u32 formType; +} Chunk; + +typedef struct +{ + s16 numChannels; + u16 numFramesH; + u16 numFramesL; + s16 sampleSize; + s16 sampleRate[5]; // 80-bit float + u16 compressionTypeH; + u16 compressionTypeL; +} CommonChunk; + +typedef struct +{ + s16 MarkerID; + u16 positionH; + u16 positionL; +} Marker; + +typedef struct +{ + s16 playMode; + s16 beginLoop; + s16 endLoop; +} Loop; + +typedef struct +{ + s8 baseNote; + s8 detune; + s8 lowNote; + s8 highNote; + s8 lowVelocity; + s8 highVelocity; + s16 gain; + Loop sustainLoop; + Loop releaseLoop; +} InstrumentChunk; + +typedef struct +{ + s32 offset; + s32 blockSize; +} SoundDataChunk; + +typedef struct +{ + s16 version; + s16 order; + s16 nEntries; +} CodeChunk; + +typedef struct +{ + u32 start; + u32 end; + u32 count; + s16 state[16]; +} ALADPCMloop; + +static char usage[] = "input.aifc output.aiff"; +static const char *progname, *infilename; +static int framesize = 9; + +void fail_parse(const char* fmt, ...) +{ + char* formatted = NULL; + va_list ap; + va_start(ap, fmt); + int size = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + if (size >= 0) + { + size++; + formatted = (char*)malloc(size); + if (formatted != NULL) + { + va_start(ap, fmt); + size = vsnprintf(formatted, size, fmt, ap); + va_end(ap); + if (size < 0) + { + free(formatted); + formatted = NULL; + } + } + } + + if (formatted != NULL) + { + fprintf(stderr, "%s: %s [%s]\n", progname, formatted, infilename); + free(formatted); + } + exit(1); +} + + +s32 myrand() +{ + static u64 state = 1619236481962341ULL; + state *= 3123692312237ULL; + state += 1; + return state >> 33; +} + +s16 qsample(f32 x, s32 scale) +{ + if (x > 0.0f) + { + return (s16)((x / scale) + 0.4999999); + } + else + { + return (s16)((x / scale) - 0.4999999); + } +} + +void clamp_to_s16(f32* in, s32* out) +{ + f32 llevel = -0x8000; + f32 ulevel = 0x7fff; + + for (s32 i = 0; i < 16; i++) + { + if (in[i] > ulevel) + in[i] = ulevel; + if (in[i] < llevel) + in[i] = llevel; + + if (in[i] > 0.0f) + { + out[i] = (s32)(in[i] + 0.5); + } + else + { + out[i] = (s32)(in[i] - 0.5); + } + } +} + +s16 clamp_bits(s32 x, s32 bits) +{ + s32 lim = 1 << (bits - 1); + if (x < -lim) + return -lim; + if (x > lim - 1) + return lim - 1; + return x; +} + +s32 readaifccodebook(FILE* fhandle, s32**** table, s16* order, s16* npredictors) +{ + BSWAP16(*order); + BSWAP16(*npredictors); + *table = (s32***)malloc(*npredictors * sizeof(s32**)); + for (s32 i = 0; i < *npredictors; i++) + { + (*table)[i] = (s32**)malloc(8 * sizeof(s32*)); + for (s32 j = 0; j < 8; j++) + { + (*table)[i][j] = (s32*)malloc((*order + 8) * sizeof(s32)); + } + } + + for (s32 i = 0; i < *npredictors; i++) + { + s32** table_entry = (*table)[i]; + for (s32 j = 0; j < *order; j++) + { + for (s32 k = 0; k < 8; k++) + { + s16 ts = 0; + BSWAP16(ts); + table_entry[k][j] = ts; + } + } + + for (s32 k = 1; k < 8; k++) + { + table_entry[k][*order] = table_entry[k - 1][*order - 1]; + } + + table_entry[0][*order] = 1 << 11; + + for (s32 k = 1; k < 8; k++) + { + s32 j = 0; + for (; j < k; j++) + { + table_entry[j][k + *order] = 0; + } + + for (; j < 8; j++) + { + table_entry[j][k + *order] = table_entry[j - k][*order]; + } + } + } + return 0; +} + +ALADPCMloop* readlooppoints(FILE* ifile, s16* nloops) +{ + BSWAP16(*nloops); + ALADPCMloop* al = (ALADPCMloop*)malloc(*nloops * sizeof(ALADPCMloop)); + for (s32 i = 0; i < *nloops; i++) + { + BSWAP32(al[i].start); + BSWAP32(al[i].end); + BSWAP32(al[i].count); + BSWAP16_MANY(al[i].state, 16); + } + return al; +} + +s32 inner_product(s32 length, s32* v1, s32* v2) +{ + s32 out = 0; + for (s32 i = 0; i < length; i++) + { + out += v1[i] * v2[i]; + } + + // Compute "out / 2^11", rounded down. + s32 dout = out / (1 << 11); + s32 fiout = dout * (1 << 11); + return dout - (out - fiout < 0); +} + +void my_decodeframe(u8* frame, s32* decompressed, s32* state, s32 order, s32*** coefTable) +{ + s32 ix[16]; + + u8 header = frame[0]; + s32 scale = 1 << (header >> 4); + s32 optimalp = header & 0xf; + + if (framesize == 5) + { + for (s32 i = 0; i < 16; i += 4) + { + u8 c = frame[1 + i / 4]; + ix[i] = c >> 6; + ix[i + 1] = (c >> 4) & 0x3; + ix[i + 2] = (c >> 2) & 0x3; + ix[i + 3] = c & 0x3; + } + } + else + { + for (s32 i = 0; i < 16; i += 2) + { + u8 c = frame[1 + i / 2]; + ix[i] = c >> 4; + ix[i + 1] = c & 0xf; + } + } + + for (s32 i = 0; i < 16; i++) + { + if (framesize == 5) + { + if (ix[i] >= 2) + ix[i] -= 4; + } + else + { + if (ix[i] >= 8) + ix[i] -= 16; + } + decompressed[i] = ix[i]; + ix[i] *= scale; + } + + for (s32 j = 0; j < 2; j++) + { + s32 in_vec[16]; + if (j == 0) + { + for (s32 i = 0; i < order; i++) + { + in_vec[i] = state[16 - order + i]; + } + } + else + { + for (s32 i = 0; i < order; i++) + { + in_vec[i] = state[8 - order + i]; + } + } + + for (s32 i = 0; i < 8; i++) + { + s32 ind = j * 8 + i; + in_vec[order + i] = ix[ind]; + state[ind] = inner_product(order + i, coefTable[optimalp][i], in_vec) + ix[ind]; + } + } +} + +void get_bounds(s32* in, s32* decompressed, s32 scale, s32* minVals, s32* maxVals) +{ + s32 minv, maxv; + if (framesize == 9) + { + minv = -8; + maxv = 7; + } + else + { + minv = -2; + maxv = 1; + } + for (s32 i = 0; i < 16; i++) + { + s32 lo = in[i] - scale / 2; + s32 hi = in[i] + scale / 2; + lo -= scale; + hi += scale; + if (decompressed[i] == minv) + lo -= scale; + else if (decompressed[i] == maxv) + hi += scale; + minVals[i] = lo; + maxVals[i] = hi; + } +} + +void write_header(FILE* ofile, const char* id, s32 size) +{ + fwrite(id, 4, 1, ofile); + BSWAP32(size); + fwrite(&size, sizeof(s32), 1, ofile); +} + +char* OldMain(char* infilename) +{ + s16 order = -1; + s16 nloops = 0; + ALADPCMloop* aloops = NULL; + s16 npredictors = -1; + s32*** coefTable = NULL; + s32 state[16]; + s32 decompressed[16]; + s32 soundPointer = -1; + s32 currPos = 0; + s32 nSamples = 0; + Chunk FormChunk = Chunk(); + ChunkHeader Header = ChunkHeader(); + CommonChunk CommChunk = CommonChunk(); + InstrumentChunk InstChunk; + SoundDataChunk SndDChunk = SoundDataChunk(); + FILE* ifile = NULL; + FILE* ofile = NULL; + + if ((ifile = fopen(infilename, "rb")) == NULL) + { + fail_parse("AIFF-C file could not be opened"); + exit(1); + } + + memset(&InstChunk, 0, sizeof(InstChunk)); + + BSWAP32(FormChunk.ckID); + BSWAP32(FormChunk.formType); + if ((FormChunk.ckID != 0x464f524d) || (FormChunk.formType != 0x41494643)) + { // FORM, AIFC + fail_parse("not an AIFF-C file"); + } + + for (;;) + { + s32 num = fread(&Header, sizeof(Header), 1, ifile); + u32 ts = 0; + + if (num <= 0) + break; + + BSWAP32(Header.ckID); + BSWAP32(Header.ckSize); + + Header.ckSize++; + Header.ckSize &= ~1; + s32 offset = ftell(ifile); + + switch (Header.ckID) + { + case 0x434f4d4d: // COMM + { + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + BSWAP16(CommChunk.compressionTypeH); + BSWAP16(CommChunk.compressionTypeL); + s32 cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL; + if (cType == 0x56415043 || cType == 0x41445039) + { // VAPC or ADP9 + framesize = 9; + } + else if (cType == 0x41445035) + { // ADP5 + framesize = 5; + } + else if (cType == 0x4850434d) + { // HPCM + framesize = 16; + } + else + { + char comprType[5] = { + static_cast(CommChunk.compressionTypeH >> 8), static_cast(CommChunk.compressionTypeH & 0xFF), + static_cast(CommChunk.compressionTypeL >> 8), static_cast(CommChunk.compressionTypeL & 0xFF), 0}; + fail_parse("file is of the wrong compression type [got %s (%08x)]", &comprType, + cType); + } + if (CommChunk.numChannels != 1) + { + fail_parse("file contains %d channels, only 1 channel supported", + CommChunk.numChannels); + } + if (CommChunk.sampleSize != 16) + { + fail_parse("file contains %d bit samples, only 16 bit samples supported", + CommChunk.sampleSize); + } + + nSamples = (CommChunk.numFramesH << 16) + CommChunk.numFramesL; + + // Allow broken input lengths + if (nSamples % 16) + { + nSamples -= (nSamples % 16); + } + + if (nSamples % 16 != 0) + { + fail_parse("number of chunks must be a multiple of 16, found %d with remainder %d", + nSamples, nSamples % 16); + } + } + break; + + case 0x53534e44: // SSND + BSWAP32(SndDChunk.offset); + BSWAP32(SndDChunk.blockSize); + assert(SndDChunk.offset == 0); + assert(SndDChunk.blockSize == 0); + soundPointer = ftell(ifile); + break; + + case 0x4150504c: // APPL + BSWAP32(ts); + if (ts == 0x73746f63) + { // stoc + u8 len = 0; + if (len == 11) + { + char ChunkName[12]; + s16 version; + ChunkName[11] = '\0'; + if (strcmp("VADPCMCODES", ChunkName) == 0) + { + BSWAP16(version); + if (version != 1) + { + fail_parse("Unknown codebook chunk version"); + } + readaifccodebook(ifile, &coefTable, &order, &npredictors); + } + else if (strcmp("VADPCMLOOPS", ChunkName) == 0) + { + BSWAP16(version); + if (version != 1) + { + fail_parse("Unknown loop chunk version"); + } + aloops = readlooppoints(ifile, &nloops); + if (nloops != 1) + { + fail_parse("Only a single loop supported"); + } + } + } + } + break; + } + + fseek(ifile, offset + Header.ckSize, SEEK_SET); + } + + if (coefTable == NULL) + { + fail_parse("Codebook missing from bitstream"); + } + + for (s32 i = 0; i < order; i++) + { + state[15 - i] = 0; + } + + u32 outputBytes = nSamples * sizeof(s16); + u8* outputBuf = (u8*)malloc(outputBytes); + + fseek(ifile, soundPointer, SEEK_SET); + s32 fails = 0; + while (currPos < nSamples) + { + u8 input[9]; + u8 encoded[9]; + s32 lastState[16]; + s32 decoded[16]; + s16 guess[16]; + s16 origGuess[16]; + + memcpy(lastState, state, sizeof(state)); + + // Decode for real + my_decodeframe(input, decompressed, state, order, coefTable); + memcpy(decoded, state, sizeof(state)); + + // Create a guess from that, by clamping to 16 bits + for (s32 i = 0; i < 16; i++) + { + origGuess[i] = clamp_bits(state[i], 16); + } + + memcpy(state, decoded, sizeof(state)); + memcpy(outputBuf + currPos * 2, decoded, sizeof(decoded)); + currPos += 16; + } + if (fails) + { + fprintf(stderr, "%s %d\n", infilename, fails); + } + + // Write an incomplete file header. We'll fill in the size later. + fwrite("FORM\0\0\0\0AIFF", 12, 1, ofile); + + // Subtract 4 from the COMM size to skip the compression field. + write_header(ofile, "COMM", sizeof(CommonChunk) - 4); + CommChunk.numFramesH = nSamples >> 16; + CommChunk.numFramesL = nSamples & 0xffff; + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + fwrite(&CommChunk, sizeof(CommonChunk) - 4, 1, ofile); + + if (nloops > 0) + { + s32 startPos = aloops[0].start, endPos = aloops[0].end; + const char* markerNames[2] = {"start", "end"}; + Marker markers[2] = {{1, static_cast(startPos >> 16), static_cast(startPos & 0xffff)}, + {2, static_cast(endPos >> 16), static_cast(endPos & 0xffff)}}; + write_header(ofile, "MARK", 2 + 2 * sizeof(Marker) + 1 + 5 + 1 + 3); + s16 numMarkers = bswap16(2); + fwrite(&numMarkers, sizeof(s16), 1, ofile); + for (s32 i = 0; i < 2; i++) + { + u8 len = (u8)strlen(markerNames[i]); + BSWAP16(markers[i].MarkerID); + BSWAP16(markers[i].positionH); + BSWAP16(markers[i].positionL); + fwrite(&markers[i], sizeof(Marker), 1, ofile); + fwrite(&len, 1, 1, ofile); + fwrite(markerNames[i], len, 1, ofile); + } + + write_header(ofile, "INST", sizeof(InstrumentChunk)); + InstChunk.sustainLoop.playMode = bswap16(1); + InstChunk.sustainLoop.beginLoop = bswap16(1); + InstChunk.sustainLoop.endLoop = bswap16(2); + InstChunk.releaseLoop.playMode = 0; + InstChunk.releaseLoop.beginLoop = 0; + InstChunk.releaseLoop.endLoop = 0; + fwrite(&InstChunk, sizeof(InstrumentChunk), 1, ofile); + } + + // Save the coefficient table for use when encoding. Ideally this wouldn't + // be needed and "tabledesign -s 1" would generate the right table, but in + // practice it's difficult to adjust samples to make that happen. + write_header(ofile, "APPL", 4 + 12 + sizeof(CodeChunk) + npredictors * order * 8 * 2); + fwrite("stoc", 4, 1, ofile); + CodeChunk cChunk; + cChunk.version = bswap16(1); + cChunk.order = bswap16(order); + cChunk.nEntries = bswap16(npredictors); + fwrite("\x0bVADPCMCODES", 12, 1, ofile); + fwrite(&cChunk, sizeof(CodeChunk), 1, ofile); + for (s32 i = 0; i < npredictors; i++) + { + for (s32 j = 0; j < order; j++) + { + for (s32 k = 0; k < 8; k++) + { + s16 ts = bswap16(coefTable[i][k][j]); + fwrite(&ts, sizeof(s16), 1, ofile); + } + } + } + + write_header(ofile, "SSND", outputBytes + 8); + SndDChunk.offset = 0; + SndDChunk.blockSize = 0; + fwrite(&SndDChunk, sizeof(SoundDataChunk), 1, ofile); + fwrite(outputBuf, outputBytes, 1, ofile); + + // Fix the size in the header + s32 fileSize = bswap32(ftell(ofile) - 8); + fseek(ofile, 4, SEEK_SET); + fwrite(&fileSize, 4, 1, ofile); + + fclose(ifile); + fclose(ofile); + return 0; +} diff --git a/ZAPDTR/ZAPD/ZBackground.cpp b/ZAPDTR/ZAPD/ZBackground.cpp new file mode 100644 index 000000000..920945b98 --- /dev/null +++ b/ZAPDTR/ZAPD/ZBackground.cpp @@ -0,0 +1,200 @@ +#include "ZBackground.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Background, ZBackground); + +#define JPEG_MARKER 0xFFD8FFE0 +#define MARKER_DQT 0xFFDB +#define MARKER_EOI 0xFFD9 + +ZBackground::ZBackground(ZFile* nParent) : ZResource(nParent) +{ +} + +void ZBackground::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + size_t i = 0; + while (true) + { + uint8_t val = rawData.at(rawDataIndex + i); + data.push_back(val); + + if (BitConverter::ToUInt16BE(rawData, rawDataIndex + i) == MARKER_EOI) + { + data.push_back(rawData.at(rawDataIndex + i + 1)); + break; + } + + i++; + } +} + +void ZBackground::ParseBinaryFile(const std::string& inFolder, bool appendOutName) +{ + fs::path filepath(inFolder); + + if (appendOutName) + filepath = filepath / (outName + "." + GetExternalExtension()); + + data = DiskFile::ReadAllBytes(filepath.string()); + + // Add padding. + data.insert(data.end(), GetRawDataSize() - data.size(), 0x00); + CheckValidJpeg(filepath.generic_string()); +} + +void ZBackground::CheckValidJpeg(const std::string& filepath) +{ + std::string filename = outName; + if (filepath != "") + { + filename = filepath; + } + + uint32_t jpegMarker = BitConverter::ToUInt32BE(data, 0); + if (jpegMarker != JPEG_MARKER) + { + HANDLE_WARNING_PROCESS( + WarningType::InvalidJPEG, + StringHelper::Sprintf("missing jpeg marker at beginning of file: '%s'", + filename.c_str()), + "The game will skip this jpeg."); + } + if (data.at(6) != 'J' || data.at(7) != 'F' || data.at(8) != 'I' || data.at(9) != 'F' || + data.at(10) != '\0') + { + std::string jfifIdentifier(data.begin() + 6, data.begin() + 6 + 5); + HANDLE_WARNING_PROCESS( + WarningType::InvalidJPEG, "missing 'JFIF' identifier", + StringHelper::Sprintf( + "This image may be corrupted, or not a jpeg. The identifier found was: '%s'", + jfifIdentifier.c_str())); + } + uint8_t majorVersion = data.at(11); + uint8_t minorVersion = data.at(12); + if (majorVersion != 0x01 || minorVersion != 0x01) + { + HANDLE_WARNING_PROCESS( + WarningType::InvalidJPEG, + StringHelper::Sprintf("wrong JFIF version '%i.%02i'", majorVersion, minorVersion), + "The expected version is '1.01'. The game may be unable to decode this image " + "correctly."); + } + if (BitConverter::ToUInt16BE(data, 20) != MARKER_DQT) + { + // This may happen when creating a custom image with Exif, XMP, thumbnail, progressive, etc. + // enabled. + HANDLE_WARNING_PROCESS(WarningType::InvalidJPEG, + "there seems to be extra data before the image data in this file", + "The game may not be able to decode this image correctly."); + } + if (data.size() > GetRawDataSize()) + { + HANDLE_WARNING_PROCESS( + WarningType::InvalidJPEG, "the image is bigger than the screen buffer", + StringHelper::Sprintf("Image size: %zu bytes\nScreen buffer size: %zu bytes", + data.size(), GetRawDataSize())); + } +} + +size_t ZBackground::GetRawDataSize() const +{ + // Jpgs use the whole sceen buffer, which is a u16 matrix. + return Globals::Instance->cfg.bgScreenHeight * Globals::Instance->cfg.bgScreenWidth * 2; +} + +Declaration* ZBackground::DeclareVar(const std::string& prefix, + [[maybe_unused]] const std::string& bodyStr) +{ + std::string auxName = name; + std::string auxOutName = outName; + + if (auxName == "") + auxName = GetDefaultName(prefix); + + if (auxOutName == "") + auxOutName = GetDefaultName(prefix); + + auto filepath = Globals::Instance->outputPath / fs::path(auxOutName).stem(); + + std::string incStr = + StringHelper::Sprintf("%s.%s.inc.c", filepath.c_str(), GetExternalExtension().c_str()); + + Declaration* decl = parent->AddDeclarationIncludeArray(rawDataIndex, incStr, GetRawDataSize(), + GetSourceTypeName(), auxName, 0); + + if (Globals::Instance->cfg.useScreenWidthHeightConstants) + { + decl->arrayItemCntStr = "SCREEN_WIDTH * SCREEN_HEIGHT / 4"; + decl->forceArrayCnt = true; + } + + decl->staticConf = staticConf; + return decl; +} + +bool ZBackground::IsExternalResource() const +{ + return true; +} + +std::string ZBackground::GetExternalExtension() const +{ + return "jpg"; +} + +void ZBackground::Save(const fs::path& outFolder) +{ + if (!Globals::Instance->otrMode) + { + fs::path filepath = outFolder / (outName + "." + GetExternalExtension()); + DiskFile::WriteAllBytes(filepath.string(), data); + } +} + +std::string ZBackground::GetBodySourceCode() const +{ + std::string bodyStr = " "; + + for (size_t i = 0; i < data.size() / 8; ++i) + { + bodyStr += StringHelper::Sprintf("0x%016llX, ", BitConverter::ToUInt64BE(data, i * 8)); + + if (i % 8 == 7) + bodyStr += "\n "; + } + + bodyStr += "\n"; + + return bodyStr; +} + +std::string ZBackground::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sBackground_%06X", prefix.c_str(), rawDataIndex); +} + +std::string ZBackground::GetSourceTypeName() const +{ + return "u64"; +} + +ZResourceType ZBackground::GetResourceType() const +{ + return ZResourceType::Background; +} + +DeclarationAlignment ZBackground::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align8; +} diff --git a/ZAPDTR/ZAPD/ZBackground.h b/ZAPDTR/ZAPD/ZBackground.h new file mode 100644 index 000000000..e3728bd98 --- /dev/null +++ b/ZAPDTR/ZAPD/ZBackground.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include "ZResource.h" + +class ZBackground : public ZResource +{ +protected: + std::vector data; + +public: + ZBackground(ZFile* nParent); + + void ParseBinaryFile(const std::string& inFolder, bool appendOutName); + + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + std::string GetDefaultName(const std::string& prefix) const override; + + void Save(const fs::path& outFolder) override; + + bool IsExternalResource() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + std::string GetExternalExtension() const override; + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; + + void CheckValidJpeg(const std::string& filepath); +}; diff --git a/ZAPDTR/ZAPD/ZBlob.cpp b/ZAPDTR/ZAPD/ZBlob.cpp new file mode 100644 index 000000000..0965d7e4e --- /dev/null +++ b/ZAPDTR/ZAPD/ZBlob.cpp @@ -0,0 +1,116 @@ +#include "ZBlob.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Blob, ZBlob); + +ZBlob::ZBlob(ZFile* nParent) : ZResource(nParent) +{ + genOTRDef = true; + RegisterRequiredAttribute("Size"); +} + +ZBlob* ZBlob::FromFile(const std::string& filePath) +{ + ZBlob* blob = new ZBlob(nullptr); + blob->name = StringHelper::Split(Path::GetFileNameWithoutExtension(filePath), ".")[0]; + blob->blobData = DiskFile::ReadAllBytes(filePath); + + return blob; +} + +void ZBlob::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + blobSize = StringHelper::StrToL(registeredAttributes.at("Size").value, 16); +} + +void ZBlob::ParseRawData() +{ + blobData.assign(parent->GetRawData().begin() + rawDataIndex, + parent->GetRawData().begin() + rawDataIndex + blobSize); +} + +Declaration* ZBlob::DeclareVar(const std::string& prefix, + [[maybe_unused]] const std::string& bodyStr) +{ + std::string auxName = name; + std::string auxOutName = outName; + + if (auxName == "") + auxName = GetDefaultName(prefix); + + if (auxOutName == "") + auxOutName = GetDefaultName(prefix); + + std::string path = Path::GetFileNameWithoutExtension(auxOutName); + + std::string assetOutDir = + (Globals::Instance->outputPath / Path::GetFileNameWithoutExtension(GetOutName())).string(); + + std::string incStr = + StringHelper::Sprintf("%s.%s.inc.c", assetOutDir.c_str(), GetExternalExtension().c_str()); + + return parent->AddDeclarationIncludeArray(rawDataIndex, incStr, GetRawDataSize(), + GetSourceTypeName(), auxName, blobData.size()); +} + +std::string ZBlob::GetBodySourceCode() const +{ + std::string sourceOutput; + + for (size_t i = 0; i < blobData.size(); i += 1) + { + if (i % 16 == 0) + sourceOutput += "\t"; + + sourceOutput += StringHelper::Sprintf("0x%02X, ", blobData[i]); + + if (i % 16 == 15) + sourceOutput += "\n"; + } + + // Ensure there's always a trailing line feed to prevent dumb warnings. + // Please don't remove this line, unless you somehow made a way to prevent + // that warning when building the OoT repo. + sourceOutput += "\n"; + + return sourceOutput; +} + +void ZBlob::Save(const fs::path& outFolder) +{ + if (!Globals::Instance->otrMode) + DiskFile::WriteAllBytes((outFolder / (name + ".bin")).string(), blobData); +} + +bool ZBlob::IsExternalResource() const +{ + return true; +} + +std::string ZBlob::GetExternalExtension() const +{ + return "bin"; +} + +std::string ZBlob::GetSourceTypeName() const +{ + return "u8"; +} + +ZResourceType ZBlob::GetResourceType() const +{ + return ZResourceType::Blob; +} + +size_t ZBlob::GetRawDataSize() const +{ + return blobSize; +} diff --git a/ZAPDTR/ZAPD/ZBlob.h b/ZAPDTR/ZAPD/ZBlob.h new file mode 100644 index 000000000..d7a7feff1 --- /dev/null +++ b/ZAPDTR/ZAPD/ZBlob.h @@ -0,0 +1,31 @@ +#pragma once + +#include "ZResource.h" +#include "tinyxml2.h" + +class ZBlob : public ZResource +{ +public: + ZBlob(ZFile* nParent); + + static ZBlob* FromFile(const std::string& filePath); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + void Save(const fs::path& outFolder) override; + + bool IsExternalResource() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + std::string GetExternalExtension() const override; + + size_t GetRawDataSize() const override; + +protected: + std::vector blobData; + size_t blobSize = 0; +}; diff --git a/ZAPDTR/ZAPD/ZCKeyFrame.cpp b/ZAPDTR/ZAPD/ZCKeyFrame.cpp new file mode 100644 index 000000000..044eafde5 --- /dev/null +++ b/ZAPDTR/ZAPD/ZCKeyFrame.cpp @@ -0,0 +1,307 @@ +#include "ZCKeyFrame.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" + +REGISTER_ZFILENODE(KeyFrameSkel, ZKeyFrameSkel); +REGISTER_ZFILENODE(KeyFrameLimbList, ZKeyFrameLimbList); + +ZKeyFrameSkel::ZKeyFrameSkel(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("LimbType"); + genOTRDef = true; +} + +ZKeyFrameSkel::~ZKeyFrameSkel() +{ +} + +ZKeyFrameLimb::ZKeyFrameLimb(ZFile* nParent) : ZResource(nParent) +{ +} + +ZKeyFrameStandardLimb::ZKeyFrameStandardLimb(ZFile* nParent) : ZKeyFrameLimb(nParent) +{ +} + +ZKeyFrameFlexLimb::ZKeyFrameFlexLimb(ZFile* nParent) : ZKeyFrameLimb(nParent) +{ +} + +ZKeyFrameLimbList::ZKeyFrameLimbList(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("LimbType"); + RegisterRequiredAttribute("LimbCount"); +} + +ZKeyFrameLimbList::ZKeyFrameLimbList(ZFile* nParent, uint32_t limbCount, ZKeyframeSkelType type) + : ZResource(nParent) +{ + numLimbs = limbCount; + limbType = type; +} + +ZKeyFrameLimbList::~ZKeyFrameLimbList() +{ + for (const auto l : limbs) + delete l; +} + +void ZKeyFrameSkel::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string limbTypeStr = registeredAttributes.at("LimbType").value; + + limbType = ZKeyFrameLimbList::ParseLimbTypeStr(limbTypeStr); + if (limbType == ZKeyframeSkelType::Error) + HANDLE_ERROR_RESOURCE( + WarningType::InvalidXML, parent, this, rawDataIndex, "Invalid limb type", + StringHelper::Sprintf("Invalid limb type. Was expecting 'Flex' or 'Normal'. Got %s.", + limbTypeStr.c_str())); +} +void ZKeyFrameLimbList::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string limbTypeStr = registeredAttributes.at("LimbType").value; + std::string numLimbStr = registeredAttributes.at("LimbCount").value; + + limbType = ParseLimbTypeStr(limbTypeStr); + + if (limbType == ZKeyframeSkelType::Error) + HANDLE_ERROR_RESOURCE( + WarningType::InvalidXML, parent, this, rawDataIndex, "Invalid limb type", + StringHelper::Sprintf("Invalid limb type. Was expecting 'Flex' or 'Normal'. Got %s.", + limbTypeStr.c_str())); + + numLimbs = (uint8_t)StringHelper::StrToL(numLimbStr); +} + +void ZKeyFrameSkel::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + limbCount = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0); + dListCount = BitConverter::ToUInt8BE(rawData, rawDataIndex + 1); + limbsPtr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 4); + + limbList = std::make_unique(parent, limbCount, limbType); + limbList->SetRawDataIndex(GETSEGOFFSET(limbsPtr)); + limbList->ParseRawData(); +} + +void ZKeyFrameSkel::DeclareReferences(const std::string& prefix) +{ + std::string defaultPrefix = name; + std::string declaration; + + if (defaultPrefix == "") + defaultPrefix = prefix; + + ZResource::DeclareReferences(defaultPrefix); + declaration += limbList->GetBodySourceCode(); + parent->AddDeclarationArray( + GETSEGOFFSET(limbsPtr), DeclarationAlignment::Align4, limbList->GetRawDataSize(), + limbList->GetSourceTypeName(), + StringHelper::Sprintf("%s_KeyFrameLimbs_%06X", prefix.c_str(), rawDataIndex), + limbList->limbs.size(), declaration); +} + +std::string ZKeyFrameSkel::GetBodySourceCode() const +{ + std::string limbStr; + + if (limbType == ZKeyframeSkelType::Normal) + Globals::Instance->GetSegmentedPtrName(limbsPtr, parent, "KeyFrameStandardLimb", limbStr, + parent->workerID); + else + Globals::Instance->GetSegmentedPtrName(limbsPtr, parent, "KeyFrameFlexLimb", limbStr, + parent->workerID); + + return StringHelper::Sprintf("\n\t0x%02X, 0x%02X, %s\n", limbCount, dListCount, + limbStr.c_str()); +} + +size_t ZKeyFrameSkel::GetRawDataSize() const +{ + return 0x8; +} + +std::string ZKeyFrameSkel::GetSourceTypeName() const +{ + return "KeyFrameSkeleton"; +} + +ZResourceType ZKeyFrameSkel::GetResourceType() const +{ + return ZResourceType::KeyFrameSkel; +} + +size_t ZKeyFrameStandardLimb::GetRawDataSize() const +{ + return 0xC; +} + +size_t ZKeyFrameFlexLimb::GetRawDataSize() const +{ + return 0x8; +} + +size_t ZKeyFrameLimbList::GetRawDataSize() const +{ + size_t limbSize; + if (limbType == ZKeyframeSkelType::Flex) + limbSize = 0x8; + else + limbSize = 0xC; + + return limbSize * numLimbs; +} + +ZKeyframeSkelType ZKeyFrameLimbList::ParseLimbTypeStr(const std::string& typeStr) +{ + if (typeStr == "Flex") + return ZKeyframeSkelType::Flex; + else if (typeStr == "Normal") + return ZKeyframeSkelType::Normal; + else + return ZKeyframeSkelType::Error; +} + +void ZKeyFrameLimb::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + dlist = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x0); + numChildren = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x4); + flags = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x5); +} + +void ZKeyFrameStandardLimb::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + ZKeyFrameLimb::ParseRawData(); + translation.x = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x6); + translation.y = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x8); + translation.z = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0xA); +} + +void ZKeyFrameFlexLimb::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + ZKeyFrameLimb::ParseRawData(); + callbackIndex = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x6); +} + +void ZKeyFrameLimbList::ParseRawData() +{ + limbs.reserve(numLimbs); + rawDataIndex = GetRawDataIndex(); + + for (uint32_t i = 0; i < numLimbs; i++) + { + ZKeyFrameLimb* limb; + if (limbType == ZKeyframeSkelType::Flex) + limb = new ZKeyFrameFlexLimb(parent); + else + limb = new ZKeyFrameStandardLimb(parent); + + limb->SetRawDataIndex(rawDataIndex + (offset_t)(i * limb->GetRawDataSize())); + limb->ParseRawData(); + limbs.push_back(limb); + } +} + +std::string ZKeyFrameLimbList::GetBodySourceCode() const +{ + std::string declaration; + + for (const auto l : limbs) + declaration += StringHelper::Sprintf("\t{ %s },\n", l->GetBodySourceCode().c_str()); + // Remove last newline + return declaration.substr(0, declaration.length() - 1); +} + +std::string ZKeyFrameStandardLimb::GetBodySourceCode() const +{ + std::string declaration; + std::string dlString; + + Globals::Instance->GetSegmentedArrayIndexedName(dlist, 8, parent, "Gfx", dlString, + parent->workerID); + + declaration += + StringHelper::Sprintf("%s, 0x%02X, 0x%02X, { 0x%04X, 0x%04X, 0x%04X},", dlString.c_str(), + numChildren, flags, translation.x, translation.y, translation.z); + return declaration; +} + +std::string ZKeyFrameFlexLimb::GetBodySourceCode() const +{ + std::string declaration; + + std::string dlString; + + Globals::Instance->GetSegmentedArrayIndexedName(dlist, 8, parent, "Gfx", dlString, + parent->workerID); + + declaration += StringHelper::Sprintf("%s, 0x%02X, 0x%02X, 0x%02X", dlString.c_str(), + numChildren, flags, callbackIndex); + return declaration; +} + +std::string ZKeyFrameStandardLimb::GetSourceTypeName() const +{ + return "KeyFrameStandardLimb"; +} + +std::string ZKeyFrameFlexLimb::GetSourceTypeName() const +{ + return "KeyFrameFlexLimb"; +} + +std::string ZKeyFrameLimbList::GetSourceTypeName() const +{ + switch (limbType) + { + case ZKeyframeSkelType::Flex: + return "KeyFrameFlexLimb"; + case ZKeyframeSkelType::Normal: + return "KeyFrameStandardLimb"; + default: + HANDLE_ERROR_RESOURCE(WarningType::InvalidXML, parent, this, rawDataIndex, + "Invalid limb type", ""); + break; + } +} + +ZResourceType ZKeyFrameStandardLimb::GetResourceType() const +{ + return ZResourceType::KeyFrameStandardLimb; +} + +ZResourceType ZKeyFrameFlexLimb::GetResourceType() const +{ + return ZResourceType::KeyFrameFlexLimb; +} + +ZResourceType ZKeyFrameLimbList::GetResourceType() const +{ + switch (limbType) + { + case ZKeyframeSkelType::Flex: + return ZResourceType::KeyFrameFlexLimb; + case ZKeyframeSkelType::Normal: + return ZResourceType::KeyFrameStandardLimb; + default: + HANDLE_ERROR_RESOURCE(WarningType::InvalidXML, parent, this, rawDataIndex, + "Invalid limb type", ""); + break; + } +} diff --git a/ZAPDTR/ZAPD/ZCKeyFrame.h b/ZAPDTR/ZAPD/ZCKeyFrame.h new file mode 100644 index 000000000..417d7cc68 --- /dev/null +++ b/ZAPDTR/ZAPD/ZCKeyFrame.h @@ -0,0 +1,121 @@ +#pragma once + +#include +#include +#include +#include + +#include "ZFile.h" + +class ZKeyFrameLimb; + +struct Vec3s +{ + int16_t x; + int16_t y; + int16_t z; +}; + +enum class ZKeyframeSkelType +{ + Normal, + Flex, + Error, +}; + +class ZKeyFrameLimbList : public ZResource +{ +public: + ZKeyFrameLimbList(); + ZKeyFrameLimbList(ZFile* nParent); + ZKeyFrameLimbList(ZFile* nParent, uint32_t limbCount, ZKeyframeSkelType type); + + ~ZKeyFrameLimbList(); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + void ParseXML(tinyxml2::XMLElement* reader) override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + + static ZKeyframeSkelType ParseLimbTypeStr(const std::string& typeStr); + + std::vector limbs; + ZKeyframeSkelType limbType; + uint8_t numLimbs; +}; + +class ZKeyFrameLimb : public ZResource +{ +public: + segptr_t dlist; + uint8_t numChildren; + uint8_t flags; + + ZKeyFrameLimb(ZFile* nParent); + void ParseRawData() override; +}; + +class ZKeyFrameStandardLimb : public ZKeyFrameLimb +{ +public: + Vec3s translation; + + ZKeyFrameStandardLimb(ZFile* nParent); + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; + +class ZKeyFrameFlexLimb : public ZKeyFrameLimb +{ +public: + uint8_t callbackIndex; + + ZKeyFrameFlexLimb(ZFile* nParent); + // void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + // std::string GetSourceOutputHeader(const std::string& prefix) override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; + +class ZKeyFrameSkel : public ZResource +{ +public: + std::unique_ptr limbList; + segptr_t limbsPtr; + ZKeyframeSkelType limbType; + uint8_t limbCount; + uint8_t dListCount; + + ZKeyFrameSkel(ZFile* nParent); + ~ZKeyFrameSkel(); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZCKeyFrameAnim.cpp b/ZAPDTR/ZAPD/ZCKeyFrameAnim.cpp new file mode 100644 index 000000000..fffb9f2ac --- /dev/null +++ b/ZAPDTR/ZAPD/ZCKeyFrameAnim.cpp @@ -0,0 +1,221 @@ +#include "ZCkeyFrameAnim.h" +#include "ZCKeyFrame.h" +#include "Globals.h" + +#include "Utils/BitConverter.h" + +REGISTER_ZFILENODE(KeyFrameAnimation, ZKeyFrameAnim); + +ZKeyFrameAnim::ZKeyFrameAnim(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("Skel"); + genOTRDef = true; +} + +ZKeyFrameAnim::~ZKeyFrameAnim() +{ +} + +void ZKeyFrameAnim::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string skelAddrStr = registeredAttributes.at("Skel").value; + skelOffset = (offset_t)StringHelper::StrToL(skelAddrStr, 16); +} + +void ZKeyFrameAnim::DeclareReferencesLate(const std::string& prefix) +{ + std::string defaultPrefix = name; + std::string declaration; + + if (defaultPrefix == "") + defaultPrefix = prefix; + + ZResource::DeclareReferences(defaultPrefix); + + declaration += "\t"; + + if (skel->limbType == ZKeyframeSkelType::Normal) + { + for (const auto b : bitFlags) + { + declaration += StringHelper::Sprintf("0x%02X, ", b); + parent->AddDeclarationArray( + GETSEGOFFSET(bitFlagsAddr), DeclarationAlignment::Align4, bitFlags.size(), "u8", + StringHelper::Sprintf("%s_bitFlags_%06X", prefix.c_str(), rawDataIndex), + bitFlags.size(), declaration); + } + } + else + { + for (const auto b : bitFlagsFlex) + { + declaration += StringHelper::Sprintf("0x%04X, ", b); + parent->AddDeclarationArray( + GETSEGOFFSET(bitFlagsAddr), DeclarationAlignment::Align4, bitFlagsFlex.size() * 2, + "u16", StringHelper::Sprintf("%s_flexBitFlags_%06X", prefix.c_str(), rawDataIndex), + bitFlagsFlex.size(), declaration); + } + } + declaration.clear(); + + for (const auto kf : keyFrames) + { + declaration += + StringHelper::Sprintf(" \t { %i, %i, %i, },\n", kf.frame, kf.value, kf.velocity); + } + // Remove last new line to prevent an extra line after the last element + declaration = declaration.substr(0, declaration.length() - 1); + parent->AddDeclarationArray( + GETSEGOFFSET(keyFramesAddr), DeclarationAlignment::Align4, keyFrames.size() * 6, "KeyFrame", + StringHelper::Sprintf("%s_KeyFrame_%06X", prefix.c_str(), rawDataIndex), keyFrames.size(), + declaration); + + declaration.clear(); + + declaration += "\t"; + + for (const auto kfNum : kfNums) + { + declaration += StringHelper::Sprintf("0x%04X, ", kfNum); + } + + parent->AddDeclarationArray( + GETSEGOFFSET(kfNumsAddr), DeclarationAlignment::Align4, kfNums.size() * 2, "s16", + StringHelper::Sprintf("%s_kfNums_%06X", prefix.c_str(), rawDataIndex), kfNums.size(), + declaration); + declaration += "\n"; + + declaration.clear(); + + declaration += "\t"; + + for (const auto pv : presetValues) + { + declaration += StringHelper::Sprintf("0x%04X, ", pv); + } + declaration += "\n"; + parent->AddDeclarationArray( + GETSEGOFFSET(presentValuesAddr), DeclarationAlignment::Align4, presetValues.size() * 2, + "s16", StringHelper::Sprintf("%s_presetValues_%06X", prefix.c_str(), rawDataIndex), + presetValues.size(), declaration); + +} + +// ParseRawDataLate is used because we need to make sure the flex skel has been processed first. +void ZKeyFrameAnim::ParseRawDataLate() +{ + const auto& rawData = parent->GetRawData(); + + skel = static_cast(parent->FindResource(skelOffset)); + size_t numLimbs = skel->limbCount; + + bitFlagsAddr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x0); + keyFramesAddr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x4); + kfNumsAddr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x8); + presentValuesAddr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0xC); + + uint32_t kfNumsSize = 0; + uint32_t presetValuesSize = 0; + uint32_t keyFramesCount = 0; + if (skel->limbType == ZKeyframeSkelType::Normal) + { + bitFlags.reserve(numLimbs); + for (size_t i = 0; i < numLimbs; i++) + { + uint8_t e = BitConverter::ToUInt8BE(rawData, GETSEGOFFSET(bitFlagsAddr) + i); + bitFlags.push_back(e); + kfNumsSize += GetSetBits((uint8_t)(e & 0b111111)); + presetValuesSize += GetSetBits((uint8_t)((e ^ 0xFF) & 0b111111)); + } + } + else + { + bitFlagsFlex.reserve(numLimbs); + for (size_t i = 0; i < numLimbs; i++) + { + uint16_t e = BitConverter::ToUInt16BE(rawData, GETSEGOFFSET(bitFlagsAddr) + (i * 2)); + bitFlagsFlex.push_back(e); + kfNumsSize += GetSetBits((uint16_t)(e & 0b111111111)); + presetValuesSize += GetSetBits((uint16_t)((e ^ 0xFFFF) & 0b111111111)); + } + } + + kfNums.reserve(kfNumsSize); + for (uint32_t i = 0; i < kfNumsSize; i++) + { + int16_t kfNum = BitConverter::ToUInt16BE(rawData, GETSEGOFFSET(kfNumsAddr) + (i * 2)); + keyFramesCount += kfNum; + kfNums.push_back(kfNum); + } + + keyFrames.reserve(keyFramesCount); + for (uint32_t i = 0; i < keyFramesCount; i++) + { + KeyFrame kf; + kf.frame = BitConverter::ToInt16BE(rawData, (GETSEGOFFSET(keyFramesAddr) + 0) + (i * 6)); + kf.value = BitConverter::ToInt16BE(rawData, (GETSEGOFFSET(keyFramesAddr) + 2) + (i * 6)); + kf.velocity = BitConverter::ToInt16BE(rawData, (GETSEGOFFSET(keyFramesAddr) + 4) + (i * 6)); + keyFrames.push_back(kf); + } + + presetValues.reserve(presetValuesSize); + for (uint32_t i = 0; i < presetValuesSize; i++) + { + presetValues.push_back( + BitConverter::ToInt16BE(rawData, GETSEGOFFSET(presentValuesAddr) + (i * 2))); + } + + unk_10 = BitConverter::ToInt16BE(rawData, GETSEGOFFSET(rawDataIndex) + 0x10); + duration = BitConverter::ToInt16BE(rawData, GETSEGOFFSET(rawDataIndex) + 0x12); +} + +std::string ZKeyFrameAnim::GetBodySourceCode() const +{ + std::string declaration; + + std::string bitFlagsStr; + std::string keyFrameStr; + std::string kfNumsStr; + std::string presetValuesStr; + + Globals::Instance->GetSegmentedPtrName(bitFlagsAddr, parent, "", bitFlagsStr, parent->workerID); + Globals::Instance->GetSegmentedPtrName(keyFramesAddr, parent, "", keyFrameStr, parent->workerID); + Globals::Instance->GetSegmentedPtrName(kfNumsAddr, parent, "", kfNumsStr, parent->workerID); + Globals::Instance->GetSegmentedPtrName(presentValuesAddr, parent, "", presetValuesStr, + parent->workerID); + + return StringHelper::Sprintf("\n\t%s, %s, %s, %s, 0x%04X, 0x%04X\n", bitFlagsStr.c_str(), + keyFrameStr.c_str(), kfNumsStr.c_str(), presetValuesStr.c_str(), + unk_10, duration); +} + +std::string ZKeyFrameAnim::GetSourceTypeName() const +{ + return "KeyFrameAnimation"; +} + +ZResourceType ZKeyFrameAnim::GetResourceType() const +{ + return ZResourceType::KeyFrameAnimation; +} + +size_t ZKeyFrameAnim::GetRawDataSize() const +{ + return 0x14; +} + +template +uint32_t ZKeyFrameAnim::GetSetBits(T data) const +{ + uint32_t num = 0; + + for (size_t i = 0; i < sizeof(T) * 8; i++) + { + if ((data >> i) & 1) + num++; + } + + return num; +} \ No newline at end of file diff --git a/ZAPDTR/ZAPD/ZCkeyFrameAnim.h b/ZAPDTR/ZAPD/ZCkeyFrameAnim.h new file mode 100644 index 000000000..64f95b13a --- /dev/null +++ b/ZAPDTR/ZAPD/ZCkeyFrameAnim.h @@ -0,0 +1,52 @@ +#pragma once +#include +#include +#include +#include + +#include "ZFile.h" + +class ZKeyFrameSkel; + +typedef struct +{ + int16_t frame; + int16_t value; + int16_t velocity; +} KeyFrame; + +class ZKeyFrameAnim : public ZResource +{ +public: + ZKeyFrameSkel* skel; + std::vector bitFlags; // Standard only + std::vector bitFlagsFlex; // Flex only + + std::vector keyFrames; + std::vector kfNums; + std::vector presetValues; + + uint16_t unk_10; + int16_t duration; + + ZKeyFrameAnim(ZFile* nParent); + ~ZKeyFrameAnim(); + void ParseXML(tinyxml2::XMLElement* reader) override; + void DeclareReferencesLate(const std::string& prefix) override; + void ParseRawDataLate() override; + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +private: + offset_t skelOffset; + segptr_t bitFlagsAddr; + segptr_t keyFramesAddr; + segptr_t kfNumsAddr; + segptr_t presentValuesAddr; + template + uint32_t GetSetBits(T data) const; +}; diff --git a/ZAPDTR/ZAPD/ZCollision.cpp b/ZAPDTR/ZAPD/ZCollision.cpp new file mode 100644 index 000000000..7a724e513 --- /dev/null +++ b/ZAPDTR/ZAPD/ZCollision.cpp @@ -0,0 +1,451 @@ +#include "ZCollision.h" +#include "ZWaterbox.h" + +#include +#include +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +REGISTER_ZFILENODE(Collision, ZCollisionHeader); + +ZCollisionHeader::ZCollisionHeader(ZFile* nParent) : ZResource(nParent) +{ + genOTRDef = true; +} + +ZCollisionHeader::~ZCollisionHeader() +{ + delete camData; +} + +void ZCollisionHeader::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + absMinX = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + absMinY = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + absMinZ = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + + absMaxX = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + absMaxY = BitConverter::ToInt16BE(rawData, rawDataIndex + 8); + absMaxZ = BitConverter::ToInt16BE(rawData, rawDataIndex + 10); + + numVerts = BitConverter::ToUInt16BE(rawData, rawDataIndex + 12); + vtxAddress = BitConverter::ToInt32BE(rawData, rawDataIndex + 16); + + numPolygons = BitConverter::ToUInt16BE(rawData, rawDataIndex + 20); + polyAddress = BitConverter::ToInt32BE(rawData, rawDataIndex + 24); + polyTypeDefAddress = BitConverter::ToInt32BE(rawData, rawDataIndex + 28); + camDataAddress = BitConverter::ToInt32BE(rawData, rawDataIndex + 32); + + numWaterBoxes = BitConverter::ToUInt16BE(rawData, rawDataIndex + 36); + waterBoxAddress = BitConverter::ToInt32BE(rawData, rawDataIndex + 40); + + vtxSegmentOffset = Seg2Filespace(vtxAddress, parent->baseAddress); + polySegmentOffset = Seg2Filespace(polyAddress, parent->baseAddress); + polyTypeDefSegmentOffset = Seg2Filespace(polyTypeDefAddress, parent->baseAddress); + camDataSegmentOffset = Seg2Filespace(camDataAddress, parent->baseAddress); + waterBoxSegmentOffset = Seg2Filespace(waterBoxAddress, parent->baseAddress); + + vertices.reserve(numVerts); + polygons.reserve(numPolygons); + waterBoxes.reserve(numWaterBoxes); + + offset_t currentPtr = vtxSegmentOffset; + + for (uint16_t i = 0; i < numVerts; i++) + { + ZVector vec(parent); + vec.ExtractFromBinary(currentPtr, ZScalarType::ZSCALAR_S16, 3); + + currentPtr += vec.GetRawDataSize(); + vertices.push_back(vec); + } + + for (uint16_t i = 0; i < numPolygons; i++) + { + ZCollisionPoly poly(parent); + poly.SetRawDataIndex(polySegmentOffset + (i * 16)); + poly.ParseRawData(); + polygons.push_back(poly); + } + + uint16_t highestPolyType = 0; + + for (const ZCollisionPoly& poly : polygons) + { + if (poly.type > highestPolyType) + highestPolyType = poly.type; + } + + for (uint16_t i = 0; i < highestPolyType + 1; i++) + { + ZSurfaceType surfaceType(parent); + surfaceType.SetRawDataIndex(polyTypeDefSegmentOffset + (i * 8)); + surfaceType.ParseRawData(); + polygonTypes.push_back(surfaceType); + } + // polygonTypes.push_back( + // BitConverter::ToUInt64BE(rawData, polyTypeDefSegmentOffset + (i * 8))); + + if (camDataAddress != SEGMENTED_NULL) + { + // Try to guess how many elements the CamDataList array has. + // The "guessing algorithm" is basically a "best effort" one and it + // is error-prone. + // This is based mostly on observation of how CollisionHeader data is + // usually ordered. If for some reason the data was in some other funny + // order, this would probably break. + // The most common ordering is: + // - *BgCamInfo* + // - SurfaceType + // - CollisionPoly + // - Vertices + // - WaterBoxes + // - CollisionHeader + offset_t upperCameraBoundary = polyTypeDefSegmentOffset; + if (upperCameraBoundary == SEGMENTED_NULL) + { + upperCameraBoundary = polySegmentOffset; + } + if (upperCameraBoundary == SEGMENTED_NULL) + { + upperCameraBoundary = vtxSegmentOffset; + } + if (upperCameraBoundary == SEGMENTED_NULL) + { + upperCameraBoundary = waterBoxSegmentOffset; + } + if (upperCameraBoundary == SEGMENTED_NULL) + { + upperCameraBoundary = rawDataIndex; + } + + // Sharp Ocarina places the CamDataEntries above the list so we need to calculate the number + // of cameras differently. + if (upperCameraBoundary < camDataSegmentOffset) + { + offset_t offset = camDataSegmentOffset; + while (rawData[offset] == 0x00 && rawData[offset + 0x4] == 0x02) + { + offset += 0x08; + } + upperCameraBoundary = offset; + } + + camData = + new CameraDataList(parent, name, rawData, camDataSegmentOffset, upperCameraBoundary); + } + + for (int32_t i = 0; i < numWaterBoxes; i++) + { + ZWaterbox waterbox(parent); + + waterbox.SetRawDataIndex(waterBoxSegmentOffset + + (i * (Globals::Instance->game == ZGame::OOT_SW97 ? 12 : 16))); + waterbox.ParseRawData(); + waterBoxes.emplace_back(waterbox); + } +} + +void ZCollisionHeader::DeclareReferences(const std::string& prefix) +{ + std::string declaration = ""; + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + if (waterBoxes.size() > 0) + { + if (!Globals::Instance->otrMode) + { + for (size_t i = 0; i < waterBoxes.size(); i++) + { + declaration += + StringHelper::Sprintf("\t{ %s },", waterBoxes[i].GetBodySourceCode().c_str()); + if (i + 1 < waterBoxes.size()) + declaration += "\n"; + } + } + + parent->AddDeclarationArray(waterBoxSegmentOffset, DeclarationAlignment::Align4, + waterBoxes[0].GetRawDataSize() * waterBoxes.size(), + waterBoxes[0].GetSourceTypeName().c_str(), + StringHelper::Sprintf("%sWaterBoxes", auxName.c_str()), + waterBoxes.size(), declaration); + } + + if (polygons.size() > 0) + { + declaration.clear(); + + if (!Globals::Instance->otrMode) + { + for (size_t i = 0; i < polygons.size(); i++) + { + declaration += StringHelper::Sprintf("\t%s,", polygons[i].GetBodySourceCode().c_str()); + if (i + 1 < polygons.size()) + declaration += "\n"; + } + } + + parent->AddDeclarationArray(polySegmentOffset, DeclarationAlignment::Align4, + polygons.size() * 16, polygons[0].GetSourceTypeName().c_str(), + StringHelper::Sprintf("%sPolygons", auxName.c_str()), + polygons.size(), declaration); + } + + declaration.clear(); + for (const auto& polyType : polygonTypes) + { + declaration += StringHelper::Sprintf("\t%s,", polyType.GetBodySourceCode().c_str()); + } + + if (polyTypeDefAddress != SEGMENTED_NULL) + parent->AddDeclarationArray(polyTypeDefSegmentOffset, DeclarationAlignment::Align4, + polygonTypes.size() * 8, + polygonTypes[0].GetSourceTypeName().c_str(), + StringHelper::Sprintf("%sSurfaceType", auxName.c_str()), + polygonTypes.size(), declaration); + + declaration.clear(); + + if (vertices.size() > 0) + { + declaration.clear(); + + if (!Globals::Instance->otrMode) + { + for (size_t i = 0; i < vertices.size(); i++) + { + declaration += + StringHelper::Sprintf("\t{ %s },", vertices[i].GetBodySourceCode().c_str()); + + if (i < vertices.size() - 1) + declaration += "\n"; + } + } + + const auto& first = vertices.front(); + if (vtxAddress != 0) + parent->AddDeclarationArray( + vtxSegmentOffset, first.GetDeclarationAlignment(), + vertices.size() * first.GetRawDataSize(), first.GetSourceTypeName(), + StringHelper::Sprintf("%sVertices", auxName.c_str()), vertices.size(), declaration); + } +} + +std::string ZCollisionHeader::GetBodySourceCode() const +{ + std::string declaration = ""; + + if (Globals::Instance->otrMode) + return declaration; + + declaration += "\n"; + + declaration += StringHelper::Sprintf("\t{ %i, %i, %i },\n", absMinX, absMinY, absMinZ); + declaration += StringHelper::Sprintf("\t{ %i, %i, %i },\n", absMaxX, absMaxY, absMaxZ); + + std::string vtxName; + Globals::Instance->GetSegmentedPtrName(vtxAddress, parent, "Vec3s", vtxName, parent->workerID); + + if (numVerts > 0) + declaration += + StringHelper::Sprintf("\tARRAY_COUNT(%s), %s,\n", vtxName.c_str(), vtxName.c_str()); + else + declaration += StringHelper::Sprintf("\t%i, %s,\n", numVerts, vtxName.c_str()); + + std::string polyName; + Globals::Instance->GetSegmentedPtrName(polyAddress, parent, "CollisionPoly", polyName, parent->workerID); + + if (numPolygons > 0) + declaration += + StringHelper::Sprintf("\tARRAY_COUNT(%s), %s,\n", polyName.c_str(), polyName.c_str()); + else + declaration += StringHelper::Sprintf("\t%i, %s,\n", numPolygons, polyName.c_str()); + + std::string surfaceName; + Globals::Instance->GetSegmentedPtrName(polyTypeDefAddress, parent, "SurfaceType", surfaceName, parent->workerID); + declaration += StringHelper::Sprintf("\t%s,\n", surfaceName.c_str()); + + std::string camName; + Globals::Instance->GetSegmentedPtrName(camDataAddress, parent, "BgCamInfo", camName, parent->workerID); + declaration += StringHelper::Sprintf("\t%s,\n", camName.c_str()); + + std::string waterBoxName; + Globals::Instance->GetSegmentedPtrName(waterBoxAddress, parent, "WaterBox", waterBoxName, parent->workerID); + + if (numWaterBoxes > 0) + declaration += StringHelper::Sprintf("\tARRAY_COUNT(%s), %s\n", waterBoxName.c_str(), + waterBoxName.c_str()); + else + declaration += StringHelper::Sprintf("\t%i, %s\n", numWaterBoxes, waterBoxName.c_str()); + + return declaration; +} + +std::string ZCollisionHeader::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sCol_%06X", prefix.c_str(), rawDataIndex); +} + +std::string ZCollisionHeader::GetSourceTypeName() const +{ + return "CollisionHeader"; +} + +ZResourceType ZCollisionHeader::GetResourceType() const +{ + return ZResourceType::CollisionHeader; +} + +size_t ZCollisionHeader::GetRawDataSize() const +{ + return 44; +} +#if 0 +WaterBoxHeader::WaterBoxHeader(const std::vector& rawData, uint32_t rawDataIndex) +{ + xMin = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + ySurface = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + zMin = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + xLength = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + zLength = BitConverter::ToInt16BE(rawData, rawDataIndex + 8); + + if (Globals::Instance->game == ZGame::OOT_SW97) + properties = BitConverter::ToInt16BE(rawData, rawDataIndex + 10); + else + properties = BitConverter::ToInt32BE(rawData, rawDataIndex + 12); +} + +std::string WaterBoxHeader::GetBodySourceCode() const +{ + return StringHelper::Sprintf("%i, %i, %i, %i, %i, 0x%08X", xMin, ySurface, zMin, xLength, + zLength, properties); +} +#endif +CameraDataList::CameraDataList(ZFile* parent, const std::string& prefix, + const std::vector& rawData, offset_t rawDataIndex, + offset_t upperCameraBoundary) +{ + std::string declaration; + + // Parse CameraDataEntries + size_t numElements = (upperCameraBoundary - rawDataIndex) / 8; + assert(numElements < 10000); + + offset_t cameraPosDataSeg = rawDataIndex; + uint32_t numDataTotal; + uint32_t cameraPosDataSegEnd = rawDataIndex; + bool isSharpOcarina = false; + + for (size_t i = 0; i < numElements; i++) + { + CameraDataEntry entry; + + entry.cameraSType = + BitConverter::ToInt16BE(rawData, rawDataIndex + (entries.size() * 8) + 0); + entry.numData = BitConverter::ToInt16BE(rawData, rawDataIndex + (entries.size() * 8) + 2); + entry.cameraPosDataSeg = + BitConverter::ToInt32BE(rawData, rawDataIndex + (entries.size() * 8) + 4); + + if (entry.cameraPosDataSeg != 0 && GETSEGNUM(entry.cameraPosDataSeg) != SEGMENT_SCENE) + { + cameraPosDataSeg = rawDataIndex + (entries.size() * 8); + break; + } + + if (rawDataIndex > GETSEGOFFSET(entry.cameraPosDataSeg)) + { + if (entry.cameraPosDataSeg != 0 && + cameraPosDataSeg > GETSEGOFFSET(entry.cameraPosDataSeg)) + cameraPosDataSeg = GETSEGOFFSET(entry.cameraPosDataSeg); + } + else + { + // Sharp Ocarina will place the cam data after the list as opposed to the original maps + // which have it before. + isSharpOcarina = true; + cameraPosDataSeg = rawDataIndex + (numElements * 0x8); + if (cameraPosDataSegEnd < GETSEGOFFSET(entry.cameraPosDataSeg)) + cameraPosDataSegEnd = GETSEGOFFSET(entry.cameraPosDataSeg); + } + + entries.emplace_back(entry); + } + + // Setting cameraPosDataAddr to rawDataIndex give a pos list length of 0 + uint32_t cameraPosDataOffset = GETSEGOFFSET(cameraPosDataSeg); + for (size_t i = 0; i < entries.size(); i++) + { + char camSegLine[2048]; + + if (entries[i].cameraPosDataSeg != 0) + { + uint32_t index = + (GETSEGOFFSET(entries[i].cameraPosDataSeg) - cameraPosDataOffset) / 0x6; + snprintf(camSegLine, 2048, "&%sCamPosData[%i]", prefix.c_str(), index); + } + else + snprintf(camSegLine, 2048, "NULL"); + + declaration += + StringHelper::Sprintf(" { 0x%04X, %i, %s },", entries[i].cameraSType, + entries[i].numData, camSegLine, rawDataIndex + (i * 8)); + + if (i < entries.size() - 1) + declaration += "\n"; + } + + parent->AddDeclarationArray( + rawDataIndex, DeclarationAlignment::Align4, entries.size() * 8, "BgCamInfo", + StringHelper::Sprintf("%sCamDataList", prefix.c_str(), rawDataIndex), entries.size(), + declaration); + + if (!isSharpOcarina) + numDataTotal = (rawDataIndex - cameraPosDataOffset) / 0x6; + else + numDataTotal = ((cameraPosDataSegEnd - cameraPosDataSeg) + 18) / 0x6; + + if (numDataTotal > 0) + { + declaration.clear(); + cameraPositionData.reserve(numDataTotal); + for (uint32_t i = 0; i < numDataTotal; i++) + { + CameraPositionData data = CameraPositionData(rawData, cameraPosDataOffset + (i * 6)); + + declaration += StringHelper::Sprintf("\t{ %6i, %6i, %6i },", data.x, data.y, data.z); + cameraPositionData.emplace_back(data); + if (i + 1 < numDataTotal) + declaration += "\n"; + } + + uint32_t cameraPosDataIndex = GETSEGOFFSET(cameraPosDataSeg); + uint32_t entrySize = numDataTotal * 0x6; + parent->AddDeclarationArray(cameraPosDataIndex, DeclarationAlignment::Align4, entrySize, + "Vec3s", StringHelper::Sprintf("%sCamPosData", prefix.c_str()), + numDataTotal, declaration); + } +} + +CameraDataList::~CameraDataList() +{ + //for (auto entry : entries) + // delete entry; +// + //for (auto camPosData : cameraPositionData) + // delete camPosData; +} + +CameraPositionData::CameraPositionData(const std::vector& rawData, uint32_t rawDataIndex) +{ + x = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + y = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + z = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); +} diff --git a/ZAPDTR/ZAPD/ZCollision.h b/ZAPDTR/ZAPD/ZCollision.h new file mode 100644 index 000000000..8c73d2eaa --- /dev/null +++ b/ZAPDTR/ZAPD/ZCollision.h @@ -0,0 +1,75 @@ +#pragma once + +#include "ZCollisionPoly.h" +#include "ZFile.h" +#include "ZResource.h" +#include "ZRoom/ZRoom.h" +#include "ZSurfaceType.h" +#include "ZVector.h" +#include "ZWaterbox.h" + +class CameraPositionData +{ +public: + int16_t x, y, z; + + CameraPositionData(const std::vector& rawData, uint32_t rawDataIndex); +}; + +class CameraDataEntry +{ +public: + int16_t cameraSType; + int16_t numData; + offset_t cameraPosDataSeg; +}; + +class CameraDataList +{ +public: + std::vector entries; + std::vector cameraPositionData; + + CameraDataList(ZFile* parent, const std::string& prefix, const std::vector& rawData, + offset_t rawDataIndex, offset_t upperCameraBoundary); + ~CameraDataList(); +}; + +class ZCollisionHeader : public ZResource +{ +public: + int16_t absMinX, absMinY, absMinZ; + int16_t absMaxX, absMaxY, absMaxZ; + uint16_t numVerts; + segptr_t vtxAddress; + uint16_t numPolygons; + segptr_t polyAddress; + segptr_t polyTypeDefAddress; + segptr_t camDataAddress; + + int32_t numWaterBoxes; + segptr_t waterBoxAddress; + + uint32_t vtxSegmentOffset, polySegmentOffset, polyTypeDefSegmentOffset, camDataSegmentOffset, + waterBoxSegmentOffset; + + std::vector vertices; + std::vector polygons; + std::vector polygonTypes; + std::vector waterBoxes; + CameraDataList* camData = nullptr; + + ZCollisionHeader(ZFile* nParent); + ~ZCollisionHeader(); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + std::string GetDefaultName(const std::string& prefix) const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZCollisionPoly.cpp b/ZAPDTR/ZAPD/ZCollisionPoly.cpp new file mode 100644 index 000000000..c4eb2d461 --- /dev/null +++ b/ZAPDTR/ZAPD/ZCollisionPoly.cpp @@ -0,0 +1,78 @@ +#include "ZCollisionPoly.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +REGISTER_ZFILENODE(CollisionPoly, ZCollisionPoly); + +ZCollisionPoly::ZCollisionPoly(ZFile* nParent) : ZResource(nParent) +{ +} + +ZCollisionPoly::~ZCollisionPoly() +{ +} + +void ZCollisionPoly::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + type = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0); + + vtxA = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + vtxB = BitConverter::ToUInt16BE(rawData, rawDataIndex + 4); + vtxC = BitConverter::ToUInt16BE(rawData, rawDataIndex + 6); + + normX = BitConverter::ToUInt16BE(rawData, rawDataIndex + 8); + normY = BitConverter::ToUInt16BE(rawData, rawDataIndex + 10); + normZ = BitConverter::ToUInt16BE(rawData, rawDataIndex + 12); + + dist = BitConverter::ToUInt16BE(rawData, rawDataIndex + 14); +} + +void ZCollisionPoly::DeclareReferences(const std::string& prefix) +{ + std::string declaration; + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + parent->AddDeclaration(rawDataIndex, DeclarationAlignment::Align4, GetRawDataSize(), + GetSourceTypeName(), name.c_str(), GetBodySourceCode()); +} + +std::string ZCollisionPoly::GetBodySourceCode() const +{ + std::string declaration; + + declaration += + StringHelper::Sprintf("{0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X}", + type, vtxA, vtxB, vtxC, normX, normY, normZ, dist); + return declaration; +} + +std::string ZCollisionPoly::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sCollisionPoly_%06X", prefix.c_str(), rawDataIndex); +} + +ZResourceType ZCollisionPoly::GetResourceType() const +{ + return ZResourceType::CollisionPoly; +} + +size_t ZCollisionPoly::GetRawDataSize() const +{ + return 16; +} + +std::string ZCollisionPoly::GetSourceTypeName() const +{ + return "CollisionPoly"; +} + +bool ZCollisionPoly::DoesSupportArray() const +{ + return true; +} \ No newline at end of file diff --git a/ZAPDTR/ZAPD/ZCollisionPoly.h b/ZAPDTR/ZAPD/ZCollisionPoly.h new file mode 100644 index 000000000..64b98ba1d --- /dev/null +++ b/ZAPDTR/ZAPD/ZCollisionPoly.h @@ -0,0 +1,29 @@ +#pragma once + +#include "ZFile.h" +#include "ZResource.h" + +class ZCollisionPoly : public ZResource +{ +public: + uint16_t type; + uint16_t vtxA, vtxB, vtxC; + uint16_t normX, normY, normZ; + uint16_t dist; + + ZCollisionPoly(ZFile* nParent); + ~ZCollisionPoly(); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + std::string GetDefaultName(const std::string& prefix) const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + bool DoesSupportArray() const override; + + size_t GetRawDataSize() const override; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/ZCutscene.cpp b/ZAPDTR/ZAPD/ZCutscene.cpp new file mode 100644 index 000000000..75fb60c60 --- /dev/null +++ b/ZAPDTR/ZAPD/ZCutscene.cpp @@ -0,0 +1,375 @@ +#include "ZCutscene.h" + +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZResource.h" + +REGISTER_ZFILENODE(Cutscene, ZCutscene); + +ZCutscene::ZCutscene(ZFile* nParent) : ZResource(nParent) +{ + genOTRDef = true; +} + +ZCutscene::~ZCutscene() +{ + for (CutsceneCommand* cmd : commands) + delete cmd; +} + +std::string ZCutscene::GetBodySourceCode() const +{ + std::string output = ""; + + output += StringHelper::Sprintf(" CS_BEGIN_CUTSCENE(%i, %i),\n", commands.size(), endFrame); + + for (size_t i = 0; i < commands.size(); i++) + { + CutsceneCommand* cmd = commands[i]; + output += " " + cmd->GenerateSourceCode(); + } + + output += StringHelper::Sprintf(" CS_END(),", commands.size(), endFrame); + + return output; +} + +size_t ZCutscene::GetRawDataSize() const +{ + size_t size = 0; + + // Beginning + size += 8; + + for (size_t i = 0; i < commands.size(); i++) + { + CutsceneCommand* cmd = commands[i]; + + size += cmd->GetCommandSize(); + } + + // End + if (Globals::Instance->game == ZGame::MM_RETAIL) + { + size += 4; + } + else + { + size += 8; + } + + return size; +} + +void ZCutscene::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + + numCommands = BitConverter::ToInt32BE(rawData, rawDataIndex + 0); + commands = std::vector(); + + endFrame = BitConverter::ToInt32BE(rawData, rawDataIndex + 4); + offset_t currentPtr = rawDataIndex + 8; + commands.reserve(numCommands); + for (int32_t i = 0; i < numCommands; i++) + { + uint32_t id = BitConverter::ToUInt32BE(rawData, currentPtr); + + if (id == 0xFFFFFFFF) + break; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + { + printf("Cutscene Command: 0x%X (%i)\n", id, id); + } + + currentPtr += 4; + + CutsceneCommand* cmd = nullptr; + + if (Globals::Instance->game == ZGame::MM_RETAIL) + { + cmd = GetCommandMM(id, currentPtr); + } + else + { + cmd = GetCommandOoT(id, currentPtr); + } + + if (cmd == nullptr) + { + HANDLE_WARNING_RESOURCE( + WarningType::NotImplemented, parent, this, rawDataIndex, + StringHelper::Sprintf("Cutscene command not implemented"), + StringHelper::Sprintf("Command ID: 0x%X\nIndex: %d\ncurrentPtr-rawDataIndex: 0x%X", + id, i, currentPtr - rawDataIndex)); + cmd = new CutsceneMMCommand_NonImplemented(rawData, currentPtr); + } + + assert(cmd != nullptr); + + cmd->commandIndex = i; + cmd->SetCommandID(id); + size_t commmandSize = cmd->GetCommandSize(); + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + { + printf("\t Command size: 0x%zX (%zu)\n", commmandSize, commmandSize); + } + currentPtr += commmandSize - 4; + + commands.push_back(cmd); + } +} + +CutsceneCommand* ZCutscene::GetCommandOoT(uint32_t id, offset_t currentPtr) const +{ + CutsceneOoT_CommandType cmdID = static_cast(id); + + const auto& rawData = parent->GetRawData(); + + switch (cmdID) + { + case CutsceneOoT_CommandType::CS_CMD_PLAYER_CUE: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_1: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_8_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_7: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_7: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_8: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_7: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_8: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_7: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_9: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_10: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_8: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_9: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_8: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_2: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_9: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_11: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_10: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_9: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_11: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_10: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_12: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_3: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_4: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_12: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_10: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_13: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_13: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_14: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_11: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_14: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_15: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_12: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_11: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_7: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_16: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_2_13: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_3_12: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_5: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_4_8: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_5_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_6_7: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_15: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_16: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_1_17: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_7_6: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_9_0: + case CutsceneOoT_CommandType::CS_CMD_ACTOR_CUE_0_17: + return new CutsceneOoTCommand_ActorCue(rawData, currentPtr); + + case CutsceneOoT_CommandType::CS_CMD_MISC: + case CutsceneOoT_CommandType::CS_CMD_LIGHT_SETTING: + case CutsceneOoT_CommandType::CS_CMD_START_SEQ: + case CutsceneOoT_CommandType::CS_CMD_STOP_SEQ: + case CutsceneOoT_CommandType::CS_CMD_FADE_OUT_SEQ: + return new CutsceneOoTCommand_GenericCmd(rawData, currentPtr, cmdID); + + case CutsceneOoT_CommandType::CS_CMD_CAM_EYE_SPLINE: + case CutsceneOoT_CommandType::CS_CMD_CAM_AT_SPLINE: + case CutsceneOoT_CommandType::CS_CMD_CAM_EYE_SPLINE_REL_TO_PLAYER: + case CutsceneOoT_CommandType::CS_CMD_CAM_AT_SPLINE_REL_TO_PLAYER: + return new CutsceneOoTCommand_GenericCameraCmd(rawData, currentPtr); + + case CutsceneOoT_CommandType::CS_CMD_RUMBLE_CONTROLLER: + return new CutsceneOoTCommand_Rumble(rawData, currentPtr); + + case CutsceneOoT_CommandType::CS_CMD_TEXT: + return new CutsceneOoTCommand_Text(rawData, currentPtr); + + case CutsceneOoT_CommandType::CS_CMD_TRANSITION: + return new CutsceneOoTCommand_Transition(rawData, currentPtr); + + case CutsceneOoT_CommandType::CS_CMD_TIME: + return new CutsceneCommand_Time(rawData, currentPtr); + + case CutsceneOoT_CommandType::CS_CMD_DESTINATION: + return new CutsceneOoTCommand_Destination(rawData, currentPtr); + + case CutsceneOoT_CommandType::CS_CMD_CAM_EYE: + case CutsceneOoT_CommandType::CS_CMD_CAM_AT: + break; + + default: + std::string errorHeader = + StringHelper::Sprintf("Warning: Invalid cutscene command ID: '0x%04X'", cmdID); + return new CutsceneOoTCommand_GenericCmd(rawData, currentPtr, cmdID); + } + + return nullptr; +} + +CutsceneCommand* ZCutscene::GetCommandMM(uint32_t id, offset_t currentPtr) const +{ + CutsceneMM_CommandType cmdID = static_cast(id); + + const auto& rawData = parent->GetRawData(); + + if (((id >= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_100) && + (id <= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_149)) || + (id == (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_201) || + ((id >= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_450) && + (id <= (uint32_t)CutsceneMM_CommandType::CS_CMD_ACTOR_CUE_599))) + { + return new CutsceneMMCommand_ActorCue(rawData, currentPtr); + } + + switch (cmdID) + { + case CutsceneMM_CommandType::CS_CMD_MISC: + case CutsceneMM_CommandType::CS_CMD_LIGHT_SETTING: + case CutsceneMM_CommandType::CS_CMD_TRANSITION: + case CutsceneMM_CommandType::CS_CMD_MOTION_BLUR: + case CutsceneMM_CommandType::CS_CMD_GIVE_TATL: + case CutsceneMM_CommandType::CS_CMD_START_SEQ: + case CutsceneMM_CommandType::CS_CMD_SFX_REVERB_INDEX_2: + case CutsceneMM_CommandType::CS_CMD_SFX_REVERB_INDEX_1: + case CutsceneMM_CommandType::CS_CMD_MODIFY_SEQ: + case CutsceneMM_CommandType::CS_CMD_STOP_SEQ: + case CutsceneMM_CommandType::CS_CMD_START_AMBIENCE: + case CutsceneMM_CommandType::CS_CMD_FADE_OUT_AMBIENCE: + case CutsceneMM_CommandType::CS_CMD_DESTINATION: + case CutsceneMM_CommandType::CS_CMD_CHOOSE_CREDITS_SCENES: + + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_FA: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_FE: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_FF: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_100: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_101: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_102: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_103: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_104: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_105: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_108: + case CutsceneMM_CommandType::CS_CMD_UNK_DATA_109: + return new CutsceneMMCommand_GenericCmd(rawData, currentPtr, cmdID); + + case CutsceneMM_CommandType::CS_CMD_TEXT: + return new CutsceneMMCommand_Text(rawData, currentPtr); + + case CutsceneMM_CommandType::CS_CMD_CAMERA_SPLINE: + return new CutsceneMMCommand_Spline(rawData, currentPtr); + + case CutsceneMM_CommandType::CS_CMD_TRANSITION_GENERAL: + return new CutsceneMMCommand_TransitionGeneral(rawData, currentPtr); + + case CutsceneMM_CommandType::CS_CMD_FADE_OUT_SEQ: + return new CutsceneMMCommand_FadeOutSeq(rawData, currentPtr); + + case CutsceneMM_CommandType::CS_CMD_TIME: + return new CutsceneCommand_Time(rawData, currentPtr); + + case CutsceneMM_CommandType::CS_CMD_PLAYER_CUE: + return new CutsceneMMCommand_ActorCue(rawData, currentPtr); + + case CutsceneMM_CommandType::CS_CMD_RUMBLE: + return new CutsceneMMCommand_Rumble(rawData, currentPtr); + + default: + std::string errorHeader = + StringHelper::Sprintf("Warning: Invalid cutscene command ID: '0x%04X'", cmdID); + return new CutsceneMMCommand_GenericCmd(rawData, currentPtr, cmdID); + } + + return nullptr; +} + +Declaration* ZCutscene::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (auxName == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), auxName, 0, bodyStr); + decl->staticConf = staticConf; + return decl; +} + +std::string ZCutscene::GetSourceTypeName() const +{ + return "CutsceneData"; +} + +ZResourceType ZCutscene::GetResourceType() const +{ + return ZResourceType::Cutscene; +} diff --git a/ZAPDTR/ZAPD/ZCutscene.h b/ZAPDTR/ZAPD/ZCutscene.h new file mode 100644 index 000000000..5b54426ce --- /dev/null +++ b/ZAPDTR/ZAPD/ZCutscene.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include "tinyxml2.h" + +#include "OtherStructs/CutsceneOoT_Commands.h" +#include "OtherStructs/CutsceneMM_Commands.h" +#include "ZFile.h" +#include "ZResource.h" + +class ZCutscene : public ZResource +{ +public: + ZCutscene(ZFile* nParent); + ~ZCutscene(); + + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + + std::string GetBodySourceCode() const override; + + size_t GetRawDataSize() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + int32_t numCommands; + int32_t endFrame; + std::vector commands; + +protected: + CutsceneCommand* GetCommandOoT(uint32_t id, offset_t currentPtr) const; + CutsceneCommand* GetCommandMM(uint32_t id, offset_t currentPtr) const; +}; diff --git a/ZAPDTR/ZAPD/ZDisplayList.cpp b/ZAPDTR/ZAPD/ZDisplayList.cpp new file mode 100644 index 000000000..03a1f016d --- /dev/null +++ b/ZAPDTR/ZAPD/ZDisplayList.cpp @@ -0,0 +1,2324 @@ +#define _CRT_SECURE_NO_WARNINGS +#include "ZDisplayList.h" + +#include +#include +#include +#include +#include + +#include "Globals.h" +#include "OutputFormatter.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "gfxd.h" + + +#define G_MDSFT_ALPHACOMPARE 0 +#define G_MDSFT_ZSRCSEL 2 +#define G_MDSFT_RENDERMODE 3 +#define G_MDSFT_BLENDER 16 + +#define G_RM_FOG_SHADE_A 0xC8000000 +#define G_RM_FOG_PRIM_A 0xC4000000 +#define G_RM_PASS 0x0C080000 +#define G_RM_AA_ZB_OPA_SURF 0x442078 +#define G_RM_AA_ZB_OPA_SURF2 0x112078 +#define G_RM_AA_ZB_XLU_SURF 0x4049D8 +#define G_RM_AA_ZB_XLU_SURF2 0x1049D8 +#define G_RM_AA_ZB_OPA_DECAL 0x442D58 +#define G_RM_AA_ZB_OPA_DECAL2 0x112D58 +#define G_RM_AA_ZB_XLU_DECAL 0x404DD8 +#define G_RM_AA_ZB_XLU_DECAL2 0x104DD8 +#define G_RM_AA_ZB_OPA_INTER 0x442478 +#define G_RM_AA_ZB_OPA_INTER2 0x112478 +#define G_RM_AA_ZB_XLU_INTER 0x4045D8 +#define G_RM_AA_ZB_XLU_INTER2 0x1045D8 +#define G_RM_AA_ZB_XLU_LINE 0x407858 +#define G_RM_AA_ZB_XLU_LINE2 0x107858 +#define G_RM_AA_ZB_DEC_LINE 0x407F58 +#define G_RM_AA_ZB_DEC_LINE2 0x107F58 +#define G_RM_AA_ZB_TEX_EDGE 0x443078 +#define G_RM_AA_ZB_TEX_EDGE2 0x113078 +#define G_RM_AA_ZB_TEX_INTER 0x443478 +#define G_RM_AA_ZB_TEX_INTER2 0x113478 +#define G_RM_AA_ZB_SUB_SURF 0x442878 +#define G_RM_AA_ZB_SUB_SURF2 0x112278 +#define G_RM_AA_ZB_PCL_SURF 0x40007B +#define G_RM_AA_ZB_PCL_SURF2 0x10007B +#define G_RM_AA_ZB_OPA_TERR 0x402078 +#define G_RM_AA_ZB_OPA_TERR2 0x102078 +#define G_RM_AA_ZB_TEX_TERR 0x403078 +#define G_RM_AA_ZB_TEX_TERR2 0x103078 +#define G_RM_AA_ZB_SUB_TERR 0x402278 +#define G_RM_AA_ZB_SUB_TERR2 0x102278 +#define G_RM_RA_ZB_OPA_SURF 0x442038 +#define G_RM_RA_ZB_OPA_SURF2 0x112038 +#define G_RM_RA_ZB_OPA_DECAL 0x442D18 +#define G_RM_RA_ZB_OPA_DECAL2 0x112D18 +#define G_RM_RA_ZB_OPA_INTER 0x442438 +#define G_RM_RA_ZB_OPA_INTER2 0x112438 +#define G_RM_AA_OPA_SURF 0x442048 +#define G_RM_AA_OPA_SURF2 0x112048 +#define G_RM_AA_XLU_SURF 0x4041C8 +#define G_RM_AA_XLU_SURF2 0x1041C8 +#define G_RM_AA_XLU_LINE 0x407048 +#define G_RM_AA_XLU_LINE2 0x107048 +#define G_RM_AA_DEC_LINE 0x407248 +#define G_RM_AA_DEC_LINE2 0x107248 +#define G_RM_AA_TEX_EDGE 0x443048 +#define G_RM_AA_TEX_EDGE2 0x113048 +#define G_RM_AA_SUB_SURF 0x442248 +#define G_RM_AA_SUB_SURF2 0x112248 +#define G_RM_AA_PCL_SURF 0x40004B +#define G_RM_AA_PCL_SURF2 0x10004B +#define G_RM_AA_OPA_TERR 0x402048 +#define G_RM_AA_OPA_TERR2 0x102048 +#define G_RM_AA_TEX_TERR 0x403048 +#define G_RM_AA_TEX_TERR2 0x103048 +#define G_RM_AA_SUB_TERR 0x402248 +#define G_RM_AA_SUB_TERR2 0x102248 +#define G_RM_RA_OPA_SURF 0x442008 +#define G_RM_RA_OPA_SURF2 0x112008 +#define G_RM_ZB_OPA_SURF 0x442230 +#define G_RM_ZB_OPA_SURF2 0x112230 +#define G_RM_ZB_XLU_SURF 0x404A50 +#define G_RM_ZB_XLU_SURF2 0x104A50 +#define G_RM_ZB_OPA_DECAL 0x442E10 +#define G_RM_ZB_OPA_DECAL2 0x112E10 +#define G_RM_ZB_XLU_DECAL 0x404E50 +#define G_RM_ZB_XLU_DECAL2 0x104E50 +#define G_RM_ZB_CLD_SURF 0x404B50 +#define G_RM_ZB_CLD_SURF2 0x104B50 +#define G_RM_ZB_OVL_SURF 0x404F50 +#define G_RM_ZB_OVL_SURF2 0x104F50 +#define G_RM_ZB_PCL_SURF 0x0C080233 +#define G_RM_ZB_PCL_SURF2 0x03020233 +#define G_RM_OPA_SURF 0x0C084000 +#define G_RM_OPA_SURF2 0x03024000 +#define G_RM_XLU_SURF 0x00404200 +#define G_RM_XLU_SURF2 0x00104240 +#define G_RM_CLD_SURF 0x00404340 +#define G_RM_CLD_SURF2 0x00104340 +#define G_RM_TEX_EDGE 0x0C087008 +#define G_RM_TEX_EDGE2 0x03027008 +#define G_RM_PCL_SURF 0x0C084203 +#define G_RM_PCL_SURF2 0x03024203 +#define G_RM_ADD 0x04484340 +#define G_RM_ADD2 0x01124340 +#define G_RM_NOOP 0x00000000 +#define G_RM_NOOP2 0x00000000 +#define G_RM_VISCVG 0x0C844040 +#define G_RM_VISCVG2 0x03214040 +#define G_RM_OPA_CI 0x0C080000 +#define G_RM_OPA_CI2 0x03020000 + +#define AA_EN 0x8 +#define Z_CMP 0x10 +#define Z_UPD 0x20 +#define IM_RD 0x40 +#define CLR_ON_CVG 0x80 +#define CVG_DST_CLAMP 0 +#define CVG_DST_WRAP 0x100 +#define CVG_DST_FULL 0x200 +#define CVG_DST_SAVE 0x300 +#define ZMODE_OPA 0 +#define ZMODE_INTER 0x400 +#define ZMODE_XLU 0x800 +#define ZMODE_DEC 0xc00 +#define CVG_X_ALPHA 0x1000 +#define ALPHA_CVG_SEL 0x2000 +#define FORCE_BL 0x4000 +#define TEX_EDGE 0x0000 + +REGISTER_ZFILENODE(DList, ZDisplayList); + +ZDisplayList::ZDisplayList(ZFile* nParent) : ZResource(nParent) +{ + lastTexWidth = 0; + lastTexHeight = 0; + lastTexAddr = 0; + lastTexFmt = F3DZEXTexFormats::G_IM_FMT_RGBA; + lastTexSiz = F3DZEXTexSizes::G_IM_SIZ_16b; + lastTexSizTest = F3DZEXTexSizes::G_IM_SIZ_16b; + lastTexLoaded = false; + lastTexIsPalette = false; + dListType = Globals::Instance->game == ZGame::OOT_SW97 ? DListType::F3DEX : DListType::F3DZEX; + genOTRDef = true; + RegisterOptionalAttribute("Ucode"); +} + +ZDisplayList::~ZDisplayList() +{ + for (auto o : otherDLists) + { + delete o; + } +} + +// EXTRACT MODE +void ZDisplayList::ExtractWithXML(tinyxml2::XMLElement* reader, uint32_t nRawDataIndex) +{ + rawDataIndex = nRawDataIndex; + ParseXML(reader); + // TODO add error handling here + bool ucodeSet = registeredAttributes.at("Ucode").wasSet; + std::string ucodeValue = registeredAttributes.at("Ucode").value; + if ((Globals::Instance->game == ZGame::OOT_SW97) || (ucodeValue == "f3dex")) + { + dListType = DListType::F3DEX; + } + else if (!ucodeSet || ucodeValue == "f3dex2") + { + dListType = DListType::F3DZEX; + } + else + { + HANDLE_ERROR_RESOURCE( + WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + StringHelper::Sprintf("Invalid ucode type in node: %s\n", reader->Name()), ""); + } + + // Don't parse raw data of external files + if (parent->GetMode() != ZFileMode::ExternalFile) + { + int32_t rawDataSize = + ZDisplayList::GetDListLength(parent->GetRawData(), rawDataIndex, dListType); + numInstructions = rawDataSize / 8; + ParseRawData(); + } + + Declaration* decl = DeclareVar("", ""); + decl->declaredInXml = true; +} + +void ZDisplayList::ExtractFromBinary(uint32_t nRawDataIndex, int32_t rawDataSize) +{ + rawDataIndex = nRawDataIndex; + name = GetDefaultName(parent->GetName()); + numInstructions = rawDataSize / 8; + + // Don't parse raw data of external files + if (parent->GetMode() == ZFileMode::ExternalFile) + return; + + ParseRawData(); +} + +void ZDisplayList::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + instructions.reserve(numInstructions); + uint32_t ptr = rawDataIndex; + + instructions.reserve(numInstructions); + for (size_t i = 0; i < numInstructions; i++) + { + instructions.push_back(BitConverter::ToUInt64BE(rawData, ptr)); + ptr += 8; + } +} + +Declaration* ZDisplayList::DeclareVar([[maybe_unused]] const std::string& prefix, + const std::string& bodyStr) +{ + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), name, numInstructions, bodyStr); + decl->isExternal = true; + decl->staticConf = staticConf; + return decl; +} + +std::string ZDisplayList::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sDL_%06X", prefix.c_str(), rawDataIndex); +} + +void ZDisplayList::ParseF3DZEX(F3DZEXOpcode opcode, uint64_t data, int32_t i, + const std::string& prefix, char* line) +{ + switch (opcode) + { + case F3DZEXOpcode::G_NOOP: + sprintf(line, "gsDPNoOpTag(0x%08" PRIX64 "),", data & 0xFFFFFFFF); + break; + case F3DZEXOpcode::G_DL: + Opcode_G_DL(data, prefix, line); + break; + case F3DZEXOpcode::G_MODIFYVTX: + Opcode_G_MODIFYVTX(data, line); + break; + case F3DZEXOpcode::G_CULLDL: + Opcode_G_CULLDL(data, line); + break; + case F3DZEXOpcode::G_TRI1: + Opcode_G_TRI1(data, line); + break; + case F3DZEXOpcode::G_TRI2: + Opcode_G_TRI2(data, line); + break; + case F3DZEXOpcode::G_QUAD: + { + int32_t aa = ((data & 0x00FF000000000000ULL) >> 48) / 2; + int32_t bb = ((data & 0x0000FF0000000000ULL) >> 40) / 2; + int32_t cc = ((data & 0x000000FF00000000ULL) >> 32) / 2; + int32_t dd = ((data & 0x000000000000FFULL)) / 2; + sprintf(line, "gsSP1Quadrangle(%i, %i, %i, %i, 0),", aa, bb, cc, dd); + } + break; + case F3DZEXOpcode::G_VTX: + Opcode_G_VTX(data, line); + break; + case F3DZEXOpcode::G_SETTIMG: // HOTSPOT + Opcode_G_SETTIMG(data, prefix, line); + break; + case F3DZEXOpcode::G_GEOMETRYMODE: + { + int32_t cccccc = (data & 0x00FFFFFF00000000) >> 32; + int32_t ssssssss = (data & 0xFFFFFFFF); + std::string geoModeStr = "G_TEXTURE_ENABLE"; + + int32_t geoModeParam = ~cccccc; + + if (ssssssss != 0) + geoModeParam = ssssssss; + + if (geoModeParam & 0x00000001) + geoModeStr += " | G_ZBUFFER"; + + if (geoModeParam & 0x00000004) + geoModeStr += " | G_SHADE"; + + if (geoModeParam & 0x00000200) + geoModeStr += " | G_CULL_FRONT"; + + if (geoModeParam & 0x00000400) + geoModeStr += " | G_CULL_BACK"; + + if (geoModeParam & 0x00010000) + geoModeStr += " | G_FOG"; + + if (geoModeParam & 0x00020000) + geoModeStr += " | G_LIGHTING"; + + if (geoModeParam & 0x00040000) + geoModeStr += " | G_TEXTURE_GEN"; + + if (geoModeParam & 0x00080000) + geoModeStr += " | G_TEXTURE_GEN_LINEAR"; + + if (geoModeParam & 0x00200000) + geoModeStr += " | G_SHADING_SMOOTH"; + + if (geoModeParam & 0x00800000) + geoModeStr += " | G_CLIPPING"; + + if (ssssssss != 0) + { + if ((~cccccc & 0xFF000000) != 0) + sprintf(line, "gsSPSetGeometryMode(%s),", geoModeStr.c_str()); + else + sprintf(line, "gsSPLoadGeometryMode(%s),", geoModeStr.c_str()); + } + else + sprintf(line, "gsSPClearGeometryMode(%s),", geoModeStr.c_str()); + } + break; + case F3DZEXOpcode::G_SETPRIMCOLOR: + Opcode_G_SETPRIMCOLOR(data, line); + break; + case F3DZEXOpcode::G_SETOTHERMODE_L: + Opcode_G_SETOTHERMODE_L(data, line); + break; + case F3DZEXOpcode::G_SETOTHERMODE_H: + Opcode_G_SETOTHERMODE_H(data, line); + break; + case F3DZEXOpcode::G_SETTILE: + Opcode_G_SETTILE(data, line); + break; + case F3DZEXOpcode::G_SETTILESIZE: + Opcode_G_SETTILESIZE(data, prefix, line); + break; + case F3DZEXOpcode::G_LOADBLOCK: + Opcode_G_LOADBLOCK(data, line); + break; + case F3DZEXOpcode::G_TEXTURE: + Opcode_G_TEXTURE(data, line); + break; + case F3DZEXOpcode::G_RDPSETOTHERMODE: + { + int32_t hhhhhh = (data & 0x00FFFFFF00000000) >> 32; + int32_t llllllll = (data & 0x00000000FFFFFFFF); + + sprintf(line, "gsDPSetOtherMode(%i, %i),", hhhhhh, llllllll); + } + break; + case F3DZEXOpcode::G_POPMTX: + { + sprintf(line, "gsSPPopMatrix(%" PRIi64 "),", data); + } + break; + case F3DZEXOpcode::G_LOADTLUT: + Opcode_G_LOADTLUT(data, prefix, line); + break; + case F3DZEXOpcode::G_SETENVCOLOR: + { + uint8_t r = (uint8_t)((data & 0xFF000000) >> 24); + uint8_t g = (uint8_t)((data & 0x00FF0000) >> 16); + uint8_t b = (uint8_t)((data & 0xFF00FF00) >> 8); + uint8_t a = (uint8_t)((data & 0x000000FF) >> 0); + + sprintf(line, "gsDPSetEnvColor(%i, %i, %i, %i),", r, g, b, a); + } + break; + case F3DZEXOpcode::G_SETCOMBINE: + { + Opcode_G_SETCOMBINE(data, line); + } + break; + case F3DZEXOpcode::G_RDPLOADSYNC: + sprintf(line, "gsDPLoadSync(),"); + break; + case F3DZEXOpcode::G_RDPPIPESYNC: + sprintf(line, "gsDPPipeSync(),"); + break; + case F3DZEXOpcode::G_RDPTILESYNC: + sprintf(line, "gsDPTileSync(),"); + break; + case F3DZEXOpcode::G_RDPFULLSYNC: + sprintf(line, "gsDPFullSync(),"); + break; + case F3DZEXOpcode::G_ENDDL: + Opcode_G_ENDDL(prefix, line); + break; + case F3DZEXOpcode::G_RDPHALF_1: + { + uint64_t data2 = instructions[i + 1]; + uint32_t h = (data & 0xFFFFFFFF); + F3DZEXOpcode opcode2 = (F3DZEXOpcode)(instructions[i + 1] >> 56); + + if (opcode2 == F3DZEXOpcode::G_BRANCH_Z) + { + uint32_t a = (data2 & 0x00FFF00000000000) >> 44; + uint32_t b = (data2 & 0x00000FFF00000000) >> 32; + uint32_t z = (data2 & 0x00000000FFFFFFFF) >> 0; + + // sprintf(line, "gsDPWord(%i, 0),", h); + sprintf(line, "gsSPBranchLessZraw(%sDlist0x%06X, 0x%02X, 0x%02X),", prefix.c_str(), + h & 0x00FFFFFF, (a / 5) | (b / 2), z); + + ZDisplayList* nList = new ZDisplayList(parent); + nList->ExtractFromBinary( + h & 0x00FFFFFF, GetDListLength(parent->GetRawData(), h & 0x00FFFFFF, dListType)); + nList->SetName(nList->GetDefaultName(prefix)); + otherDLists.push_back(nList); + + i++; + } + } + break; + case F3DZEXOpcode::G_MTX: + Opcode_G_MTX(data, line); + break; + default: + sprintf(line, "// Opcode 0x%02X unimplemented!", (uint32_t)opcode); + break; + } +} + +void ZDisplayList::ParseF3DEX(F3DEXOpcode opcode, uint64_t data, const std::string& prefix, + char* line) +{ + switch (opcode) + { + case F3DEXOpcode::G_NOOP: + sprintf(line, "gsDPNoOpTag(0x%08" PRIX64 "),", data & 0xFFFFFFFF); + break; + case F3DEXOpcode::G_VTX: + Opcode_G_VTX(data, line); + break; + case F3DEXOpcode::G_DL: + Opcode_G_DL(data, prefix, line); + break; + case F3DEXOpcode::G_CULLDL: + Opcode_G_CULLDL(data, line); + break; + case F3DEXOpcode::G_MODIFYVTX: + Opcode_G_MODIFYVTX(data, line); + break; + case F3DEXOpcode::G_MTX: + Opcode_G_MTX(data, line); + break; + case F3DEXOpcode::G_TRI1: + Opcode_G_TRI1(data, line); + break; + case F3DEXOpcode::G_TRI2: + Opcode_G_TRI2(data, line); + break; + case F3DEXOpcode::G_ENDDL: + Opcode_G_ENDDL(prefix, line); + break; + case F3DEXOpcode::G_RDPLOADSYNC: + sprintf(line, "gsDPLoadSync(),"); + break; + case F3DEXOpcode::G_RDPPIPESYNC: + sprintf(line, "gsDPPipeSync(),"); + break; + case F3DEXOpcode::G_RDPTILESYNC: + sprintf(line, "gsDPTileSync(),"); + break; + case F3DEXOpcode::G_RDPFULLSYNC: + sprintf(line, "gsDPFullSync(),"); + break; + case F3DEXOpcode::G_TEXTURE: + Opcode_G_TEXTURE(data, line); + break; + case F3DEXOpcode::G_SETTIMG: + Opcode_G_SETTIMG(data, prefix, line); + break; + case F3DEXOpcode::G_SETTILE: + Opcode_G_SETTILE(data, line); + break; + case F3DEXOpcode::G_SETTILESIZE: + Opcode_G_SETTILESIZE(data, prefix, line); + break; + case F3DEXOpcode::G_LOADBLOCK: + Opcode_G_LOADBLOCK(data, line); + break; + case F3DEXOpcode::G_SETCOMBINE: + Opcode_G_SETCOMBINE(data, line); + break; + case F3DEXOpcode::G_SETPRIMCOLOR: + Opcode_G_SETPRIMCOLOR(data, line); + break; + case F3DEXOpcode::G_SETOTHERMODE_L: + Opcode_G_SETOTHERMODE_L(data, line); + break; + case F3DEXOpcode::G_SETOTHERMODE_H: + Opcode_G_SETOTHERMODE_H(data, line); + break; + case F3DEXOpcode::G_LOADTLUT: + Opcode_G_LOADTLUT(data, prefix, line); + break; + case F3DEXOpcode::G_CLEARGEOMETRYMODE: + case F3DEXOpcode::G_SETGEOMETRYMODE: + { + int32_t cccccc = (data & 0x00FFFFFF00000000) >> 32; + int32_t ssssssss = (data & 0xFFFFFFFF); + std::string geoModeStr = "G_TEXTURE_ENABLE"; + + int32_t geoModeParam = ~cccccc; + + if (ssssssss != 0) + geoModeParam = ssssssss; + + if (geoModeParam & 0x00000002) + geoModeStr += " | G_TEXTURE_ENABLE"; + + if (geoModeParam & 0x00000200) + geoModeStr += " | G_SHADING_SMOOTH"; + + if (geoModeParam & 0x00001000) + geoModeStr += " | G_CULL_FRONT"; + + if (geoModeParam & 0x00002000) + geoModeStr += " | G_CULL_BACK"; + + if (geoModeParam & 0x00000001) + geoModeStr += " | G_ZBUFFER"; + + if (geoModeParam & 0x00000004) + geoModeStr += " | G_SHADE"; + + if (geoModeParam & 0x00010000) + geoModeStr += " | G_FOG"; + + if (geoModeParam & 0x00020000) + geoModeStr += " | G_LIGHTING"; + + if (geoModeParam & 0x00040000) + geoModeStr += " | G_TEXTURE_GEN"; + + if (geoModeParam & 0x00080000) + geoModeStr += " | G_TEXTURE_GEN_LINEAR"; + + if (geoModeParam & 0x00800000) + geoModeStr += " | G_CLIPPING"; + + if (opcode == F3DEXOpcode::G_SETGEOMETRYMODE) + sprintf(line, "gsSPSetGeometryMode(%s),", geoModeStr.c_str()); + else + sprintf(line, "gsSPClearGeometryMode(%s),", geoModeStr.c_str()); + } + break; + default: + sprintf(line, "// Opcode 0x%02X unimplemented!", (uint32_t)opcode); + break; + } +} + +int32_t ZDisplayList::GetDListLength(const std::vector& rawData, uint32_t rawDataIndex, + DListType dListType) +{ + uint8_t endDLOpcode; + uint8_t branchListOpcode; + + if (dListType == DListType::F3DZEX) + { + endDLOpcode = static_cast(F3DZEXOpcode::G_ENDDL); + branchListOpcode = static_cast(F3DZEXOpcode::G_DL); + } + else + { + endDLOpcode = static_cast(F3DEXOpcode::G_ENDDL); + branchListOpcode = static_cast(F3DEXOpcode::G_DL); + } + + uint32_t ptr = rawDataIndex; + size_t rawDataSize = rawData.size(); + while (true) + { + if (ptr >= rawDataSize) + { + std::string errorHeader = + StringHelper::Sprintf("reached end of file when trying to find the end of the " + "DisplayList starting at offset 0x%X", + rawDataIndex); + std::string errorBody = StringHelper::Sprintf("Raw data size: 0x%zX.", rawDataSize); + HANDLE_ERROR_PROCESS(WarningType::Always, errorHeader, errorBody); + } + + uint8_t opcode = rawData.at(ptr); + bool dlNoPush = rawData.at(ptr + 1) == 1; + ptr += 8; + + if (opcode == endDLOpcode || (opcode == branchListOpcode && dlNoPush)) + { + return ptr - rawDataIndex; + } + } +} + +bool ZDisplayList::SequenceCheck(std::vector sequence, int32_t startIndex) +{ + bool success = true; + + for (size_t j = 0; j < sequence.size(); j++) + { + F3DZEXOpcode opcode = (F3DZEXOpcode)(instructions[startIndex + j] >> 56); + + if (sequence[j] != opcode) + { + success = false; + break; + } + } + + if (success) + return true; + + return false; +} + +int32_t ZDisplayList::OptimizationChecks(int32_t startIndex, std::string& output, + const std::string& prefix) +{ + int32_t result = -1; + + result = OptimizationCheck_LoadTextureBlock(startIndex, output, prefix); + + if (result != -1) + return result; + + return -1; +} + +int32_t ZDisplayList::OptimizationCheck_LoadTextureBlock(int32_t startIndex, std::string& output, + [[maybe_unused]] const std::string& prefix) +{ + std::vector sequence = {F3DZEXOpcode::G_SETTIMG, F3DZEXOpcode::G_SETTILE, + F3DZEXOpcode::G_RDPLOADSYNC, F3DZEXOpcode::G_LOADBLOCK, + F3DZEXOpcode::G_RDPPIPESYNC, F3DZEXOpcode::G_SETTILE, + F3DZEXOpcode::G_SETTILESIZE}; + + bool seqRes = SequenceCheck(sequence, startIndex); + + if (seqRes) + { + // gsDPLoadTextureBlock(texAddr, fmt, siz, width, height, pal, cms, cmt, masks, maskt, + // shifts, shiftt) gsDPLoadMultiBlock(texAddr, tmem, rtile, fmt, siz, width, height, pal, + // cms, cmt, masks, maskt, shifts, shiftt) gsDPLoadTextureBlock_4b(texAddr, fmt, width, + // height, pal, cms, cmt, masks, maskt, shifts, shiftt) gsDPLoadMultiBlock_4b(texAddr, tmem, + // rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) + + uint32_t texAddr, tmem, rtile, fmt, siz, sizB, width, height, width2, height2, pal, cms, + cmt, masks, maskt, shifts, shiftt; + std::string texStr; + + // gsDPSetTextureImage + { + uint64_t data = instructions[startIndex + 0]; + + int32_t __ = (data & 0x00FF000000000000) >> 48; + // int32_t www = (data & 0x00000FFF00000000) >> 32; + + fmt = (__ & 0xE0) >> 5; + siz = (__ & 0x18) >> 3; + texAddr = Seg2Filespace(data, parent->baseAddress); + uint32_t segmentNumber = GETSEGNUM(data); + + lastTexSeg = segmentNumber; + + Globals::Instance->GetSegmentedPtrName(data & 0xFFFFFFFF, parent, "", texStr, + parent->workerID); + } + + // gsDPSetTile + { + uint64_t data = instructions[startIndex + 1]; + + tmem = + (data & 0b0000000000000000111111111111111100000000000000000000000000000000) >> 32; + + cmt = (data & 0b0000000000000000000000000000000000000000000011000000000000000000) >> 18; + maskt = + (data & 0b0000000000000000000000000000000000000000000000111100000000000000) >> 14; + shiftt = + (data & 0b0000000000000000000000000000000000000000000000000011110000000000) >> 10; + cms = (data & 0b0000000000000000000000000000000000000000000000000000001100000000) >> 8; + masks = + (data & 0b0000000000000000000000000000000000000000000000000000000011110000) >> 4; + shifts = (data & 0b0000000000000000000000000000000000000000000000000000000000001111); + + // sprintf(line, "gsDPSetTile(%s, %s, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i),", + // fmtTbl[fff].c_str(), sizTbl[ii].c_str(), nnnnnnnnn, mmmmmmmmm, ttt, pppp, cc, aaaa, + // ssss, dd, bbbb, uuuu); + } + + // gsDPLoadSync + + // gsDPLoadBlock + + // gsDPPipeSync + + // gsDPSetTile + { + uint64_t data = instructions[startIndex + 5]; + int32_t __ = (data & 0x00FF000000000000) >> 48; + pal = (data & 0b0000000000000000000000000000000000000000111100000000000000000000) >> 20; + // siz = (__ & 0x18) >> 3; + rtile = + (data & 0b0000000000000000000000000000000011111111000000000000000000000000) >> 24; + sizB = (__ & 0x18) >> 3; + } + + // gsDPSetTileSize + { + uint64_t data = instructions[startIndex + 6]; + int32_t uuu = (data & 0x0000000000FFF000) >> 12; + int32_t vvv = (data & 0x0000000000000FFF); + + int32_t shiftAmtW = 2; + int32_t shiftAmtH = 2; + + if (sizB == (int32_t)F3DZEXTexSizes::G_IM_SIZ_8b && + fmt == (int32_t)F3DZEXTexFormats::G_IM_FMT_IA) + shiftAmtW = 3; + + if (sizB == (int32_t)F3DZEXTexSizes::G_IM_SIZ_4b) + shiftAmtW = 3; + + if (sizB == (int32_t)F3DZEXTexSizes::G_IM_SIZ_4b && + fmt == (int32_t)F3DZEXTexFormats::G_IM_FMT_IA) + shiftAmtH = 3; + + width = (uuu >> shiftAmtW) + 1; + height = (vvv >> shiftAmtH) + 1; + + width2 = (uuu >> 2) + 1; + height2 = (vvv >> 2) + 1; + } + + const char* fmtTbl[] = {"G_IM_FMT_RGBA", "G_IM_FMT_YUV", "G_IM_FMT_CI", "G_IM_FMT_IA", + "G_IM_FMT_I"}; + const char* sizTbl[] = {"G_IM_SIZ_4b", "G_IM_SIZ_8b", "G_IM_SIZ_16b", "G_IM_SIZ_32b"}; + + // output += StringHelper::Sprintf("gsDPLoadTextureBlock(%s, %s, %s, %i, %i, %i, %i, %i, %i, + // %i, %i, %i),", texStr.c_str(), fmtTbl[fmt], sizTbl[siz].c_str(), width, height, + // pal, cms, cmt, masks, maskt, shifts, shiftt); + + if (siz == 2 && sizB == 0) + { + if (tmem != 0) + output += StringHelper::Sprintf( + "gsDPLoadMultiBlock_4b(%s, %i, %i, %s, %i, %i, %i, %i, %i, %i, %i, %i, %i),", + texStr.c_str(), tmem, rtile, fmtTbl[fmt], width2, height2, pal, cms, cmt, masks, + maskt, shifts, shiftt); + else + output += StringHelper::Sprintf( + "gsDPLoadTextureBlock_4b(%s, %s, %i, %i, %i, %i, %i, %i, %i, %i, %i),", + texStr.c_str(), fmtTbl[fmt], width2, height2, pal, cms, cmt, masks, maskt, + shifts, shiftt); + } + else if (siz == 2 && sizB != 0) + { + if (tmem != 0) + output += StringHelper::Sprintf( + "gsDPLoadMultiBlock(%s, %i, %i, %s, %s, %i, %i, %i, %i, %i, %i, %i, %i, %i),", + texStr.c_str(), tmem, rtile, fmtTbl[fmt], sizTbl[sizB], width2, height2, pal, + cms, cmt, masks, maskt, shifts, shiftt); + else + output += StringHelper::Sprintf( + "gsDPLoadTextureBlock(%s, %s, %s, %i, %i, %i, %i, %i, %i, %i, %i, %i),", + texStr.c_str(), fmtTbl[fmt], sizTbl[sizB], width2, height2, pal, cms, cmt, + masks, maskt, shifts, shiftt); + } + else + { + if (siz != sizB) + { + lastTexAddr = texAddr; + lastTexFmt = (F3DZEXTexFormats)fmt; + lastTexWidth = width; + lastTexHeight = height; + lastTexSiz = (F3DZEXTexSizes)siz; + lastTexLoaded = true; + + TextureGenCheck(); + + return -1; + } + + output += StringHelper::Sprintf( + "gsDPLoadMultiBlock(%s, %i, %i, %s, %s, %i, %i, %i, %i, %i, %i, %i, %i, %i),", + texStr.c_str(), tmem, rtile, fmtTbl[fmt], sizTbl[siz], width, height, pal, cms, cmt, + masks, maskt, shifts, shiftt); + } + + lastTexAddr = texAddr; + lastTexFmt = (F3DZEXTexFormats)fmt; + lastTexWidth = width; + lastTexHeight = height; + lastTexSiz = (F3DZEXTexSizes)siz; + lastTexLoaded = true; + + TextureGenCheck(); + + return (int32_t)sequence.size(); + } + + return -1; +} + +void ZDisplayList::Opcode_G_DL(uint64_t data, const std::string& prefix, char* line) +{ + int32_t pp = (data & 0x00FF000000000000) >> 56; + int32_t segNum = GETSEGNUM(data); + + Declaration* dListDecl = nullptr; + + if (parent != nullptr) + dListDecl = parent->GetDeclaration(GETSEGOFFSET(data)); + + if (pp != 0) + { + if (!Globals::Instance->HasSegment(segNum, parent->workerID)) + sprintf(line, "gsSPBranchList(0x%08" PRIX64 "),", data & 0xFFFFFFFF); + else if (dListDecl != nullptr) + sprintf(line, "gsSPBranchList(%s),", dListDecl->declName.c_str()); + else + sprintf(line, "gsSPBranchList(%sDlist0x%06" PRIX64 "),", prefix.c_str(), + GETSEGOFFSET(data)); + } + else + { + if (!Globals::Instance->HasSegment(segNum, parent->workerID)) + sprintf(line, "gsSPDisplayList(0x%08" PRIX64 "),", data & 0xFFFFFFFF); + else if (dListDecl != nullptr) + sprintf(line, "gsSPDisplayList(%s),", dListDecl->declName.c_str()); + else + sprintf(line, "gsSPDisplayList(%sDlist0x%06" PRIX64 "),", prefix.c_str(), + GETSEGOFFSET(data)); + } + + // if (segNum == 8 || segNum == 9 || segNum == 10 || segNum == 11 || segNum == 12 || segNum == + // 13) // Used for runtime-generated display lists + if (!Globals::Instance->HasSegment(segNum, parent->workerID)) + { + if (pp != 0) + sprintf(line, "gsSPBranchList(0x%08" PRIX64 "),", data & 0xFFFFFFFF); + else + sprintf(line, "gsSPDisplayList(0x%08" PRIX64 "),", data & 0xFFFFFFFF); + } + else + { + ZDisplayList* nList = new ZDisplayList(parent); + nList->ExtractFromBinary(GETSEGOFFSET(data), GetDListLength(parent->GetRawData(), + GETSEGOFFSET(data), dListType)); + nList->SetName(nList->GetDefaultName(prefix)); + + otherDLists.push_back(nList); + } +} + +void ZDisplayList::Opcode_G_MODIFYVTX(uint64_t data, char* line) +{ + int32_t ww = (data & 0x00FF000000000000ULL) >> 48; + int32_t nnnn = (data & 0x0000FFFF00000000ULL) >> 32; + int32_t vvvvvvvv = (data & 0x00000000FFFFFFFFULL); + + sprintf(line, "gsSPModifyVertex(%i, %i, %i),", nnnn / 2, ww, vvvvvvvv); +} + +void ZDisplayList::Opcode_G_CULLDL(uint64_t data, char* line) +{ + int32_t vvvv = (data & 0xFFFF00000000) >> 32; + int32_t wwww = (data & 0x0000FFFF); + + sprintf(line, "gsSPCullDisplayList(%i, %i),", vvvv / 2, wwww / 2); +} + +void ZDisplayList::Opcode_G_TRI1(uint64_t data, char* line) +{ + if (dListType == DListType::F3DZEX) + { + int32_t aa = ((data & 0x00FF000000000000ULL) >> 48) / 2; + int32_t bb = ((data & 0x0000FF0000000000ULL) >> 40) / 2; + int32_t cc = ((data & 0x000000FF00000000ULL) >> 32) / 2; + sprintf(line, "gsSP1Triangle(%i, %i, %i, 0),", aa, bb, cc); + } + else + { + int32_t aa = ((data & 0x0000000000FF0000ULL) >> 16) / 2; + int32_t bb = ((data & 0x000000000000FF00ULL) >> 8) / 2; + int32_t cc = ((data & 0x00000000000000FFULL) >> 0) / 2; + sprintf(line, "gsSP1Triangle(%i, %i, %i, 0),", aa, bb, cc); + } +} + +void ZDisplayList::Opcode_G_TRI2(uint64_t data, char* line) +{ + int32_t aa = ((data & 0x00FF000000000000ULL) >> 48) / 2; + int32_t bb = ((data & 0x0000FF0000000000ULL) >> 40) / 2; + int32_t cc = ((data & 0x000000FF00000000ULL) >> 32) / 2; + int32_t dd = ((data & 0x00000000FF0000ULL) >> 16) / 2; + int32_t ee = ((data & 0x0000000000FF00ULL) >> 8) / 2; + int32_t ff = ((data & 0x000000000000FFULL) >> 0) / 2; + sprintf(line, "gsSP2Triangles(%i, %i, %i, 0, %i, %i, %i, 0),", aa, bb, cc, dd, ee, ff); +} + +void ZDisplayList::Opcode_G_MTX(uint64_t data, char* line) +{ + uint32_t pp = 0; + uint32_t mm = (data & 0x00000000FFFFFFFF); + bool push = false; + bool load = false; + bool projection = false; + + if (dListType == DListType::F3DEX) + pp = (data & 0x00FF000000000000) >> 48; + else + pp = (data & 0x000000FF00000000) >> 32; + + std::string matrixRef; + + if (Globals::Instance->cfg.symbolMap.find(mm) != Globals::Instance->cfg.symbolMap.end()) + matrixRef = StringHelper::Sprintf("&%s", Globals::Instance->cfg.symbolMap[mm].c_str()); + else + matrixRef = StringHelper::Sprintf("0x%08X", mm); + + pp ^= 0x01; + + if (pp & 0x01) + push = true; + + if (pp & 0x02) + load = true; + + if (pp & 0x04) + projection = true; + + sprintf(line, "gsSPMatrix(%s, %s | %s | %s),", matrixRef.c_str(), + projection ? "G_MTX_PROJECTION" : "G_MTX_MODELVIEW", + push ? "G_MTX_PUSH" : "G_MTX_NOPUSH", load ? "G_MTX_LOAD" : "G_MTX_MUL"); +} + +void ZDisplayList::Opcode_G_VTX(uint64_t data, char* line) +{ + int32_t nn = (data & 0x000FF00000000000ULL) >> 44; + int32_t aa = (data & 0x000000FF00000000ULL) >> 32; + + uint32_t vtxAddr = Seg2Filespace(data, parent->baseAddress); + + if (dListType == DListType::F3DZEX) + sprintf(line, "gsSPVertex(@r, %i, %i),", nn, ((aa >> 1) - nn)); + else + { + uint32_t hi = data >> 32; + +#define _SHIFTR(v, s, w) (((uint32_t)v >> s) & ((0x01 << w) - 1)) + + nn = _SHIFTR(hi, 10, 6); + + sprintf(line, "gsSPVertex(@r, %i, %i),", nn, _SHIFTR(hi, 17, 7)); + } + + // Hack: Don't extract vertices from a unknown segment. + if (!Globals::Instance->HasSegment(GETSEGNUM(data), parent->workerID)) + { + segptr_t segmented = data & 0xFFFFFFFF; + references.push_back(segmented); + parent->AddDeclaration(segmented, DeclarationAlignment::Align8, 16, "Vtx", + StringHelper::Sprintf("0x%08X", segmented), ""); + return; + } + references.push_back(data); + + { + uint32_t currentPtr = Seg2Filespace(data, parent->baseAddress); + Declaration* decl; + + // Check for vertex intersections from other display lists + // TODO: These two could probably be condenced to one... + decl = parent->GetDeclarationRanged(vtxAddr + (nn * 16)); + if (decl != nullptr) + { + int32_t diff = decl->address - vtxAddr; + if (diff > 0) + nn = diff / 16; + else + nn = 0; + } + + decl = parent->GetDeclarationRanged(vtxAddr); + if (decl != nullptr) + { + int32_t diff = decl->address - vtxAddr; + if (diff > 0) + nn = diff / 16; + else + nn = 0; + } + + if (nn > 0) + { + std::vector vtxList; + vtxList.reserve(nn); + + for (int32_t i = 0; i < nn; i++) + { + ZVtx vtx(parent); + vtx.ExtractFromFile(currentPtr); + vtxList.push_back(vtx); + + currentPtr += 16; + } + + vertices[vtxAddr] = vtxList; + } + } +} + +void ZDisplayList::Opcode_G_TEXTURE(uint64_t data, char* line) +{ + int32_t ____ = (data & 0x0000FFFF00000000) >> 32; + int32_t ssss = (data & 0x00000000FFFF0000) >> 16; + int32_t tttt = (data & 0x000000000000FFFF); + int32_t lll = (____ & 0x3800) >> 11; + int32_t ddd = (____ & 0x700) >> 8; + int32_t nnnnnnn = 0; + + if (dListType == DListType::F3DEX) + nnnnnnn = (____ & 0xFF); + else + nnnnnnn = (____ & 0xFE) >> 1; + + sprintf(line, "gsSPTexture(%i, %i, %i, %i, %s),", ssss, tttt, lll, ddd, + nnnnnnn == 1 ? "G_ON" : "G_OFF"); +} + +void ZDisplayList::Opcode_G_SETTIMG(uint64_t data, const std::string& prefix, char* line) +{ + int32_t __ = (data & 0x00FF000000000000) >> 48; + int32_t www = (data & 0x00000FFF00000000) >> 32; + const char* fmtTbl[] = {"G_IM_FMT_RGBA", "G_IM_FMT_YUV", "G_IM_FMT_CI", "G_IM_FMT_IA", + "G_IM_FMT_I"}; + const char* sizTbl[] = {"G_IM_SIZ_4b", "G_IM_SIZ_8b", "G_IM_SIZ_16b", "G_IM_SIZ_32b"}; + + uint32_t fmt = (__ & 0xE0) >> 5; + uint32_t siz = (__ & 0x18) >> 3; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + printf("TextureGenCheck G_SETTIMG\n"); + + TextureGenCheck(); // HOTSPOT + + lastTexFmt = (F3DZEXTexFormats)fmt; + lastTexSiz = (F3DZEXTexSizes)siz; + lastTexSeg = data; + lastTexAddr = Seg2Filespace(data, parent->baseAddress); + + int32_t segmentNumber = GETSEGNUM(data); + + if (segmentNumber != 2) + { + char texStr[2048]; + int32_t texAddress = Seg2Filespace(data, parent->baseAddress); + Declaration* texDecl = nullptr; + + if (parent != nullptr) + { + if (Globals::Instance->HasSegment(segmentNumber, parent->workerID)) + texDecl = parent->GetDeclaration(texAddress); + else + texDecl = parent->GetDeclaration(data); + } + + if (texDecl != nullptr) + sprintf(texStr, "%s", texDecl->declName.c_str()); + else if (data != 0 && Globals::Instance->HasSegment(segmentNumber, parent->workerID)) + sprintf(texStr, "%sTex_%06X", prefix.c_str(), texAddress); + else + { + sprintf(texStr, "0x%08" PRIX64, data & 0xFFFFFFFF); + } + + sprintf(line, "gsDPSetTextureImage(%s, %s, %i, %s),", fmtTbl[fmt], sizTbl[siz], www + 1, + texStr); + } + else + { + std::string texName; + Globals::Instance->GetSegmentedPtrName(data, parent, "", texName, parent->workerID); + sprintf(line, "gsDPSetTextureImage(%s, %s, %i, %s),", fmtTbl[fmt], sizTbl[siz], www + 1, + texName.c_str()); + } +} + +void ZDisplayList::Opcode_G_SETTILE(uint64_t data, char* line) +{ + int32_t fff = (data & 0b0000000011100000000000000000000000000000000000000000000000000000) >> 53; + int32_t ii = (data & 0b0000000000011000000000000000000000000000000000000000000000000000) >> 51; + int32_t nnnnnnnnn = + (data & 0b0000000000000011111111100000000000000000000000000000000000000000) >> 41; + int32_t mmmmmmmmm = + (data & 0b0000000000000000000000011111111100000000000000000000000000000000) >> 32; + int32_t ttt = (data & 0b0000000000000000000000000000000000000111000000000000000000000000) >> 24; + int32_t pppp = + (data & 0b0000000000000000000000000000000000000000111100000000000000000000) >> 20; + int32_t cc = (data & 0b0000000000000000000000000000000000000000000011000000000000000000) >> 18; + int32_t aaaa = + (data & 0b0000000000000000000000000000000000000000000000111100000000000000) >> 14; + int32_t ssss = + (data & 0b0000000000000000000000000000000000000000000000000011110000000000) >> 10; + int32_t dd = (data & 0b0000000000000000000000000000000000000000000000000000001100000000) >> 8; + int32_t bbbb = (data & 0b0000000000000000000000000000000000000000000000000000000011110000) >> 4; + int32_t uuuu = (data & 0b0000000000000000000000000000000000000000000000000000000000001111); + + const char* fmtTbl[] = {"G_IM_FMT_RGBA", "G_IM_FMT_YUV", "G_IM_FMT_CI", "G_IM_FMT_IA", + "G_IM_FMT_I"}; + const char* sizTbl[] = {"G_IM_SIZ_4b", "G_IM_SIZ_8b", "G_IM_SIZ_16b", "G_IM_SIZ_32b"}; + + if (fff == (int32_t)F3DZEXTexFormats::G_IM_FMT_CI) + lastCISiz = (F3DZEXTexSizes)ii; + + lastTexSizTest = (F3DZEXTexSizes)ii; + + sprintf(line, "gsDPSetTile(%s, %s, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i),", fmtTbl[fff], + sizTbl[ii], nnnnnnnnn, mmmmmmmmm, ttt, pppp, cc, aaaa, ssss, dd, bbbb, uuuu); +} + +void ZDisplayList::Opcode_G_SETTILESIZE(uint64_t data, [[maybe_unused]] const std::string& prefix, + char* line) +{ + int32_t sss = (data & 0x00FFF00000000000) >> 44; + int32_t ttt = (data & 0x00000FFF00000000) >> 32; + int32_t uuu = (data & 0x0000000000FFF000) >> 12; + int32_t vvv = (data & 0x0000000000000FFF); + int32_t i = (data & 0x000000000F000000) >> 24; + + int32_t shiftAmtW = 2; + int32_t shiftAmtH = 2; + + if (lastTexSizTest == F3DZEXTexSizes::G_IM_SIZ_8b && + lastTexFmt == F3DZEXTexFormats::G_IM_FMT_IA) + shiftAmtW = 3; + + // if (lastTexFmt == F3DZEXTexFormats::G_IM_FMT_I || lastTexFmt == + // F3DZEXTexFormats::G_IM_FMT_CI) + if (lastTexSizTest == F3DZEXTexSizes::G_IM_SIZ_4b) + shiftAmtW = 3; + + if (lastTexSizTest == F3DZEXTexSizes::G_IM_SIZ_4b && + lastTexFmt == F3DZEXTexFormats::G_IM_FMT_IA) + shiftAmtH = 3; + + lastTexWidth = (uuu >> shiftAmtW) + 1; + lastTexHeight = (vvv >> shiftAmtH) + 1; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + printf("lastTexWidth: %i lastTexHeight: %i, lastTexSizTest: 0x%x, lastTexFmt: 0x%x\n", + lastTexWidth, lastTexHeight, (uint32_t)lastTexSizTest, (uint32_t)lastTexFmt); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + printf("TextureGenCheck G_SETTILESIZE\n"); + + TextureGenCheck(); + + sprintf(line, "gsDPSetTileSize(%i, %i, %i, %i, %i),", i, sss, ttt, uuu, vvv); +} + +void ZDisplayList::Opcode_G_LOADBLOCK(uint64_t data, char* line) +{ + int32_t sss = (data & 0x00FFF00000000000) >> 48; + int32_t ttt = (data & 0x00000FFF00000000) >> 36; + int32_t i = (data & 0x000000000F000000) >> 24; + int32_t xxx = (data & 0x0000000000FFF000) >> 12; + int32_t ddd = (data & 0x0000000000000FFF); + + lastTexLoaded = true; + + sprintf(line, "gsDPLoadBlock(%i, %i, %i, %i, %i),", i, sss, ttt, xxx, ddd); +} + +void ZDisplayList::Opcode_G_SETCOMBINE(uint64_t data, char* line) +{ + int32_t a0 = (data & 0b000000011110000000000000000000000000000000000000000000000000000) >> 52; + int32_t c0 = (data & 0b000000000001111100000000000000000000000000000000000000000000000) >> 47; + int32_t aa0 = (data & 0b00000000000000011100000000000000000000000000000000000000000000) >> 44; + int32_t ac0 = (data & 0b00000000000000000011100000000000000000000000000000000000000000) >> 41; + int32_t a1 = (data & 0b000000000000000000000011110000000000000000000000000000000000000) >> 37; + int32_t c1 = (data & 0b000000000000000000000000001111100000000000000000000000000000000) >> 32; + int32_t b0 = (data & 0b000000000000000000000000000000011110000000000000000000000000000) >> 28; + int32_t b1 = (data & 0b000000000000000000000000000000000001111000000000000000000000000) >> 24; + int32_t aa1 = (data & 0b00000000000000000000000000000000000000111000000000000000000000) >> 21; + int32_t ac1 = (data & 0b00000000000000000000000000000000000000000111000000000000000000) >> 18; + int32_t d0 = (data & 0b000000000000000000000000000000000000000000000111000000000000000) >> 15; + int32_t ab0 = (data & 0b00000000000000000000000000000000000000000000000111000000000000) >> 12; + int32_t ad0 = (data & 0b00000000000000000000000000000000000000000000000000111000000000) >> 9; + int32_t d1 = (data & 0b000000000000000000000000000000000000000000000000000000111000000) >> 6; + int32_t ab1 = (data & 0b00000000000000000000000000000000000000000000000000000000111000) >> 3; + int32_t ad1 = (data & 0b00000000000000000000000000000000000000000000000000000000000111) >> 0; + + const char* modesA[] = {"COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIRONMENT", + "1", "NOISE", "0", "9", "10", "11", + "12", "13", "14", "0"}; + const char* modesB[] = {"COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIRONMENT", + "CENTER", "K4", "8", "9", "10", "11", + "12", "13", "14", "0"}; + const char* modesC[] = {"COMBINED", + "TEXEL0", + "TEXEL1", + "PRIMITIVE", + "SHADE", + "ENVIRONMENT", + "1", + "COMBINED_ALPHA", + "TEXEL0_ALPHA", + "TEXEL1_ALPHA", + "PRIMITIVE_ALPHA", + "SHADE_ALPHA", + "ENV_ALPHA", + "LOD_FRACTION", + "PRIM_LOD_FRAC", + "K5", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "0"}; + const char* modesD[] = {"COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", + "SHADE", "ENVIRONMENT", "1", "0"}; + + const char* modes2[] = {"COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", + "SHADE", "ENVIRONMENT", "1", "0"}; + const char* modes2C[] = {"LOD_FRACTION", "TEXEL0", "TEXEL1", "PRIMITIVE", + "SHADE", "ENVIRONMENT", "PRIM_LOD_FRAC", "0"}; + + sprintf(line, + "gsDPSetCombineLERP(%s, %s, %s, %s, %s, %s, %s, %s,\n %s, %s, " + "%s, %s, %s, %s, %s, %s),", + modesA[a0], modesB[b0], modesC[c0], modesD[d0], modes2[aa0], modes2[ab0], modes2C[ac0], + modes2[ad0], modesA[a1], modesB[b1], modesC[c1], modesD[d1], modes2[aa1], modes2[ab1], + modes2C[ac1], modes2[ad1]); +} + +void ZDisplayList::Opcode_G_SETPRIMCOLOR(uint64_t data, char* line) +{ + int32_t mm = (data & 0x0000FF0000000000) >> 40; + int32_t ff = (data & 0x000000FF00000000) >> 32; + int32_t rr = (data & 0x00000000FF000000) >> 24; + int32_t gg = (data & 0x0000000000FF0000) >> 16; + int32_t bb = (data & 0x000000000000FF00) >> 8; + int32_t aa = (data & 0x00000000000000FF) >> 0; + sprintf(line, "gsDPSetPrimColor(%i, %i, %i, %i, %i, %i),", mm, ff, rr, gg, bb, aa); +} + +void ZDisplayList::Opcode_F3DEX_G_SETOTHERMODE_L(uint64_t data, char* line) +{ + int32_t sft = (data & 0x0000FF0000000000) >> 40; + int32_t len = (data & 0x000000FF00000000) >> 32; + int32_t dat = (data & 0xFFFFFFFF); + + // TODO: Output the correct render modes in data + + sprintf(line, "gsSPSetOtherMode(0xE2, %i, %i, 0x%08X),", sft, len, dat); +} + +void ZDisplayList::Opcode_G_SETOTHERMODE_L(uint64_t data, char* line) +{ + int32_t dd = (data & 0xFFFFFFFF); + int32_t sft = 0; + int32_t len = 0; + + if (dListType == DListType::F3DEX) + { + sft = (data & 0x0000FF0000000000) >> 40; + len = (data & 0x000000FF00000000) >> 32; + } + else + { + int32_t ss = (data & 0x0000FF0000000000) >> 40; + len = ((data & 0x000000FF00000000) >> 32) + 1; + sft = 32 - (len)-ss; + } + + if (sft == G_MDSFT_RENDERMODE) + { + uint32_t mode1 = (dd & 0xCCCC0000) >> 0; + uint32_t mode2 = (dd & 0x3333FFFF); + + // TODO: Jesus Christ This is Messy + + uint32_t tblA[] = {G_RM_FOG_SHADE_A, + G_RM_FOG_PRIM_A, + G_RM_PASS, + G_RM_AA_ZB_OPA_SURF, + G_RM_AA_ZB_XLU_SURF, + G_RM_AA_ZB_OPA_DECAL, + G_RM_AA_ZB_XLU_DECAL, + G_RM_AA_ZB_OPA_INTER, + G_RM_AA_ZB_XLU_INTER, + G_RM_AA_ZB_XLU_LINE, + G_RM_AA_ZB_DEC_LINE, + G_RM_AA_ZB_TEX_EDGE, + G_RM_AA_ZB_TEX_INTER, + G_RM_AA_ZB_SUB_SURF, + G_RM_AA_ZB_PCL_SURF, + G_RM_AA_ZB_OPA_TERR, + G_RM_AA_ZB_TEX_TERR, + G_RM_AA_ZB_SUB_TERR, + G_RM_RA_ZB_OPA_SURF, + G_RM_RA_ZB_OPA_DECAL, + G_RM_RA_ZB_OPA_INTER, + G_RM_AA_OPA_SURF, + G_RM_AA_XLU_SURF, + G_RM_AA_XLU_LINE, + G_RM_AA_DEC_LINE, + G_RM_AA_TEX_EDGE, + G_RM_AA_SUB_SURF, + G_RM_AA_PCL_SURF, + G_RM_AA_OPA_TERR, + G_RM_AA_TEX_TERR, + G_RM_AA_SUB_TERR, + G_RM_RA_OPA_SURF, + G_RM_ZB_OPA_SURF, + G_RM_ZB_XLU_SURF, + G_RM_ZB_OPA_DECAL, + G_RM_ZB_XLU_DECAL, + G_RM_ZB_CLD_SURF, + G_RM_ZB_OVL_SURF, + G_RM_ZB_PCL_SURF, + G_RM_OPA_SURF, + G_RM_XLU_SURF, + G_RM_CLD_SURF, + G_RM_TEX_EDGE, + G_RM_PCL_SURF, + G_RM_ADD, + G_RM_NOOP, + G_RM_VISCVG, + G_RM_OPA_CI}; + + uint32_t tblB[] = {G_RM_AA_ZB_OPA_SURF2, + G_RM_AA_ZB_XLU_SURF2, + G_RM_AA_ZB_OPA_DECAL2, + G_RM_AA_ZB_XLU_DECAL2, + G_RM_AA_ZB_OPA_INTER2, + G_RM_AA_ZB_XLU_INTER2, + G_RM_AA_ZB_XLU_LINE2, + G_RM_AA_ZB_DEC_LINE2, + G_RM_AA_ZB_TEX_EDGE2, + G_RM_AA_ZB_TEX_INTER2, + G_RM_AA_ZB_SUB_SURF2, + G_RM_AA_ZB_PCL_SURF2, + G_RM_AA_ZB_OPA_TERR2, + G_RM_AA_ZB_TEX_TERR2, + G_RM_AA_ZB_SUB_TERR2, + G_RM_RA_ZB_OPA_SURF2, + G_RM_RA_ZB_OPA_DECAL2, + G_RM_RA_ZB_OPA_INTER2, + G_RM_AA_OPA_SURF2, + G_RM_AA_XLU_SURF2, + G_RM_AA_XLU_LINE2, + G_RM_AA_DEC_LINE2, + G_RM_AA_TEX_EDGE2, + G_RM_AA_SUB_SURF2, + G_RM_AA_PCL_SURF2, + G_RM_AA_OPA_TERR2, + G_RM_AA_TEX_TERR2, + G_RM_AA_SUB_TERR2, + G_RM_RA_OPA_SURF2, + G_RM_ZB_OPA_SURF2, + G_RM_ZB_XLU_SURF2, + G_RM_ZB_OPA_DECAL2, + G_RM_ZB_XLU_DECAL2, + G_RM_ZB_CLD_SURF2, + G_RM_ZB_OVL_SURF2, + G_RM_ZB_PCL_SURF2, + G_RM_OPA_SURF2, + G_RM_XLU_SURF2, + G_RM_CLD_SURF2, + G_RM_TEX_EDGE2, + G_RM_PCL_SURF2, + G_RM_ADD2, + G_RM_NOOP2, + G_RM_VISCVG2, + G_RM_OPA_CI2}; + + std::map str = { + {G_RM_FOG_SHADE_A, "G_RM_FOG_SHADE_A"}, + {G_RM_FOG_PRIM_A, "G_RM_FOG_PRIM_A"}, + {G_RM_PASS, "G_RM_PASS"}, + {G_RM_AA_ZB_OPA_SURF, "G_RM_AA_ZB_OPA_SURF"}, + {G_RM_AA_ZB_OPA_SURF2, "G_RM_AA_ZB_OPA_SURF2"}, + {G_RM_AA_ZB_XLU_SURF, "G_RM_AA_ZB_XLU_SURF"}, + {G_RM_AA_ZB_XLU_SURF2, "G_RM_AA_ZB_XLU_SURF2"}, + {G_RM_AA_ZB_OPA_DECAL, "G_RM_AA_ZB_OPA_DECAL"}, + {G_RM_AA_ZB_OPA_DECAL2, "G_RM_AA_ZB_OPA_DECAL2"}, + {G_RM_AA_ZB_XLU_DECAL, "G_RM_AA_ZB_XLU_DECAL"}, + {G_RM_AA_ZB_XLU_DECAL2, "G_RM_AA_ZB_XLU_DECAL2"}, + {G_RM_AA_ZB_OPA_INTER, "G_RM_AA_ZB_OPA_INTER"}, + {G_RM_AA_ZB_OPA_INTER2, "G_RM_AA_ZB_OPA_INTER2"}, + {G_RM_AA_ZB_XLU_INTER, "G_RM_AA_ZB_XLU_INTER"}, + {G_RM_AA_ZB_XLU_INTER2, "G_RM_AA_ZB_XLU_INTER2"}, + {G_RM_AA_ZB_XLU_LINE, "G_RM_AA_ZB_XLU_LINE"}, + {G_RM_AA_ZB_XLU_LINE2, "G_RM_AA_ZB_XLU_LINE2"}, + {G_RM_AA_ZB_DEC_LINE, "G_RM_AA_ZB_DEC_LINE"}, + {G_RM_AA_ZB_DEC_LINE2, "G_RM_AA_ZB_DEC_LINE2"}, + {G_RM_AA_ZB_TEX_EDGE, "G_RM_AA_ZB_TEX_EDGE"}, + {G_RM_AA_ZB_TEX_EDGE2, "G_RM_AA_ZB_TEX_EDGE2"}, + {G_RM_AA_ZB_TEX_INTER, "G_RM_AA_ZB_TEX_INTER"}, + {G_RM_AA_ZB_TEX_INTER2, "G_RM_AA_ZB_TEX_INTER2"}, + {G_RM_AA_ZB_SUB_SURF, "G_RM_AA_ZB_SUB_SURF"}, + {G_RM_AA_ZB_SUB_SURF2, "G_RM_AA_ZB_SUB_SURF2"}, + {G_RM_AA_ZB_PCL_SURF, "G_RM_AA_ZB_PCL_SURF"}, + {G_RM_AA_ZB_PCL_SURF2, "G_RM_AA_ZB_PCL_SURF2"}, + {G_RM_AA_ZB_OPA_TERR, "G_RM_AA_ZB_OPA_TERR"}, + {G_RM_AA_ZB_OPA_TERR2, "G_RM_AA_ZB_OPA_TERR2"}, + {G_RM_AA_ZB_TEX_TERR, "G_RM_AA_ZB_TEX_TERR"}, + {G_RM_AA_ZB_TEX_TERR2, "G_RM_AA_ZB_TEX_TERR2"}, + {G_RM_AA_ZB_SUB_TERR, "G_RM_AA_ZB_SUB_TERR"}, + {G_RM_AA_ZB_SUB_TERR2, "G_RM_AA_ZB_SUB_TERR2"}, + {G_RM_RA_ZB_OPA_SURF, "G_RM_RA_ZB_OPA_SURF"}, + {G_RM_RA_ZB_OPA_SURF2, "G_RM_RA_ZB_OPA_SURF2"}, + {G_RM_RA_ZB_OPA_DECAL, "G_RM_RA_ZB_OPA_DECAL"}, + {G_RM_RA_ZB_OPA_DECAL2, "G_RM_RA_ZB_OPA_DECAL2"}, + {G_RM_RA_ZB_OPA_INTER, "G_RM_RA_ZB_OPA_INTER"}, + {G_RM_RA_ZB_OPA_INTER2, "G_RM_RA_ZB_OPA_INTER2"}, + {G_RM_AA_OPA_SURF, "G_RM_AA_OPA_SURF"}, + {G_RM_AA_OPA_SURF2, "G_RM_AA_OPA_SURF2"}, + {G_RM_AA_XLU_SURF, "G_RM_AA_XLU_SURF"}, + {G_RM_AA_XLU_SURF2, "G_RM_AA_XLU_SURF2"}, + {G_RM_AA_XLU_LINE, "G_RM_AA_XLU_LINE"}, + {G_RM_AA_XLU_LINE2, "G_RM_AA_XLU_LINE2"}, + {G_RM_AA_DEC_LINE, "G_RM_AA_DEC_LINE"}, + {G_RM_AA_DEC_LINE2, "G_RM_AA_DEC_LINE2"}, + {G_RM_AA_TEX_EDGE, "G_RM_AA_TEX_EDGE"}, + {G_RM_AA_TEX_EDGE2, "G_RM_AA_TEX_EDGE2"}, + {G_RM_AA_SUB_SURF, "G_RM_AA_SUB_SURF"}, + {G_RM_AA_SUB_SURF2, "G_RM_AA_SUB_SURF2"}, + {G_RM_AA_PCL_SURF, "G_RM_AA_PCL_SURF"}, + {G_RM_AA_PCL_SURF2, "G_RM_AA_PCL_SURF2"}, + {G_RM_AA_OPA_TERR, "G_RM_AA_OPA_TERR"}, + {G_RM_AA_OPA_TERR2, "G_RM_AA_OPA_TERR2"}, + {G_RM_AA_TEX_TERR, "G_RM_AA_TEX_TERR"}, + {G_RM_AA_TEX_TERR2, "G_RM_AA_TEX_TERR2"}, + {G_RM_AA_TEX_TERR, "G_RM_AA_TEX_TERR"}, + {G_RM_AA_TEX_TERR2, "G_RM_AA_TEX_TERR2"}, + {G_RM_AA_SUB_TERR, "G_RM_AA_SUB_TERR"}, + {G_RM_AA_SUB_TERR2, "G_RM_AA_SUB_TERR2"}, + {G_RM_RA_OPA_SURF, "G_RM_RA_OPA_SURF"}, + {G_RM_RA_OPA_SURF2, "G_RM_RA_OPA_SURF2"}, + {G_RM_ZB_OPA_SURF, "G_RM_ZB_OPA_SURF"}, + {G_RM_ZB_OPA_SURF2, "G_RM_ZB_OPA_SURF2"}, + {G_RM_ZB_XLU_SURF, "G_RM_ZB_XLU_SURF"}, + {G_RM_ZB_XLU_SURF2, "G_RM_ZB_XLU_SURF2"}, + {G_RM_ZB_OPA_DECAL, "G_RM_ZB_OPA_DECAL"}, + {G_RM_ZB_OPA_DECAL2, "G_RM_ZB_OPA_DECAL2"}, + {G_RM_ZB_XLU_DECAL, "G_RM_ZB_XLU_DECAL"}, + {G_RM_ZB_XLU_DECAL2, "G_RM_ZB_XLU_DECAL2"}, + {G_RM_ZB_CLD_SURF, "G_RM_ZB_CLD_SURF"}, + {G_RM_ZB_CLD_SURF2, "G_RM_ZB_CLD_SURF2"}, + {G_RM_ZB_OVL_SURF, "G_RM_ZB_OVL_SURF"}, + {G_RM_ZB_OVL_SURF2, "G_RM_ZB_OVL_SURF2"}, + {G_RM_ZB_PCL_SURF, "G_RM_ZB_PCL_SURF"}, + {G_RM_ZB_PCL_SURF2, "G_RM_ZB_PCL_SURF2"}, + {G_RM_OPA_SURF, "G_RM_OPA_SURF"}, + {G_RM_OPA_SURF2, "G_RM_OPA_SURF2"}, + {G_RM_XLU_SURF, "G_RM_XLU_SURF"}, + {G_RM_XLU_SURF2, "G_RM_XLU_SURF2"}, + {G_RM_CLD_SURF, "G_RM_CLD_SURF"}, + {G_RM_CLD_SURF2, "G_RM_CLD_SURF2"}, + {G_RM_TEX_EDGE, "G_RM_TEX_EDGE"}, + {G_RM_TEX_EDGE2, "G_RM_TEX_EDGE2"}, + {G_RM_PCL_SURF, "G_RM_PCL_SURF"}, + {G_RM_PCL_SURF2, "G_RM_PCL_SURF2"}, + {G_RM_ADD, "G_RM_ADD"}, + {G_RM_ADD2, "G_RM_ADD2"}, + {G_RM_NOOP, "G_RM_NOOP"}, + {G_RM_NOOP2, "G_RM_NOOP2"}, + {G_RM_VISCVG, "G_RM_VISCVG"}, + {G_RM_VISCVG2, "G_RM_VISCVG2"}, + {G_RM_OPA_CI, "G_RM_OPA_CI"}, + {G_RM_OPA_CI2, "G_RM_OPA_CI2"}, + }; + + for (uint32_t k = 0; k < sizeof(tblA) / 4; k++) + { + if ((dd & tblA[k]) == tblA[k]) + { + if (tblA[k] > mode1) + mode1 = tblA[k]; + } + } + + for (uint32_t k = 0; k < sizeof(tblB) / 4; k++) + { + if ((dd & tblB[k]) == tblB[k]) + { + if (tblB[k] > mode2) + mode2 = tblB[k]; + } + } + + std::string mode1Str = str[mode1]; + std::string mode2Str = str[mode2]; + + if (mode1Str == "") + { + mode1Str = StringHelper::Sprintf("0x%08X", mode1); + } + + if (mode2Str == "") + { + if (mode2 & AA_EN) + { + mode2Str += "AA_EN | "; + } + + if (mode2 & Z_CMP) + { + mode2Str += "Z_CMP | "; + } + + if (mode2 & Z_UPD) + { + mode2Str += "Z_UPD | "; + } + + if (mode2 & IM_RD) + { + mode2Str += "IM_RD | "; + } + + if (mode2 & CLR_ON_CVG) + { + mode2Str += "CLR_ON_CVG | "; + } + + if (mode2 & CVG_DST_CLAMP) + { + mode2Str += "CVG_DST_CLAMP | "; + } + + if (mode2 & CVG_DST_WRAP) + { + mode2Str += "CVG_DST_WRAP | "; + } + + if (mode2 & CVG_DST_FULL) + { + mode2Str += "CVG_DST_FULL | "; + } + + if (mode2 & CVG_DST_SAVE) + { + mode2Str += "CVG_DST_SAVE | "; + } + + int32_t zMode = mode2 & 0xC00; + + if (zMode == ZMODE_INTER) + { + mode2Str += "ZMODE_INTER | "; + } + else if (zMode == ZMODE_XLU) + { + mode2Str += "ZMODE_XLU | "; + } + else if (zMode == ZMODE_DEC) + { + mode2Str += "ZMODE_DEC | "; + } + + if (mode2 & CVG_X_ALPHA) + { + mode2Str += "CVG_X_ALPHA | "; + } + + if (mode2 & ALPHA_CVG_SEL) + { + mode2Str += "ALPHA_CVG_SEL | "; + } + + if (mode2 & FORCE_BL) + { + mode2Str += "FORCE_BL | "; + } + + int32_t bp = (mode2 >> 28) & 0b11; + int32_t ba = (mode2 >> 24) & 0b11; + int32_t bm = (mode2 >> 20) & 0b11; + int32_t bb = (mode2 >> 16) & 0b11; + + mode2Str += StringHelper::Sprintf("GBL_c2(%i, %i, %i, %i)", bp, ba, bm, bb); + // mode2Str = StringHelper::Sprintf("0x%08X", mode2); + } + + sprintf(line, "gsDPSetRenderMode(%s, %s),", mode1Str.c_str(), mode2Str.c_str()); + } + else + { + sprintf(line, "gsSPSetOtherMode(0xE2, %i, %i, 0x%08X),", sft, len, dd); + } +} + +void ZDisplayList::Opcode_G_SETOTHERMODE_H(uint64_t data, char* line) +{ + int32_t ss = (data & 0x0000FF0000000000) >> 40; + int32_t nn = (data & 0x000000FF00000000) >> 32; + int32_t dd = (data & 0xFFFFFFFF); + + int32_t sft = 32 - (nn + 1) - ss; + + if (sft == 14) // G_MDSFT_TEXTLUT + { + const char* types[] = {"G_TT_NONE", "G_TT_NONE", "G_TT_RGBA16", "G_TT_IA16"}; + sprintf(line, "gsDPSetTextureLUT(%s),", types[dd >> 14]); + } + else + sprintf(line, "gsSPSetOtherMode(0xE3, %i, %i, 0x%08X),", sft, nn + 1, dd); +} + +void ZDisplayList::Opcode_G_LOADTLUT(uint64_t data, [[maybe_unused]] const std::string& prefix, + char* line) +{ + int32_t t = (data & 0x0000000007000000) >> 24; + int32_t ccc = (data & 0x00000000003FF000) >> 14; + + lastTexWidth = sqrt(ccc + 1); + lastTexHeight = sqrt(ccc + 1); + + lastTexLoaded = true; + lastTexIsPalette = true; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + printf("TextureGenCheck G_LOADTLUT (lastCISiz: %i)\n", (uint32_t)lastCISiz); + + TextureGenCheck(); + + sprintf(line, "gsDPLoadTLUTCmd(%i, %i),", t, ccc); +} + +void ZDisplayList::Opcode_G_ENDDL([[maybe_unused]] const std::string& prefix, char* line) +{ + sprintf(line, "gsSPEndDisplayList(),"); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + printf("TextureGenCheck G_ENDDL\n"); + + TextureGenCheck(); +} + +static int32_t GfxdCallback_FormatSingleEntry() +{ + ZDisplayList* self = static_cast(gfxd_udata_get()); + gfxd_puts("\t"); + gfxd_macro_dflt(); + gfxd_puts(","); + + auto macroId = gfxd_macro_id(); + + switch (macroId) + { + case gfxd_SP1Triangle: + case gfxd_SP2Triangles: + if (self->lastTexture != nullptr && self->lastTexture->IsColorIndexed() && + !self->lastTexture->HasTlut()) + { + auto tex = self->lastTexture; + auto tlut = self->lastTlut; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + { + if (tlut != nullptr) + printf("CI texture '%s' (0x%X), TLUT: '%s' (0x%X)\n", tex->GetName().c_str(), + tex->GetRawDataIndex(), tlut->GetName().c_str(), + tlut->GetRawDataIndex()); + else + printf("CI texture '%s' (0x%X), TLUT: null\n", tex->GetName().c_str(), + tex->GetRawDataIndex()); + } + + if (tlut != nullptr && !tex->HasTlut()) + tex->SetTlut(tlut); + } + break; + } + + // dont print a new line after the last command + switch (macroId) + { + case gfxd_SPEndDisplayList: + case gfxd_SPBranchList: + break; + + default: + gfxd_puts("\n"); + break; + } + + return 0; +} + +static int32_t GfxdCallback_Vtx(uint32_t seg, int32_t count) +{ + ZDisplayList* self = static_cast(gfxd_udata_get()); + uint32_t vtxOffset = Seg2Filespace(seg, self->parent->baseAddress); + + if (GETSEGNUM(seg) == self->parent->segment) + { + Declaration* decl; + + // Check for vertex intersections from other display lists + // TODO: These two could probably be condenced to one... + decl = self->parent->GetDeclarationRanged(vtxOffset + (count * 16)); + if (decl != nullptr) + { + int32_t diff = decl->address - vtxOffset; + + if (diff > 0) + count = diff / 16; + else + count = 0; + } + + decl = self->parent->GetDeclarationRanged(vtxOffset); + if (decl != nullptr) + { + int32_t diff = decl->address - vtxOffset; + + if (diff > 0) + count = diff / 16; + else + count = 0; + } + + if (count > 0) + { + std::vector vtxList; + vtxList.reserve(count); + + uint32_t currentPtr = vtxOffset; + for (int32_t i = 0; i < count; i++) + { + ZVtx vtx(self->parent); + vtx.ExtractFromFile(currentPtr); + + vtxList.push_back(vtx); + currentPtr += 16; + } + + bool keyAlreadyOccupied = self->vertices.find(vtxOffset) != self->vertices.end(); + + if (self->GetName() == "gSunDL") + { + for (auto& vtx: vtxList) + { + vtx.t = (((vtx.t >> 5) - 1) / 2) << 5; + } + } + + // In some cases a vtxList already exists at vtxOffset. Only override the existing list + // if the new one is bigger. + if (!keyAlreadyOccupied || + (keyAlreadyOccupied && vtxList.size() > self->vertices[vtxOffset].size())) + self->vertices[vtxOffset] = vtxList; + } + } + + self->references.push_back(seg); + + if (!Globals::Instance->otrMode) + gfxd_puts("@r"); + + return 1; +} + +static int32_t GfxdCallback_Texture(segptr_t seg, int32_t fmt, int32_t siz, int32_t width, + int32_t height, [[maybe_unused]] int32_t pal) +{ + ZDisplayList* self = static_cast(gfxd_udata_get()); + uint32_t texOffset = Seg2Filespace(seg, self->parent->baseAddress); + + self->lastTexWidth = width; + self->lastTexHeight = height; + self->lastTexAddr = texOffset; + self->lastTexSeg = seg; + self->lastTexFmt = static_cast(fmt); + self->lastTexSiz = static_cast(siz); + self->lastTexLoaded = true; + self->lastTexIsPalette = false; + + self->TextureGenCheck(); + + std::string texName; + Globals::Instance->GetSegmentedPtrName(seg, self->parent, "", texName, self->parent->workerID); + + gfxd_puts(texName.c_str()); + + return 1; +} + +static int32_t GfxdCallback_Palette(uint32_t seg, [[maybe_unused]] int32_t idx, int32_t count) +{ + ZDisplayList* self = static_cast(gfxd_udata_get()); + uint32_t palOffset = Seg2Filespace(seg, self->parent->baseAddress); + + self->lastTexWidth = sqrt(count); + self->lastTexHeight = sqrt(count); + self->lastTexAddr = palOffset; + self->lastTexSeg = seg; + self->lastTexSiz = F3DZEXTexSizes::G_IM_SIZ_16b; + self->lastTexFmt = F3DZEXTexFormats::G_IM_FMT_RGBA; + self->lastTexLoaded = true; + self->lastTexIsPalette = true; + + self->TextureGenCheck(); + + std::string palName; + Globals::Instance->GetSegmentedPtrName(seg, self->parent, "", palName, self->parent->workerID); + + gfxd_puts(palName.c_str()); + + return 1; +} + +static int32_t GfxdCallback_DisplayList(uint32_t seg) +{ + ZDisplayList* self = static_cast(gfxd_udata_get()); + uint32_t dListOffset = GETSEGOFFSET(seg); + uint32_t dListSegNum = GETSEGNUM(seg); + + std::string dListName = ""; + bool addressFound = + Globals::Instance->GetSegmentedPtrName(seg, self->parent, "Gfx", dListName, false, self->parent->workerID); + + if (!addressFound) + { + if (self->parent->segment == dListSegNum) + { + ZDisplayList* newDList = new ZDisplayList(self->parent); + newDList->ExtractFromBinary( + dListOffset, + self->GetDListLength(self->parent->GetRawData(), dListOffset, self->dListType)); + newDList->SetName(newDList->GetDefaultName(self->parent->GetName())); + self->otherDLists.push_back(newDList); + dListName = newDList->GetName(); + } + else + { + Globals::Instance->WarnHardcodedPointer(seg, self->parent, self, + self->GetRawDataIndex()); + } + } + + gfxd_puts(dListName.c_str()); + + return 1; +} + +static int32_t GfxdCallback_Matrix(uint32_t seg) +{ + std::string mtxName; + ZDisplayList* self = static_cast(gfxd_udata_get()); + + bool addressFound = + Globals::Instance->GetSegmentedPtrName(seg, self->parent, "Mtx", mtxName, false, self->parent->workerID); + + if (!addressFound) + { + if (GETSEGNUM(seg) == self->parent->segment) + { + Declaration* decl = + self->parent->GetDeclaration(Seg2Filespace(seg, self->parent->baseAddress)); + if (decl == nullptr) + { + ZMtx mtx(self->parent); + mtx.SetName(mtx.GetDefaultName(self->GetName())); + mtx.ExtractFromFile(Seg2Filespace(seg, self->parent->baseAddress)); + mtx.DeclareVar(self->GetName(), ""); + + mtx.GetSourceOutputCode(self->GetName()); + self->mtxList.push_back(mtx); + mtxName = "&" + mtx.GetName(); + } + } + else + { + Globals::Instance->WarnHardcodedPointer(seg, self->parent, self, + self->GetRawDataIndex()); + } + } + + gfxd_puts(mtxName.c_str()); + + return 1; +} + +void ZDisplayList::DeclareReferences(const std::string& prefix) +{ + std::string sourceOutput; + + if (Globals::Instance->useLegacyZDList) + sourceOutput += ProcessLegacy(prefix); + else + sourceOutput += ProcessGfxDis(prefix); + + // Iterate through our vertex lists, connect intersecting lists. + if (vertices.size() > 0) + { + std::vector>> verticesSorted(vertices.begin(), + vertices.end()); + + for (size_t i = 0; i < verticesSorted.size() - 1; i++) + { + size_t vtxSize = vertices[verticesSorted[i].first].size() * 16; + + if ((verticesSorted[i].first + vtxSize) > verticesSorted[i + 1].first) + { + int32_t intersectAmt = + (verticesSorted[i].first + vtxSize) - verticesSorted[i + 1].first; + int32_t intersectIndex = intersectAmt / 16; + + for (size_t j = intersectIndex; j < verticesSorted[i + 1].second.size(); j++) + vertices[verticesSorted[i].first].push_back(verticesSorted[i + 1].second[j]); + + vertices.erase(verticesSorted[i + 1].first); + verticesSorted.erase(verticesSorted.begin() + i + 1); + + i--; + } + } + + // Generate Vertex Declarations + for (auto& item : vertices) + { + std::string declaration = ""; + + offset_t curAddr = item.first; + auto& firstVtx = item.second.at(0); + + for (auto vtx : item.second) + declaration += StringHelper::Sprintf("\t%s,\n", vtx.GetBodySourceCode().c_str()); + + Declaration* decl = parent->AddDeclarationArray( + curAddr, firstVtx.GetDeclarationAlignment(), + item.second.size() * firstVtx.GetRawDataSize(), firstVtx.GetSourceTypeName(), + firstVtx.GetDefaultName(name), item.second.size(), declaration); + + /*for (auto vtx : item.second) + { + ZVtx* nVtx = new ZVtx(vtx.parent); + nVtx->x = vtx.x; + nVtx->y = vtx.y; + nVtx->z = vtx.z; + nVtx->flag = vtx.flag; + nVtx->s = vtx.s; + nVtx->t = vtx.t; + nVtx->r = vtx.r; + nVtx->g = vtx.g; + nVtx->b = vtx.b; + nVtx->a = vtx.a; + decl->vertexHack.push_back(nVtx); + }*/ + + decl->isExternal = true; + } + } + + Declaration* decl = DeclareVar("", sourceOutput); + decl->references = references; + + // Iterate through our vertex lists, connect intersecting lists. + if (vertices.size() > 0) + { + std::vector>> verticesSorted(vertices.begin(), + vertices.end()); + + for (size_t i = 0; i < verticesSorted.size() - 1; i++) + { + // int32_t vtxSize = verticesSorted[i].second.size() * 16; + size_t vtxSize = vertices[verticesSorted[i].first].size() * 16; + + if ((verticesSorted[i].first + vtxSize) > verticesSorted[i + 1].first) + { + int32_t intersectAmt = + (verticesSorted[i].first + vtxSize) - verticesSorted[i + 1].first; + int32_t intersectIndex = intersectAmt / 16; + + for (size_t j = intersectIndex; j < verticesSorted[i + 1].second.size(); j++) + vertices[verticesSorted[i].first].push_back(verticesSorted[i + 1].second[j]); + + vertices.erase(verticesSorted[i + 1].first); + verticesSorted.erase(verticesSorted.begin() + i + 1); + + i--; + } + } + + // Generate Vertex Declarations + std::vector vtxKeys; + vtxKeys.reserve(vertices.size()); + for (auto& item : vertices) + vtxKeys.push_back(item.first); + + // for (pair> item : vertices) + for (size_t i = 0; i < vtxKeys.size(); i++) + { + auto& item = vertices[vtxKeys[i]]; + + //std::string declaration; + + //for (auto& vtx : item) + //declaration += StringHelper::Sprintf("\t%s,\n", vtx.GetBodySourceCode().c_str()); + + // Ensure there's always a trailing line feed to prevent dumb warnings. + // Please don't remove this line, unless you somehow made a way to prevent + // that warning when building the OoT repo. + //declaration += "\n"; + + if (parent != nullptr) + { + std::string vtxName; + ZResource* vtxRes = parent->FindResource(vtxKeys[i]); + + if (vtxRes != nullptr) + vtxName = vtxRes->GetName(); + else + vtxName = StringHelper::Sprintf("%sVtx_%06X", prefix.c_str(), vtxKeys[i]); + + auto filepath = Globals::Instance->outputPath / vtxName; + std::string incStr = + StringHelper::Sprintf("%s.%s.inc", filepath.string().c_str(), "vtx"); + + Declaration* vtxDecl = parent->AddDeclarationIncludeArray( + vtxKeys[i], incStr, item.size() * 16, "Vtx", vtxName, item.size()); + vtxDecl->isExternal = true; + } + } + } +} + +std::string ZDisplayList::ProcessLegacy(const std::string& prefix) +{ + char line[4096]; + std::string sourceOutput; + + for (size_t i = 0; i < instructions.size(); i++) + { + uint8_t opcode = (uint8_t)(instructions[i] >> 56); + uint64_t data = instructions[i]; + sourceOutput += " "; + + auto start = std::chrono::steady_clock::now(); + + int32_t optimizationResult = OptimizationChecks(i, sourceOutput, prefix); + + if (optimizationResult != -1) + { + i += optimizationResult - 1; + line[0] = '\0'; + } + else + { + if (dListType == DListType::F3DZEX) + ParseF3DZEX((F3DZEXOpcode)opcode, data, i, prefix, line); + else + ParseF3DEX((F3DEXOpcode)opcode, data, prefix, line); + } + + auto end = std::chrono::steady_clock::now(); + int64_t diff = std::chrono::duration_cast(end - start).count(); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG && diff > 5) + printf("F3DOP: 0x%02X, TIME: %" PRIi64 "ms\n", opcode, diff); + + sourceOutput += line; + + if (i < instructions.size() - 1) + sourceOutput += "\n"; + } + + return sourceOutput; +} + +std::string ZDisplayList::ProcessGfxDis([[maybe_unused]] const std::string& prefix) +{ + std::string sourceOutput; + + OutputFormatter outputformatter; + int32_t dListSize = instructions.size() * sizeof(instructions[0]); + + gfxd_input_buffer(instructions.data(), dListSize); + gfxd_endian(gfxd_endian_little, sizeof(uint64_t)); // tell gfxdis what format the data is + + gfxd_macro_fn(GfxdCallback_FormatSingleEntry); // format for each command entry + gfxd_vtx_callback(GfxdCallback_Vtx); // handle vertices + gfxd_timg_callback(GfxdCallback_Texture); // handle textures + gfxd_tlut_callback(GfxdCallback_Palette); // handle palettes + gfxd_dl_callback(GfxdCallback_DisplayList); // handle child display lists + gfxd_mtx_callback(GfxdCallback_Matrix); // handle matrices + gfxd_output_callback( + outputformatter.StaticWriter()); // convert tabs to 4 spaces and enforce 120 line limit + + gfxd_enable(gfxd_emit_dec_color); // use decimal for colors + + // set microcode. see gfxd.h for more options. + gfxd_target(gfxd_f3dex2); + + gfxd_udata_set(this); + gfxd_execute(); // generate display list + sourceOutput += outputformatter.GetOutput(); // write formatted display list + + MergeConnectingVertexLists(); + + return sourceOutput; +} + +void ZDisplayList::MergeConnectingVertexLists() +{ + if (vertices.size() > 0) + { + std::vector>> vertexKeys(vertices.begin(), + vertices.end()); + std::pair> lastItem = vertexKeys.at(0); + + for (size_t i = 1; i < vertexKeys.size(); i++) + { + std::pair> curItem = vertexKeys[i]; + + size_t lastItemEnd = lastItem.first + (lastItem.second.size() * 16); + bool lastItemIntersects = lastItemEnd >= curItem.first; + + if (lastItemIntersects) + { + int intersectedVtxStart = (lastItemEnd - curItem.first) / 16; + + for (size_t j = intersectedVtxStart; j < curItem.second.size(); j++) + vertices[lastItem.first].push_back(curItem.second[j]); + + vertices.erase(curItem.first); + vertexKeys.erase(vertexKeys.begin() + i); + + lastItem.second = vertices[lastItem.first]; + + i--; + } + else + lastItem = curItem; + } + } +} + +void ZDisplayList::TextureGenCheck() +{ + if (TextureGenCheck(lastTexWidth, lastTexHeight, lastTexAddr, lastTexSeg, lastTexFmt, + lastTexSiz, lastTexLoaded, lastTexIsPalette, this)) + { + lastTexAddr = 0; + lastTexLoaded = false; + lastTexIsPalette = false; + } +} + +bool ZDisplayList::TextureGenCheck(int32_t texWidth, int32_t texHeight, uint32_t texAddr, + uint32_t texSeg, F3DZEXTexFormats texFmt, F3DZEXTexSizes texSiz, + bool texLoaded, bool texIsPalette, ZDisplayList* self) +{ + uint32_t segmentNumber = GETSEGNUM(texSeg); + + if (!texIsPalette) + self->lastTexture = nullptr; + else + self->lastTlut = nullptr; + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_DEBUG) + printf("TextureGenCheck seg=%i width=%i height=%i ispal=%i addr=0x%06X\n", segmentNumber, + texWidth, texHeight, texIsPalette, texAddr); + + if ((texSeg != 0 || texAddr != 0) && texWidth > 0 && texHeight > 0 && texLoaded && + Globals::Instance->HasSegment(segmentNumber, self->parent->workerID)) + { + ZFile* auxParent = nullptr; + if (segmentNumber == self->parent->segment) + { + auxParent = self->parent; + } + else + { + // Try to find a non-external file (i.e., one we are actually extracting) + // which has the same segment number we are looking for. + auto segs = Globals::Instance->GetSegmentRefFiles(self->parent->workerID); + for (auto& otherFile : segs[segmentNumber]) + { + if (!otherFile->isExternalFile) + { + auxParent = otherFile; + } + } + } + + if (auxParent == nullptr) + { + // We can't declare the texture in any of the files we are extracting. + return false; + } + + if (auxParent->IsOffsetInFileRange(texAddr)) + { + ZTexture* tex = auxParent->GetTextureResource(texAddr); + if (tex != nullptr) + tex->isPalette = texIsPalette; + else + { + tex = new ZTexture(auxParent); + tex->ExtractFromBinary(texAddr, texWidth, texHeight, + TexFormatToTexType(texFmt, texSiz), texIsPalette); + auxParent->AddTextureResource(texAddr, tex); + } + + if (!texIsPalette) + self->lastTexture = tex; + else + self->lastTlut = tex; + + if (auxParent->GetDeclaration(texAddr) == nullptr) + { + tex->DeclareVar(self->GetName(), ""); + } + + return true; + } + } + + return false; +} + +TextureType ZDisplayList::TexFormatToTexType(F3DZEXTexFormats fmt, F3DZEXTexSizes siz) +{ + if (fmt == F3DZEXTexFormats::G_IM_FMT_RGBA) + { + if (siz == F3DZEXTexSizes::G_IM_SIZ_16b) + return TextureType::RGBA16bpp; + else if (siz == F3DZEXTexSizes::G_IM_SIZ_32b) + return TextureType::RGBA32bpp; + } + else if (fmt == F3DZEXTexFormats::G_IM_FMT_CI) + { + if (Globals::Instance->useLegacyZDList) + return TextureType::Palette8bpp; + else + { + if (siz == F3DZEXTexSizes::G_IM_SIZ_4b) + return TextureType::Palette4bpp; + else if (siz == F3DZEXTexSizes::G_IM_SIZ_8b) + return TextureType::Palette8bpp; + } + } + else if (fmt == F3DZEXTexFormats::G_IM_FMT_IA) + { + if (siz == F3DZEXTexSizes::G_IM_SIZ_4b) + return TextureType::GrayscaleAlpha4bpp; + else if (siz == F3DZEXTexSizes::G_IM_SIZ_8b) + return TextureType::GrayscaleAlpha8bpp; + else if (siz == F3DZEXTexSizes::G_IM_SIZ_16b) + return TextureType::GrayscaleAlpha16bpp; + } + else if (fmt == F3DZEXTexFormats::G_IM_FMT_I) + { + if (siz == F3DZEXTexSizes::G_IM_SIZ_4b) + return TextureType::Grayscale4bpp; + else if (siz == F3DZEXTexSizes::G_IM_SIZ_8b) + return TextureType::Grayscale8bpp; + else if (siz == F3DZEXTexSizes::G_IM_SIZ_16b) + return TextureType::Grayscale8bpp; + } + + return TextureType::RGBA16bpp; +} + +bool ZDisplayList::IsExternalResource() const +{ + return false; +} + +std::string ZDisplayList::GetExternalExtension() const +{ + return "dlist"; +} + +std::string ZDisplayList::GetSourceTypeName() const +{ + return "Gfx"; +} + +ZResourceType ZDisplayList::GetResourceType() const +{ + return ZResourceType::DisplayList; +} + +size_t ZDisplayList::GetRawDataSize() const +{ + return instructions.size() * 8; +} + +DeclarationAlignment ZDisplayList::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align8; +} diff --git a/ZAPDTR/ZAPD/ZDisplayList.h b/ZAPDTR/ZAPD/ZDisplayList.h new file mode 100644 index 000000000..09c7c0546 --- /dev/null +++ b/ZAPDTR/ZAPD/ZDisplayList.h @@ -0,0 +1,265 @@ +#pragma once + +#include "ZMtx.h" +#include "ZResource.h" +#include "ZRoom/ZRoom.h" +#include "ZTexture.h" +#include "ZVtx.h" +#include "tinyxml2.h" + +#include +#include +#include + +enum class F3DEXOpcode +{ + G_SPNOOP = 0x00, + G_MTX = 0x01, + G_MOVEMEM = 0x03, + G_VTX = 0x04, + G_DL = 0x06, + G_LOAD_UCODE = 0xAF, + G_BRANCH_Z = 0xB0, + G_TRI2 = 0xB1, + G_MODIFYVTX = 0xB2, + G_RDPHALF_2 = 0xB3, + G_RDPHALF_1 = 0xB4, + G_QUAD = 0xB5, + G_CLEARGEOMETRYMODE = 0xB6, + G_SETGEOMETRYMODE = 0xB7, + G_ENDDL = 0xB8, + G_SETOTHERMODE_L = 0xB9, + G_SETOTHERMODE_H = 0xBA, + G_TEXTURE = 0xBB, + G_MOVEWORD = 0xBC, + G_POPMTX = 0xBD, + G_CULLDL = 0xBE, + G_TRI1 = 0xBF, + G_NOOP = 0xC0, + G_TEXRECT = 0xE4, + G_TEXRECTFLIP = 0xE5, + G_RDPLOADSYNC = 0xE6, + G_RDPPIPESYNC = 0xE7, + G_RDPTILESYNC = 0xE8, + G_RDPFULLSYNC = 0xE9, + G_SETKEYGB = 0xEA, + G_SETKEYR = 0xEB, + G_SETCONVERT = 0xEC, + G_SETSCISSOR = 0xED, + G_SETPRIMDEPTH = 0xEE, + G_RDPSETOTHERMODE = 0xEF, + G_LOADTLUT = 0xF0, + G_SETTILESIZE = 0xF2, + G_LOADBLOCK = 0xF3, + G_LOADTILE = 0xF4, + G_SETTILE = 0xF5, + G_FILLRECT = 0xF6, + G_SETFILLCOLOR = 0xF7, + G_SETFOGCOLOR = 0xF8, + G_SETBLENDCOLOR = 0xF9, + G_SETPRIMCOLOR = 0xFA, + G_SETENVCOLOR = 0xFB, + G_SETCOMBINE = 0xFC, + G_SETTIMG = 0xFD, + G_SETZIMG = 0xFE, + G_SETCIMG = 0xFF +}; + +enum class F3DZEXOpcode +{ + G_NOOP = 0x00, + G_VTX = 0x01, + G_MODIFYVTX = 0x02, + G_CULLDL = 0x03, + G_BRANCH_Z = 0x04, + G_TRI1 = 0x05, + G_TRI2 = 0x06, + G_QUAD = 0x07, + G_SPECIAL_3 = 0xD3, + G_SPECIAL_2 = 0xD4, + G_SPECIAL_1 = 0xD5, + G_DMA_IO = 0xD6, + G_TEXTURE = 0xD7, + G_POPMTX = 0xD8, + G_GEOMETRYMODE = 0xD9, + G_MTX = 0xDA, + G_MOVEWORD = 0xDB, + G_MOVEMEM = 0xDC, + G_LOAD_UCODE = 0xDD, + G_DL = 0xDE, + G_ENDDL = 0xDF, + G_SPNOOP = 0xE0, + G_RDPHALF_1 = 0xE1, + G_SETOTHERMODE_L = 0xE2, + G_SETOTHERMODE_H = 0xE3, + G_TEXRECT = 0xE4, + G_TEXRECTFLIP = 0xE5, + G_RDPLOADSYNC = 0xE6, + G_RDPPIPESYNC = 0xE7, + G_RDPTILESYNC = 0xE8, + G_RDPFULLSYNC = 0xE9, + G_SETKEYGB = 0xEA, + G_SETKEYR = 0xEB, + G_SETCONVERT = 0xEC, + G_SETSCISSOR = 0xED, + G_SETPRIMDEPTH = 0xEE, + G_RDPSETOTHERMODE = 0xEF, + G_LOADTLUT = 0xF0, + G_RDPHALF_2 = 0xF1, + G_SETTILESIZE = 0xF2, + G_LOADBLOCK = 0xF3, + G_LOADTILE = 0xF4, + G_SETTILE = 0xF5, + G_FILLRECT = 0xF6, + G_SETFILLCOLOR = 0xF7, + G_SETFOGCOLOR = 0xF8, + G_SETBLENDCOLOR = 0xF9, + G_SETPRIMCOLOR = 0xFA, + G_SETENVCOLOR = 0xFB, + G_SETCOMBINE = 0xFC, + G_SETTIMG = 0xFD, + G_SETZIMG = 0xFE, + G_SETCIMG = 0xFF, +}; + +enum class F3DZEXTexFormats +{ + G_IM_FMT_RGBA, + G_IM_FMT_YUV, + G_IM_FMT_CI, + G_IM_FMT_IA, + G_IM_FMT_I +}; + +enum class F3DZEXTexSizes +{ + G_IM_SIZ_4b, + G_IM_SIZ_8b, + G_IM_SIZ_16b, + G_IM_SIZ_32b +}; + +enum class DListType +{ + F3DZEX, + F3DEX, +}; + +enum class OoTSegments +{ + DirectReference = 0, + TitleStatic = 1, + Scene = 2, + Room = 3, + GameplayKeep = 4, + FieldDungeonKeep = 5, + Object = 6, + LinkAnimation = 7, + IconItemStatic = 8, + IconItem24Static = 9, + Unknown10 = 10, + Unknown11 = 11, + Unknown12 = 12, + IconFieldDungeonStatic = 13, + IconItemLanguageStatic = 14, + ZBuffer = 15, + FrameBuffer = 16, +}; + + +class ZDisplayList : public ZResource +{ +protected: + static TextureType TexFormatToTexType(F3DZEXTexFormats fmt, F3DZEXTexSizes siz); + + void ParseF3DZEX(F3DZEXOpcode opcode, uint64_t data, int32_t i, const std::string& prefix, + char* line); + void ParseF3DEX(F3DEXOpcode opcode, uint64_t data, const std::string& prefix, char* line); + + // Various Instruction Optimizations + bool SequenceCheck(std::vector sequence, int32_t startIndex); + int32_t OptimizationChecks(int32_t startIndex, std::string& output, const std::string& prefix); + int32_t OptimizationCheck_LoadTextureBlock(int32_t startIndex, std::string& output, + const std::string& prefix); + // int32_t OptimizationCheck_LoadMultiBlock(int32_t startIndex, std::string& output, std::string + // prefix); + + // F3DEX Specific Opcode Values + void Opcode_F3DEX_G_SETOTHERMODE_L(uint64_t data, char* line); + + // Shared Opcodes between F3DZEX and F3DEX + void Opcode_G_DL(uint64_t data, const std::string& prefix, char* line); + void Opcode_G_MODIFYVTX(uint64_t data, char* line); + void Opcode_G_CULLDL(uint64_t data, char* line); + void Opcode_G_TRI1(uint64_t data, char* line); + void Opcode_G_TRI2(uint64_t data, char* line); + void Opcode_G_MTX(uint64_t data, char* line); + void Opcode_G_VTX(uint64_t data, char* line); + void Opcode_G_TEXTURE(uint64_t data, char* line); + void Opcode_G_SETTIMG(uint64_t data, const std::string& prefix, char* line); + void Opcode_G_SETTILE(uint64_t data, char* line); + void Opcode_G_SETTILESIZE(uint64_t data, const std::string& prefix, char* line); + void Opcode_G_LOADBLOCK(uint64_t data, char* line); + void Opcode_G_SETCOMBINE(uint64_t data, char* line); + void Opcode_G_SETPRIMCOLOR(uint64_t data, char* line); + void Opcode_G_SETOTHERMODE_L(uint64_t data, char* line); + void Opcode_G_SETOTHERMODE_H(uint64_t data, char* line); + void Opcode_G_LOADTLUT(uint64_t data, const std::string& prefix, char* line); + void Opcode_G_ENDDL(const std::string& prefix, char* line); + +public: + std::vector instructions; + + int32_t lastTexWidth, lastTexHeight, lastTexAddr, lastTexSeg; + F3DZEXTexFormats lastTexFmt; + F3DZEXTexSizes lastTexSiz, lastTexSizTest, lastCISiz; + bool lastTexLoaded; + bool lastTexIsPalette; + + DListType dListType; + + std::map> vertices; + std::vector otherDLists; + + ZTexture* lastTexture = nullptr; + ZTexture* lastTlut = nullptr; + + std::vector references; + std::vector mtxList; + + ZDisplayList(ZFile* nParent); + ~ZDisplayList(); + + void ExtractWithXML(tinyxml2::XMLElement* reader, uint32_t nRawDataIndex) override; + void ExtractFromBinary(uint32_t nRawDataIndex, int32_t rawDataSize); + + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetDefaultName(const std::string& prefix) const override; + + void TextureGenCheck(); + static bool TextureGenCheck(int32_t texWidth, int32_t texHeight, uint32_t texAddr, + uint32_t texSeg, F3DZEXTexFormats texFmt, F3DZEXTexSizes texSiz, + bool texLoaded, bool texIsPalette, ZDisplayList* self); + static int32_t GetDListLength(const std::vector& rawData, uint32_t rawDataIndex, + DListType dListType); + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; + void DeclareReferences(const std::string& prefix) override; + std::string ProcessLegacy(const std::string& prefix); + std::string ProcessGfxDis(const std::string& prefix); + + // Combines vertex lists from the vertices map which touch or intersect + void MergeConnectingVertexLists(); + + bool IsExternalResource() const override; + std::string GetExternalExtension() const override; + std::string GetSourceTypeName() const override; + + ZResourceType GetResourceType() const override; + +protected: + size_t numInstructions; +}; diff --git a/ZAPDTR/ZAPD/ZFile.cpp b/ZAPDTR/ZAPD/ZFile.cpp new file mode 100644 index 000000000..25bf5a02e --- /dev/null +++ b/ZAPDTR/ZAPD/ZFile.cpp @@ -0,0 +1,1505 @@ +#include "ZFile.h" + +#include +#include +#include +#include + +#include "Globals.h" +#include "OutputFormatter.h" +#include "Utils/BinaryWriter.h" +#include "Utils/BitConverter.h" +#include "Utils/Directory.h" +#include +#include "Utils/MemoryStream.h" +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZAnimation.h" +#include "ZArray.h" +#include "ZBackground.h" +#include "ZBlob.h" +#include "ZCollision.h" +#include "ZCutscene.h" +#include "ZDisplayList.h" +#include "ZLimb.h" +#include "ZMtx.h" +#include "ZRoom/ZRoom.h" +#include "ZScalar.h" +#include "ZSkeleton.h" +#include "ZSymbol.h" +#include "ZTexture.h" +#include "ZVector.h" +#include "ZVtx.h" +#undef FindResource + +ZFile::ZFile() +{ + resources = std::vector(); + basePath = ""; + declarations = std::map(); + defines = ""; + baseAddress = 0; + rangeStart = 0x000000000; + rangeEnd = 0xFFFFFFFF; + workerID = 0; +} + +ZFile::ZFile(const fs::path& nOutPath, const std::string& nName) : ZFile() +{ + name = nName; + outName = nName; + outputPath = nOutPath; +} + +ZFile::ZFile(ZFileMode nMode, tinyxml2::XMLElement* reader, const fs::path& nBasePath, + const fs::path& nOutPath, const std::string& filename, const fs::path& nXmlFilePath, int nWorkerID) + : ZFile() +{ + xmlFilePath = nXmlFilePath; + if (nBasePath == "") + basePath = Directory::GetCurrentDirectory(); + else + basePath = nBasePath; + + if (nOutPath == "") + outputPath = Directory::GetCurrentDirectory(); + else + outputPath = nOutPath; + + mode = nMode; + workerID = nWorkerID; + + ParseXML(reader, filename); + if (mode != ZFileMode::ExternalFile) + DeclareResourceSubReferences(); +} + +ZFile::~ZFile() +{ + for (ZResource* res : resources) + delete res; + + for (auto d : declarations) + delete d.second; + + for (auto sym : symbolResources) + delete sym.second; +} + +void ZFile::ParseXML(tinyxml2::XMLElement* reader, const std::string& filename) +{ + assert(mode != ZFileMode::Invalid); + + if (filename == "") + name = reader->Attribute("Name"); + else + name = filename; + + outName = name; + const char* outNameXml = reader->Attribute("OutName"); + if (outNameXml != nullptr) + outName = outNameXml; + + // TODO: This should be a variable on the ZFile, but it is a large change in order to force all + // ZResource types to have a parent ZFile. + const char* gameStr = reader->Attribute("Game"); + if (reader->Attribute("Game") != nullptr) + { + if (std::string_view(gameStr) == "MM") + Globals::Instance->game = ZGame::MM_RETAIL; + else if (std::string_view(gameStr) == "SW97" || std::string_view(gameStr) == "OOTSW97") + Globals::Instance->game = ZGame::OOT_SW97; + else if (std::string_view(gameStr) == "OOT") + Globals::Instance->game = ZGame::OOT_RETAIL; + else + { + std::string errorHeader = + StringHelper::Sprintf("'Game' type '%s' is not supported.", gameStr); + HANDLE_ERROR_PROCESS(WarningType::InvalidAttributeValue, errorHeader, ""); + } + } + + if (reader->Attribute("BaseAddress") != nullptr) + baseAddress = StringHelper::StrToL(reader->Attribute("BaseAddress"), 16); + + if (reader->Attribute("RangeStart") != nullptr) + rangeStart = StringHelper::StrToL(reader->Attribute("RangeStart"), 16); + + if (reader->Attribute("RangeEnd") != nullptr) + rangeEnd = StringHelper::StrToL(reader->Attribute("RangeEnd"), 16); + + if (reader->Attribute("Compilable") != nullptr) + isCompilable = true; + + if (rangeStart > rangeEnd) + HANDLE_ERROR_PROCESS( + WarningType::Always, + StringHelper::Sprintf("'RangeStart' 0x%06X must be before 'RangeEnd' 0x%06X", + rangeStart, rangeEnd), + ""); + + const char* segmentXml = reader->Attribute("Segment"); + if (segmentXml != nullptr) + { + if (!StringHelper::HasOnlyDigits(segmentXml)) + { + HANDLE_ERROR_PROCESS(WarningType::Always, + StringHelper::Sprintf("error: Invalid segment value '%s': must be " + "a decimal between 0 and 15 inclusive", + segmentXml), + ""); + } + + segment = StringHelper::StrToL(segmentXml, 10); + if (segment > 15) + { + if (segment == 128) + { +#ifdef DEPRECATION_ON + HANDLE_WARNING_PROCESS( + WarningType::Always, "warning: segment 128 is deprecated.", + "Remove 'Segment=\"128\"' from the xml to use virtual addresses\n"); +#endif + } + else + { + HANDLE_ERROR_PROCESS( + WarningType::Always, + StringHelper::Sprintf("error: invalid segment value '%s': must be a decimal " + "number between 0 and 15 inclusive", + segmentXml), + ""); + } + } + } + Globals::Instance->AddSegment(segment, this, workerID); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + { + if (segment == 0x80) + { + printf("File '%s' using virtual addresses.\n", GetName().c_str()); + } + else + { + printf("File '%s' using segment %X.\n", GetName().c_str(), segment); + } + } + + const char* segmentDefines = reader->Attribute("Defines"); + if (segmentDefines != NULL) + { + makeDefines = true; + } + + if (mode == ZFileMode::Extract || mode == ZFileMode::ExternalFile || mode == ZFileMode::ExtractDirectory) + { + if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) + { + if (!DiskFile::Exists((basePath / name).string())) + { + std::string errorHeader = StringHelper::Sprintf("binary file '%s' does not exist.", + (basePath / name).c_str()); + HANDLE_ERROR_PROCESS(WarningType::Always, errorHeader, ""); + } + } + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + rawData = Globals::Instance->GetBaseromFile(name); + else + rawData = Globals::Instance->GetBaseromFile((basePath / name).string()); + + if (reader->Attribute("RangeEnd") == nullptr) + rangeEnd = rawData.size(); + } + + std::unordered_set nameSet; + std::unordered_set outNameSet; + std::unordered_set offsetSet; + + auto nodeMap = *GetNodeMap(); + uint32_t rawDataIndex = 0; + + for (tinyxml2::XMLElement* child = reader->FirstChildElement(); child != nullptr; + child = child->NextSiblingElement()) + { + const char* nameXml = child->Attribute("Name"); + const char* outNameXml = child->Attribute("OutName"); + const char* offsetXml = child->Attribute("Offset"); + + // Check for repeated attributes. + if (offsetXml != nullptr) + { + if (!StringHelper::IsValidOffset(std::string_view(offsetXml))) + { + HANDLE_ERROR(WarningType::InvalidXML, + StringHelper::Sprintf("Invalid offset %s entered", offsetXml), ""); + } + rawDataIndex = strtol(offsetXml, NULL, 16); + + if (offsetSet.find(offsetXml) != offsetSet.end()) + { + std::string errorHeader = + StringHelper::Sprintf("repeated 'Offset' attribute: %s", offsetXml); + HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, ""); + } + offsetSet.insert(offsetXml); + } + else + { + HANDLE_WARNING_RESOURCE(WarningType::MissingOffsets, this, nullptr, rawDataIndex, + StringHelper::Sprintf("no offset specified for %s.", nameXml), + ""); + } + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + printf("%s: 0x%06X\n", nameXml, rawDataIndex); + + if (outNameXml != nullptr) + { + if (outNameSet.find(outNameXml) != outNameSet.end()) + { + std::string errorHeader = + StringHelper::Sprintf("repeated 'OutName' attribute: %s", outNameXml); + HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, ""); + } + outNameSet.insert(outNameXml); + } + if (nameXml != nullptr) + { + if (nameSet.find(nameXml) != nameSet.end()) + { + std::string errorHeader = + StringHelper::Sprintf("repeated 'Name' attribute: %s", nameXml); + HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, ""); + } + nameSet.insert(nameXml); + } + + std::string nodeName = std::string(child->Name()); + + if (nodeMap.find(nodeName) != nodeMap.end()) + { + ZResource* nRes = nodeMap[nodeName](this); + + if (mode == ZFileMode::Extract || mode == ZFileMode::ExternalFile || + mode == ZFileMode::ExtractDirectory) + { + if (!isCompilable) + nRes->ExtractWithXML(child, rawDataIndex); + } + switch (nRes->GetResourceType()) + { + case ZResourceType::Texture: + AddTextureResource(rawDataIndex, static_cast(nRes)); + break; + + case ZResourceType::Symbol: + AddSymbolResource(rawDataIndex, static_cast(nRes)); + break; + + default: + AddResource(nRes); + break; + } + + rawDataIndex += nRes->GetRawDataSize(); + } + else if (std::string_view(child->Name()) == "File") + { + std::string errorHeader = "Can't declare a inside a "; + HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, ""); + } + else + { + std::string errorHeader = StringHelper::Sprintf( + "Unknown element found inside a element: %s", nodeName.c_str()); + HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, ""); + } + } +} + +void ZFile::DeclareResourceSubReferences() +{ + for (size_t i = 0; i < resources.size(); i++) + { + resources.at(i)->DeclareReferences(name); + } +} + +void ZFile::BuildSourceFile() +{ + if (mode == ZFileMode::ExternalFile) + return; + + if (!Directory::Exists(outputPath)) + Directory::CreateDirectory(outputPath.string()); + + GenerateSourceFiles(); +} + +std::string ZFile::GetName() const +{ + return name; +} + +std::string ZFile::GetOutName() const +{ + return outName.string(); +} + +ZFileMode ZFile::GetMode() const +{ + return mode; +} + +const fs::path& ZFile::GetXmlFilePath() const +{ + return xmlFilePath; +} + +const std::vector& ZFile::GetRawData() const +{ + return rawData; +} + +void ZFile::ExtractResources() +{ + if (mode == ZFileMode::ExternalFile) + return; + + if (!Directory::Exists(outputPath)) + Directory::CreateDirectory(outputPath.string()); + + if (!Directory::Exists(GetSourceOutputFolderPath())) + Directory::CreateDirectory(GetSourceOutputFolderPath().string()); + + for (size_t i = 0; i < resources.size(); i++) + resources[i]->ParseRawDataLate(); + for (size_t i = 0; i < resources.size(); i++) + resources[i]->DeclareReferencesLate(name); + + if (Globals::Instance->genSourceFile) + GenerateSourceFiles(); + + auto memStreamFile = std::shared_ptr(new MemoryStream()); + BinaryWriter writerFile = BinaryWriter(memStreamFile); + + ExporterSet* exporterSet = Globals::Instance->GetExporterSet(); + + if (exporterSet != nullptr && exporterSet->beginFileFunc != nullptr) + exporterSet->beginFileFunc(this); + + int totalMs = 0; + + for (ZResource* res : resources) + { + auto start = std::chrono::steady_clock::now(); + + auto memStreamRes = std::shared_ptr(new MemoryStream()); + BinaryWriter writerRes = BinaryWriter(memStreamRes); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + printf("Saving resource %s\n", res->GetName().c_str()); + + res->Save(outputPath); + + // Check if we have an exporter "registered" for this resource type + ZResourceExporter* exporter = Globals::Instance->GetExporter(res->GetResourceType()); + if (exporter != nullptr) + { + // exporter->Save(res, Globals::Instance->outputPath.string(), &writerFile); + exporter->Save(res, Globals::Instance->outputPath.string(), &writerRes); + } + + if (exporterSet != nullptr && exporterSet->resSaveFunc != nullptr) + exporterSet->resSaveFunc(res, writerRes); + + auto end = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast(end - start).count(); + + totalMs += diff; + + //printf("Res %s in %lims\n", res->GetName().c_str(), diff); + } + + //printf("File %s in %lims\n", GetName().c_str(), totalMs); + + if (memStreamFile->GetLength() > 0) + { + DiskFile::WriteAllBytes(StringHelper::Sprintf("%s%s.bin", + Globals::Instance->outputPath.string().c_str(), + GetName().c_str()), + memStreamFile->ToVector()); + } + + writerFile.Close(); + + if (exporterSet != nullptr && exporterSet->endFileFunc != nullptr) + exporterSet->endFileFunc(this); +} + +void ZFile::AddResource(ZResource* res) +{ + resources.push_back(res); +} + +ZResource* ZFile::FindResource(offset_t rawDataIndex) +{ + for (ZResource* res : resources) + { + if (res->GetRawDataIndex() == rawDataIndex) + return res; + } + + return nullptr; +} + +std::vector ZFile::GetResourcesOfType(ZResourceType resType) +{ + std::vector resList; + resList.reserve(resources.size()); + + for (ZResource* res : resources) + { + if (res->GetResourceType() == resType) + resList.push_back(res); + } + + return resList; +} + +Declaration* ZFile::AddDeclaration(offset_t address, DeclarationAlignment alignment, size_t size, + const std::string& varType, const std::string& varName, + const std::string& body) +{ + bool validOffset = DeclarationSanityChecks(address, varName); + if (!validOffset) + return nullptr; + + Declaration* decl = GetDeclaration(address); + if (decl == nullptr) + { + decl = Declaration::Create(address, alignment, size, varType, varName, body); + declarations[address] = decl; + } + else + { + decl->alignment = alignment; + decl->size = size; + decl->declType = varType; + decl->declName = varName; + decl->declBody = body; + } + + return decl; +} + +Declaration* ZFile::AddDeclarationArray(offset_t address, DeclarationAlignment alignment, + size_t size, const std::string& varType, + const std::string& varName, size_t arrayItemCnt, + const std::string& body) +{ + bool validOffset = DeclarationSanityChecks(address, varName); + if (!validOffset) + return nullptr; + + Declaration* decl = GetDeclaration(address); + if (decl == nullptr) + { + decl = Declaration::CreateArray(address, alignment, size, varType, varName, body, + arrayItemCnt); + + declarations[address] = decl; + } + else + { + if (decl->isPlaceholder) + decl->declName = varName; + + decl->alignment = alignment; + decl->size = size; + decl->declType = varType; + decl->isArray = true; + decl->arrayItemCnt = arrayItemCnt; + decl->declBody = body; + } + + return decl; +} + +Declaration* ZFile::AddDeclarationArray(offset_t address, DeclarationAlignment alignment, + size_t size, const std::string& varType, + const std::string& varName, + const std::string& arrayItemCntStr, const std::string& body) +{ + bool validOffset = DeclarationSanityChecks(address, varName); + if (!validOffset) + return nullptr; + + Declaration* decl = GetDeclaration(address); + if (decl == nullptr) + { + decl = Declaration::CreateArray(address, alignment, size, varType, varName, body, + arrayItemCntStr); + + declarations[address] = decl; + } + else + { + decl->alignment = alignment; + decl->size = size; + decl->declType = varType; + decl->declName = varName; + decl->isArray = true; + decl->arrayItemCntStr = arrayItemCntStr; + decl->declBody = body; + } + return decl; +} + +Declaration* ZFile::AddDeclarationPlaceholder(offset_t address, const std::string& varName) +{ + bool validOffset = DeclarationSanityChecks(address, varName); + if (!validOffset) + return nullptr; + + Declaration* decl; + if (declarations.find(address) == declarations.end()) + { + decl = Declaration::CreatePlaceholder(address, varName); + declarations[address] = decl; + } + else + decl = declarations[address]; + + return decl; +} + +Declaration* ZFile::AddDeclarationInclude(offset_t address, const std::string& includePath, + size_t size, const std::string& varType, + const std::string& varName) +{ + bool validOffset = DeclarationSanityChecks(address, varName); + if (!validOffset) + return nullptr; + + Declaration* decl = GetDeclaration(address); + if (decl == nullptr) + { + decl = Declaration::CreateInclude(address, includePath, size, varType, varName); + declarations[address] = decl; + } + else + { + decl->includePath = includePath; + decl->size = size; + decl->declType = varType; + decl->declName = varName; + } + return decl; +} + +Declaration* ZFile::AddDeclarationIncludeArray(offset_t address, std::string& includePath, + size_t size, const std::string& varType, + const std::string& varName, size_t arrayItemCnt) +{ + bool validOffset = DeclarationSanityChecks(address, varName); + if (!validOffset) + return nullptr; + + if (StringHelper::StartsWith(includePath, "assets/extracted/")) + includePath = "assets/" + StringHelper::Split(includePath, "assets/extracted/")[1]; + if (StringHelper::StartsWith(includePath, "assets/custom/")) + includePath = "assets/" + StringHelper::Split(includePath, "assets/custom/")[1]; + + Declaration* decl = GetDeclaration(address); + if (decl == nullptr) + { + decl = Declaration::CreateInclude(address, includePath, size, varType, varName); + + decl->isArray = true; + decl->arrayItemCnt = arrayItemCnt; + + declarations[address] = decl; + } + else + { + decl->includePath = includePath; + decl->declType = varType; + decl->declName = varName; + decl->size = size; + decl->isArray = true; + decl->arrayItemCnt = arrayItemCnt; + } + return decl; +} + +Declaration* ZFile::AddDeclarationIncludeArray(offset_t address, std::string& includePath, + size_t size, const std::string& varType, + const std::string& varName, + const std::string& defines, size_t arrayItemCnt) +{ + bool validOffset = DeclarationSanityChecks(address, varName); + if (!validOffset) + return nullptr; + + if (StringHelper::StartsWith(includePath, "assets/extracted/")) + includePath = "assets/" + StringHelper::Split(includePath, "assets/extracted/")[1]; + if (StringHelper::StartsWith(includePath, "assets/custom/")) + includePath = "assets/" + StringHelper::Split(includePath, "assets/custom/")[1]; + + Declaration* decl = GetDeclaration(address); + if (decl == nullptr) + { + decl = Declaration::CreateInclude(address, includePath, size, varType, varName, defines); + + decl->isArray = true; + decl->arrayItemCnt = arrayItemCnt; + + declarations[address] = decl; + } + else + { + decl->includePath = includePath; + decl->declType = varType; + decl->declName = varName; + decl->defines = defines; + decl->size = size; + decl->isArray = true; + decl->arrayItemCnt = arrayItemCnt; + } + return decl; +} + +bool ZFile::DeclarationSanityChecks(uint32_t address, const std::string& varName) +{ + assert(GETSEGNUM(address) == 0); + assert(varName != ""); +#ifdef DEVELOPMENT + if (address == 0x0000) + { + [[maybe_unused]] int32_t bp = 0; + } +#endif + + if (!IsOffsetInFileRange(address)) + { + fprintf(stderr, + "%s: Warning in %s\n" + "\t Tried to declare a variable outside of this file's range. Ignoring...\n" + "\t\t Variable's name: %s\n" + "\t\t Variable's offset: 0x%06X\n", + __PRETTY_FUNCTION__, name.c_str(), varName.c_str(), address); + return false; + } + + return true; +} + +bool ZFile::GetDeclarationPtrName(segptr_t segAddress, const std::string& expectedType, + std::string& declName) const +{ + if (segAddress == 0) + { + declName = "NULL"; + return true; + } + + uint32_t offset = Seg2Filespace(segAddress, baseAddress); + Declaration* decl = GetDeclaration(offset); + if (GETSEGNUM(segAddress) != segment || decl == nullptr) + { + declName = StringHelper::Sprintf("0x%08X", segAddress); + return false; + } + + if (expectedType != "" && expectedType != "void*") + { + if (expectedType != decl->declType && "static " + expectedType != decl->declType) + { + declName = StringHelper::Sprintf("0x%08X", segAddress); + return false; + } + } + + if (!decl->isArray) + declName = "&" + decl->declName; + else + declName = decl->declName; + return true; +} + +bool ZFile::GetDeclarationArrayIndexedName(segptr_t segAddress, size_t elementSize, + const std::string& expectedType, + std::string& declName) const +{ + if (segAddress == 0) + { + declName = "NULL"; + return true; + } + + uint32_t address = Seg2Filespace(segAddress, baseAddress); + Declaration* decl = GetDeclarationRanged(address); + if (GETSEGNUM(segAddress) != segment || decl == nullptr || !decl->isArray) + { + declName = StringHelper::Sprintf("0x%08X", segAddress); + return false; + } + + if (expectedType != "" && expectedType != "void*") + { + if (expectedType != decl->declType && "static " + expectedType != decl->declType) + { + declName = StringHelper::Sprintf("0x%08X", segAddress); + return false; + } + } + + if (decl->address == address) + { + declName = decl->declName; + return true; + } + + if ((address - decl->address) % elementSize != 0 || !(address < decl->address + decl->size)) + { + declName = StringHelper::Sprintf("0x%08X", segAddress); + return false; + } + + uint32_t index = (address - decl->address) / elementSize; + declName = StringHelper::Sprintf("&%s[%u]", decl->declName.c_str(), index); + return true; +} + +Declaration* ZFile::GetDeclaration(offset_t address) const +{ + if (declarations.find(address) != declarations.end()) + return declarations.at(address); + + return nullptr; +} + +Declaration* ZFile::GetDeclarationRanged(offset_t address) const +{ + for (const auto decl : declarations) + { + if (address >= decl.first && address < decl.first + decl.second->size) + return decl.second; + } + + return nullptr; +} + +bool ZFile::HasDeclaration(offset_t address) +{ + assert(GETSEGNUM(address) == 0); + return declarations.find(address) != declarations.end(); +} + +size_t ZFile::GetDeclarationSizeFromNeighbor(uint32_t declarationAddress) +{ + auto currentDecl = declarations.find(declarationAddress); + if (currentDecl == declarations.end()) + return 0; + + auto nextDecl = currentDecl; + std::advance(nextDecl, 1); + if (nextDecl == declarations.end()) + return GetRawData().size() - currentDecl->first; + + return nextDecl->first - currentDecl->first; +} + +void ZFile::GenerateSourceFiles() +{ + std::string sourceOutput; + + sourceOutput += "#include \"ultra64.h\"\n"; + sourceOutput += "#include \"z64.h\"\n"; + sourceOutput += "#include \"macros.h\"\n"; + sourceOutput += GetHeaderInclude(); + + bool hasZRoom = false; + for (const auto& res : resources) + { + ZResourceType resType = res->GetResourceType(); + if (resType == ZResourceType::Room || resType == ZResourceType::Scene || + resType == ZResourceType::AltHeader) + { + hasZRoom = true; + break; + } + } + + if (hasZRoom) + { + sourceOutput += GetZRoomHeaderInclude(); + } + + sourceOutput += GetExternalFileHeaderInclude(); + + GeneratePlaceholderDeclarations(); + + // Generate Code + for (size_t i = 0; i < resources.size(); i++) + { + ZResource* res = resources.at(i); + res->GetSourceOutputCode(name); + } + + sourceOutput += ProcessDeclarations(); + + fs::path outPath = GetSourceOutputFolderPath() / outName.stem().concat(".c"); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + printf("Writing C file: %s\n", outPath.c_str()); + + if (!Globals::Instance->otrMode) + { + OutputFormatter formatter; + formatter.Write(sourceOutput); + + DiskFile::WriteAllText(outPath, formatter.GetOutput()); + } + + GenerateSourceHeaderFiles(); +} + +void ZFile::GenerateSourceHeaderFiles() +{ + OutputFormatter formatter; + // Use parent folder and output name as guard as some headers have the same output name + std::string guard = xmlFilePath.parent_path().stem().string() + "_" + outName.stem().string(); + + std::transform(guard.begin(), guard.end(), guard.begin(), ::toupper); + formatter.Write( + StringHelper::Sprintf("#ifndef %s_H\n#define %s_H 1\n\n", guard.c_str(), guard.c_str())); + formatter.Write("#include \"align_asset_macro.h\"\n"); + + std::set nameSet; + for (ZResource* res : resources) + { + if (res->GetResourceType() == ZResourceType::TextureAnimation) + { + int bp = 1; + } + std::string resSrc = res->GetSourceOutputHeader("", &nameSet); + if (!resSrc.empty()) + { + formatter.Write(resSrc.front() == '\n' ? resSrc : "\n" + resSrc); + formatter.Write(res == resources.back() ? "" : "\n"); + } + } + + for (auto& sym : symbolResources) + { + formatter.Write("\n\n"); + formatter.Write(sym.second->GetSourceOutputHeader("", &nameSet)); + } + + formatter.Write(ProcessExterns()); + + formatter.Write(StringHelper::Sprintf("\n#endif // %s_H\n", guard.c_str())); + + fs::path headerFilename = GetSourceOutputFolderPath() / outName.stem().concat(".h"); + + if (Globals::Instance->verbosity >= VerbosityLevel::VERBOSITY_INFO) + printf("Writing H file: %s\n", headerFilename.c_str()); + + std::string output = formatter.GetOutput(); + + if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) + DiskFile::WriteAllText(headerFilename, output); + else if (Globals::Instance->sourceOutputPath != "") + { + std::string xmlPath = xmlFilePath.string(); + xmlPath = StringHelper::Replace(xmlPath, "\\", "/"); + auto pathList = StringHelper::Split(xmlPath, "/"); + std::string outPath = ""; + + //for (int i = 0; i < 3; i++) + // outPath += pathList[i] + "/"; + // + for (int i = 0; i < pathList.size(); i++) + { + if (i == pathList.size() - 1) + { + outPath += Path::GetFileNameWithoutExtension(pathList[i]) + "/"; + outPath += outName.string() + ".h"; + } + else if (pathList[i] != "xml") + { + outPath += pathList[i]; + } + else + { + continue; + } + + if (i < pathList.size() - 1) + outPath += "/"; + } + + DiskFile::WriteAllText(outPath, output); + } +} + +std::string ZFile::GetHeaderInclude() const +{ + std::string headers = StringHelper::Sprintf( + "#include \"%s.h\"\n", (outName.parent_path() / outName.stem()).string().c_str()); + + return headers; +} + +std::string ZFile::GetZRoomHeaderInclude() const +{ + std::string headers; + headers += "#include \"segment_symbols.h\"\n"; + headers += "#include \"command_macros_base.h\"\n"; + headers += "#include \"z64cutscene_commands.h\"\n"; + headers += "#include \"variables.h\"\n"; + return headers; +} + +std::string ZFile::GetExternalFileHeaderInclude() const +{ + std::string externalFilesIncludes = ""; + + for (ZFile* externalFile : Globals::Instance->files) + { + if (externalFile != this) + { + fs::path outputFolderPath = externalFile->GetSourceOutputFolderPath(); + if (outputFolderPath == this->GetSourceOutputFolderPath()) + outputFolderPath = externalFile->outName.stem(); + else + outputFolderPath /= externalFile->outName.stem(); + + externalFilesIncludes += + StringHelper::Sprintf("#include \"%s.h\"\n", outputFolderPath.string().c_str()); + } + } + + return externalFilesIncludes; +} + +void ZFile::GeneratePlaceholderDeclarations() +{ + // Generate placeholder declarations + for (ZResource* res : resources) + { + if (GetDeclaration(res->GetRawDataIndex()) != nullptr) + { + continue; + } + + Declaration* decl = res->DeclareVar(GetName(), ""); + + if (decl != nullptr) + { + decl->staticConf = res->GetStaticConf(); + if (res->GetResourceType() == ZResourceType::Symbol) + { + decl->staticConf = StaticConfig::Off; + } + } + } +} + +void ZFile::AddTextureResource(uint32_t offset, ZTexture* tex) +{ + for (auto res : resources) + assert(res->GetRawDataIndex() != offset); + + resources.push_back(tex); + texturesResources[offset] = tex; +} + +ZTexture* ZFile::GetTextureResource(uint32_t offset) const +{ + auto tex = texturesResources.find(offset); + if (tex != texturesResources.end()) + return tex->second; + + return nullptr; +} + +void ZFile::AddSymbolResource(uint32_t offset, ZSymbol* sym) +{ + symbolResources[offset] = sym; +} + +ZSymbol* ZFile::GetSymbolResource(uint32_t offset) const +{ + auto sym = symbolResources.find(offset); + if (sym != symbolResources.end()) + return sym->second; + + return nullptr; +} + +ZSymbol* ZFile::GetSymbolResourceRanged(uint32_t offset) const +{ + for (const auto decl : symbolResources) + { + if (offset >= decl.first && offset < decl.first + decl.second->GetRawDataSize()) + return decl.second; + } + + return nullptr; +} + +fs::path ZFile::GetSourceOutputFolderPath() const +{ + return outputPath / outName.parent_path(); +} + +bool ZFile::IsOffsetInFileRange(uint32_t offset) const +{ + if (!(offset < rawData.size())) + return false; + + return rangeStart <= offset && offset < rangeEnd; +} +bool ZFile::IsSegmentedInFilespaceRange(segptr_t segAddress) const +{ + uint8_t segnum = GETSEGNUM(segAddress); + uint32_t offset = Seg2Filespace(segAddress, baseAddress); + + if (segment != segnum) + return false; + + return IsOffsetInFileRange(offset); +} + +std::map* ZFile::GetNodeMap() +{ + static std::map nodeMap; + return &nodeMap; +} + +void ZFile::RegisterNode(std::string nodeName, ZResourceFactoryFunc* nodeFunc) +{ + std::map* nodeMap = GetNodeMap(); + (*nodeMap)[nodeName] = nodeFunc; +} + +std::string ZFile::ProcessDeclarations() +{ + std::string output; + + if (declarations.size() == 0) + return output; + + defines += ProcessTextureIntersections(name); + + // printf("RANGE START: 0x%06X - RANGE END: 0x%06X\n", rangeStart, rangeEnd); + + MergeNeighboringDeclarations(); + + for (std::pair item : declarations) + ProcessDeclarationText(item.second); + + for (std::pair item : declarations) + { + while (item.second->size % 4 != 0) + item.second->size++; + } + + HandleUnaccountedData(); + + // Go through include declarations + // First, handle the prototypes (static only for now) + for (std::pair item : declarations) + { + output += item.second->GetStaticForwardDeclarationStr(); + } + + output += "\n"; + + // Next, output the actual declarations + for (const auto& item : declarations) + { + if (!IsOffsetInFileRange(item.first)) + continue; + + if (item.second->includePath != "") + { + if (item.second->isExternal) + { + if (!Globals::Instance->otrMode) + { + // HACK + std::string extType; + + if (item.second->declType == "Gfx") + extType = "dlist"; + else if (item.second->declType == "Vtx") + extType = "vtx"; + + auto filepath = outputPath / item.second->declName; + DiskFile::WriteAllText( + StringHelper::Sprintf("%s.%s.inc", filepath.string().c_str(), extType.c_str()), + item.second->declBody); + } + } + + output += item.second->GetExternalDeclarationStr(); + } + else if (item.second->declType != "") + { + output += item.second->GetNormalDeclarationStr(); + } + } + + return output; +} + +void ZFile::MergeNeighboringDeclarations() +{ + // Optimization: See if there are any arrays side by side that can be merged... + std::vector> declarationKeys(declarations.begin(), + declarations.end()); + + std::pair lastItem = declarationKeys.at(0); + + for (size_t i = 1; i < declarationKeys.size(); i++) + { + std::pair curItem = declarationKeys[i]; + + if (curItem.second->isArray && lastItem.second->isArray) + { + if (curItem.second->declType == lastItem.second->declType) + { + if (!curItem.second->declaredInXml && !lastItem.second->declaredInXml) + { + // TEST: For now just do Vtx declarations... + if (lastItem.second->declType == "Vtx") + { + int32_t sizeDiff = curItem.first - (lastItem.first + lastItem.second->size); + + // Make sure there isn't an unaccounted inbetween these two + if (sizeDiff == 0) + { + lastItem.second->size += curItem.second->size; + lastItem.second->arrayItemCnt += curItem.second->arrayItemCnt; + lastItem.second->declBody += "\n" + curItem.second->declBody; + declarations.erase(curItem.first); + declarationKeys.erase(declarationKeys.begin() + i); + delete curItem.second; + i--; + continue; + } + } + } + } + } + + lastItem = curItem; + } +} + +void ZFile::ProcessDeclarationText(Declaration* decl) +{ + size_t refIndex = 0; + + if (!(decl->references.size() > 0)) + return; + + for (size_t i = 0; i < decl->declBody.size() - 1; i++) + { + char c = decl->declBody[i]; + char c2 = decl->declBody[i + 1]; + + if (c == '@' && c2 == 'r') + { + std::string vtxName; + Globals::Instance->GetSegmentedArrayIndexedName(decl->references[refIndex], 0x10, this, + "Vtx", vtxName, workerID); + decl->declBody.replace(i, 2, vtxName); + + refIndex++; + + if (refIndex >= decl->references.size()) + break; + } + } +} + +std::string ZFile::ProcessExterns() +{ + std::string output = ""; + bool hadDefines = true; // Previous declaration included defines. + + for (const auto& item : declarations) + { + if (!IsOffsetInFileRange(item.first)) + { + continue; + } + + std::string itemDefines = item.second->GetDefinesStr(); + // Add a newline above if previous has no defines and this one does. + if (!hadDefines && (itemDefines.length() > 0)) + { + output.push_back('\n'); + } + output += item.second->GetExternStr(); + output += itemDefines; + + // Newline below if this one has defines. + if ((hadDefines = (itemDefines.length() > 0))) + { + output.push_back('\n'); + } + } + + output += defines; + + return output; +} + +std::string ZFile::ProcessTextureIntersections([[maybe_unused]] const std::string& prefix) +{ + if (texturesResources.empty()) + return ""; + + std::string defines; + std::vector> texturesSorted(texturesResources.begin(), + texturesResources.end()); + + for (size_t i = 0; i < texturesSorted.size() - 1; i++) + { + uint32_t currentOffset = texturesSorted[i].first; + uint32_t nextOffset = texturesSorted[i + 1].first; + auto& currentTex = texturesResources.at(currentOffset); + int texSize = currentTex->GetRawDataSize(); + + if (currentTex->WasDeclaredInXml()) + { + // We believe the user is right. + continue; + } + + if ((currentOffset + texSize) > nextOffset) + { + uint32_t offsetDiff = nextOffset - currentOffset; + if (currentTex->isPalette) + { + // Shrink palette so it doesn't overlap + currentTex->SetDimensions(offsetDiff / currentTex->GetPixelMultiplyer(), 1); + + if (declarations.find(currentOffset) != declarations.end()) + declarations.at(currentOffset)->size = currentTex->GetRawDataSize(); + currentTex->DeclareVar(GetName(), ""); + } + else + { + std::string texName; + std::string texNextName; + GetDeclarationPtrName(currentOffset, "", texName); + + Declaration* nextDecl = GetDeclaration(nextOffset); + if (nextDecl == nullptr) + texNextName = texturesResources.at(nextOffset)->GetName(); + else + texNextName = nextDecl->declName; + +#if 0 + defines += StringHelper::Sprintf("#define %s ((u32)%s + 0x%06X)\n", + texNextName.c_str(), texName.c_str(), offsetDiff); +#endif + + delete declarations[nextOffset]; + declarations.erase(nextOffset); + texturesResources.erase(nextOffset); + texturesSorted.erase(texturesSorted.begin() + i + 1); + + i--; + } + } + } + + return defines; +} + +void ZFile::HandleUnaccountedData() +{ + uint32_t lastAddr = 0; + uint32_t lastSize = 0; + std::vector declsAddresses; + + if (Globals::Instance->otrMode) + return; + + declsAddresses.reserve(declarations.size()); + for (const auto& item : declarations) + { + declsAddresses.push_back(item.first); + } + + bool breakLoop = false; + for (offset_t currentAddress : declsAddresses) + { + if (currentAddress >= rangeEnd) + { + breakLoop = true; + break; + } + + if (currentAddress < rangeStart) + { + lastAddr = currentAddress; + continue; + } + + breakLoop = HandleUnaccountedAddress(currentAddress, lastAddr, lastSize); + if (breakLoop) + break; + + lastAddr = currentAddress; + } + + if (!breakLoop) + { + // TODO: change rawData.size() to rangeEnd + // HandleUnaccountedAddress(rangeEnd, lastAddr, lastSize); + HandleUnaccountedAddress(rawData.size(), lastAddr, lastSize); + } +} + +bool ZFile::HandleUnaccountedAddress(offset_t currentAddress, offset_t lastAddr, uint32_t& lastSize) +{ + if (currentAddress != lastAddr && declarations.find(lastAddr) != declarations.end()) + { + Declaration* lastDecl = declarations.at(lastAddr); + lastSize = lastDecl->size; + + if (lastAddr + lastSize > currentAddress) + { + Declaration* currentDecl = declarations.at(currentAddress); + + std::string intersectionInfo = StringHelper::Sprintf( + "Resource from 0x%06X:0x%06X (%s) conflicts with 0x%06X (%s).", lastAddr, + lastAddr + lastSize, lastDecl->declName.c_str(), currentAddress, + currentDecl->declName.c_str()); + HANDLE_WARNING_RESOURCE(WarningType::Intersection, this, nullptr, currentAddress, + "intersection detected", intersectionInfo); + } + } + + uint32_t unaccountedAddress = lastAddr + lastSize; + + if (unaccountedAddress != currentAddress && lastAddr >= rangeStart && + unaccountedAddress < rangeEnd) + { + int diff = currentAddress - unaccountedAddress; + bool nonZeroUnaccounted = false; + + std::string src = " "; + + if (currentAddress > rawData.size()) + { + throw std::runtime_error(StringHelper::Sprintf( + "ZFile::ProcessDeclarations(): Fatal error while processing XML '%s'.\n" + "\t Offset '0x%X' is outside of the limits of file '%s', which has a size of " + "'0x%X'.\n" + "\t Aborting...", + xmlFilePath.c_str(), currentAddress, name.c_str(), rawData.size())); + } + + // Handle Align8 + if (currentAddress % 8 == 0 && diff % 8 != 0) + { + Declaration* currentDecl = GetDeclaration(currentAddress); + + if (currentDecl != nullptr) + { + if (currentDecl->alignment == DeclarationAlignment::Align8) + { + // Check removed bytes are zeroes + if (BitConverter::ToUInt32BE(rawData, unaccountedAddress + diff - 4) == 0) + { + diff -= 4; + } + } + + if (diff == 0) + { + return false; + } + } + } + + for (int i = 0; i < diff; i++) + { + uint8_t val = rawData.at(unaccountedAddress + i); + src += StringHelper::Sprintf("0x%02X, ", val); + if (val != 0x00) + { + nonZeroUnaccounted = true; + } + + if (Globals::Instance->verboseUnaccounted) + { + if ((i % 4 == 3)) + { + src += StringHelper::Sprintf(" // 0x%06X", unaccountedAddress + i - 3); + if (i != (diff - 1)) + { + src += "\n\t"; + } + } + } + else + { + if ((i % 16 == 15) && (i != (diff - 1))) + src += "\n "; + } + } + + if (declarations.find(unaccountedAddress) == declarations.end() && diff > 0) + { + std::string unaccountedPrefix = "unaccounted"; + + if (diff < 16 && !nonZeroUnaccounted) + { + unaccountedPrefix = "possiblePadding"; + + // Strip unnecessary padding at the end of the file. + if (unaccountedAddress + diff >= rawData.size()) + return true; + } + + Declaration* decl = AddDeclarationArray( + unaccountedAddress, DeclarationAlignment::Align4, diff, "u8", + StringHelper::Sprintf("%s_%s_%06X", name.c_str(), unaccountedPrefix.c_str(), + unaccountedAddress), + diff, src); + + decl->isUnaccounted = true; + if (Globals::Instance->forceUnaccountedStatic) + decl->staticConf = StaticConfig::On; + + if (nonZeroUnaccounted) + { + HANDLE_WARNING_RESOURCE(WarningType::Unaccounted, this, nullptr, unaccountedAddress, + "a non-zero unaccounted block was found", + StringHelper::Sprintf("Block size: '0x%X'", diff)); + } + else if (diff >= 16) + { + HANDLE_WARNING_RESOURCE(WarningType::Unaccounted, this, nullptr, unaccountedAddress, + "a big (size>=0x10) zero-only unaccounted block was found", + StringHelper::Sprintf("Block size: '0x%X'", diff)); + } + } + } + + return false; +} diff --git a/ZAPDTR/ZAPD/ZFile.h b/ZAPDTR/ZAPD/ZFile.h new file mode 100644 index 000000000..92ed99b58 --- /dev/null +++ b/ZAPDTR/ZAPD/ZFile.h @@ -0,0 +1,150 @@ +#pragma once +#define NO_GDI +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "ZSymbol.h" +#include "ZTexture.h" +#include "tinyxml2.h" +#undef FindResource + +enum class ZFileMode +{ + BuildTexture, + BuildOverlay, + BuildBlob, + BuildSourceFile, + BuildBackground, + Extract, + ExternalFile, + ExtractDirectory, + Invalid, + Custom = 1000, // Used for exporter file modes +}; + +enum class ZGame +{ + OOT_RETAIL, + OOT_SW97, + MM_RETAIL +}; + +class ZFile +{ +public: + std::map declarations; + std::vector resources; + std::string defines; + + int workerID; + bool isCompilable = false; + + // Default to using virtual addresses + uint32_t segment = 0x80; + uint32_t baseAddress, rangeStart, rangeEnd; + bool isExternalFile = false; + // Whether to make defines for texture dimensions, and possibly more in future + bool makeDefines = false; + + ZFile(const fs::path& nOutPath, const std::string& nName); + ZFile(ZFileMode nMode, tinyxml2::XMLElement* reader, const fs::path& nBasePath, + const fs::path& nOutPath, const std::string& filename, const fs::path& nXmlFilePath, int nWorkerID); + ~ZFile(); + + std::string GetName() const; + std::string GetOutName() const; + ZFileMode GetMode() const; + const fs::path& GetXmlFilePath() const; + const std::vector& GetRawData() const; + void ExtractResources(); + void BuildSourceFile(); + void AddResource(ZResource* res); + ZResource* FindResource(offset_t rawDataIndex); + std::vector GetResourcesOfType(ZResourceType resType); + + Declaration* AddDeclaration(offset_t address, DeclarationAlignment alignment, size_t size, + const std::string& varType, const std::string& varName, + const std::string& body); + + Declaration* AddDeclarationArray(offset_t address, DeclarationAlignment alignment, size_t size, + const std::string& varType, const std::string& varName, + size_t arrayItemCnt, const std::string& body); + Declaration* AddDeclarationArray(offset_t address, DeclarationAlignment alignment, size_t size, + const std::string& varType, const std::string& varName, + const std::string& arrayItemCntStr, const std::string& body); + + Declaration* AddDeclarationPlaceholder(offset_t address, const std::string& varName); + + Declaration* AddDeclarationInclude(offset_t address, const std::string& includePath, + size_t size, const std::string& varType, + const std::string& varName); + Declaration* AddDeclarationIncludeArray(offset_t address, std::string& includePath, size_t size, + const std::string& varType, const std::string& varName, + size_t arrayItemCnt); + Declaration* AddDeclarationIncludeArray(offset_t address, std::string& includePath, size_t size, + const std::string& varType, const std::string& varName, + const std::string& defines, size_t arrayItemCnt); + + bool GetDeclarationPtrName(segptr_t segAddress, const std::string& expectedType, + std::string& declName) const; + bool GetDeclarationArrayIndexedName(segptr_t segAddress, size_t elementSize, + const std::string& expectedType, + std::string& declName) const; + + Declaration* GetDeclaration(offset_t address) const; + Declaration* GetDeclarationRanged(offset_t address) const; + bool HasDeclaration(offset_t address); + size_t GetDeclarationSizeFromNeighbor(uint32_t declarationAddress); + + std::string GetHeaderInclude() const; + std::string GetZRoomHeaderInclude() const; + std::string GetExternalFileHeaderInclude() const; + + void GeneratePlaceholderDeclarations(); + + void AddTextureResource(uint32_t offset, ZTexture* tex); + ZTexture* GetTextureResource(uint32_t offset) const; + + void AddSymbolResource(uint32_t offset, ZSymbol* sym); + ZSymbol* GetSymbolResource(uint32_t offset) const; + ZSymbol* GetSymbolResourceRanged(uint32_t offset) const; + + fs::path GetSourceOutputFolderPath() const; + + bool IsOffsetInFileRange(uint32_t offset) const; + bool IsSegmentedInFilespaceRange(segptr_t segAddress) const; + + static std::map* GetNodeMap(); + static void RegisterNode(std::string nodeName, ZResourceFactoryFunc* nodeFunc); + +protected: + std::string name; + fs::path outName = ""; + fs::path basePath; + fs::path outputPath; + fs::path xmlFilePath; + std::vector rawData; + + // Keep track of every texture of this ZFile. + // The pointers declared here are "borrowed" (somebody else is the owner), + // so ZFile shouldn't delete/free those textures. + std::map texturesResources; + std::map symbolResources; + ZFileMode mode = ZFileMode::Invalid; + + ZFile(); + void ParseXML(tinyxml2::XMLElement* reader, const std::string& filename); + void DeclareResourceSubReferences(); + void GenerateSourceFiles(); + void GenerateSourceHeaderFiles(); + bool DeclarationSanityChecks(uint32_t address, const std::string& varName); + std::string ProcessDeclarations(); + void MergeNeighboringDeclarations(); + void ProcessDeclarationText(Declaration* decl); + std::string ProcessExterns(); + + std::string ProcessTextureIntersections(const std::string& prefix); + void HandleUnaccountedData(); + bool HandleUnaccountedAddress(offset_t currentAddress, offset_t lastAddr, uint32_t& lastSize); +}; diff --git a/ZAPDTR/ZAPD/ZLimb.cpp b/ZAPDTR/ZAPD/ZLimb.cpp new file mode 100644 index 000000000..736f2de52 --- /dev/null +++ b/ZAPDTR/ZAPD/ZLimb.cpp @@ -0,0 +1,420 @@ +#include "ZLimb.h" + +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZSkeleton.h" + +REGISTER_ZFILENODE(Limb, ZLimb); + +ZLimb::ZLimb(ZFile* nParent) : ZResource(nParent), segmentStruct(nParent) +{ + RegisterOptionalAttribute("EnumName"); + RegisterOptionalAttribute("LimbType"); + RegisterOptionalAttribute("Type"); +} + +void ZLimb::ExtractFromBinary(uint32_t nRawDataIndex, ZLimbType nType) +{ + rawDataIndex = nRawDataIndex; + type = nType; + + // Don't parse raw data of external files + if (parent->GetMode() == ZFileMode::ExternalFile) + return; + + ParseRawData(); +} + +void ZLimb::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + auto& enumNameXml = registeredAttributes.at("EnumName").value; + if (enumNameXml != "") + { + enumName = enumNameXml; + } + + // Reading from a + std::string limbType = registeredAttributes.at("LimbType").value; + if (limbType == "") // Reading from a + limbType = registeredAttributes.at("Type").value; + + if (limbType == "") + { + HANDLE_ERROR_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex, + "missing 'LimbType' attribute in ", ""); + } + + type = GetTypeByAttributeName(limbType); + if (type == ZLimbType::Invalid) + { + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + "invalid value found for 'LimbType' attribute", ""); + } +} + +void ZLimb::ParseRawData() +{ + ZResource::ParseRawData(); + const auto& rawData = parent->GetRawData(); + + if (type == ZLimbType::Curve) + { + childIndex = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0); + siblingIndex = BitConverter::ToUInt8BE(rawData, rawDataIndex + 1); + + dListPtr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 4); + dList2Ptr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 8); + return; + } + if (type == ZLimbType::Legacy) + { + dListPtr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x00); + legTransX = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x04); + legTransY = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x08); + legTransZ = BitConverter::ToFloatBE(rawData, rawDataIndex + 0x0C); + rotX = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x10); + rotY = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x12); + rotZ = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x14); + childPtr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x18); + siblingPtr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x1C); + return; + } + + transX = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + transY = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + transZ = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + + childIndex = rawData.at(rawDataIndex + 6); + siblingIndex = rawData.at(rawDataIndex + 7); + + switch (type) + { + case ZLimbType::LOD: + dList2Ptr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 12); + [[fallthrough]]; + case ZLimbType::Standard: + dListPtr = BitConverter::ToUInt32BE(rawData, rawDataIndex + 8); + break; + + case ZLimbType::Skin: + skinSegmentType = + static_cast(BitConverter::ToInt32BE(rawData, rawDataIndex + 8)); + skinSegment = BitConverter::ToUInt32BE(rawData, rawDataIndex + 12); + if (skinSegmentType == ZLimbSkinType::SkinType_Animated) + { + if (skinSegment != 0 && GETSEGNUM(skinSegment) == parent->segment) + { + uint32_t skinSegmentOffset = Seg2Filespace(skinSegment, parent->baseAddress); + segmentStruct.ExtractFromFile(skinSegmentOffset); + } + } + break; + + case ZLimbType::Curve: + case ZLimbType::Legacy: + break; + + case ZLimbType::Invalid: + assert(!"whoops"); + break; + } +} + +void ZLimb::DeclareReferences(const std::string& prefix) +{ + std::string varPrefix = name; + if (varPrefix == "") + varPrefix = prefix; + + ZResource::DeclareReferences(varPrefix); + + std::string suffix; + switch (type) + { + case ZLimbType::Legacy: + if (childPtr != 0 && GETSEGNUM(childPtr) == parent->segment) + { + uint32_t childOffset = Seg2Filespace(childPtr, parent->baseAddress); + if (!parent->HasDeclaration(childOffset)) + { + ZLimb* child = new ZLimb(parent); + child->ExtractFromBinary(childOffset, ZLimbType::Legacy); + child->DeclareVar(varPrefix, ""); + child->DeclareReferences(varPrefix); + parent->AddResource(child); + } + } + if (siblingPtr != 0 && GETSEGNUM(siblingPtr) == parent->segment) + { + uint32_t siblingdOffset = Seg2Filespace(siblingPtr, parent->baseAddress); + if (!parent->HasDeclaration(siblingdOffset)) + { + ZLimb* sibling = new ZLimb(parent); + sibling->ExtractFromBinary(siblingdOffset, ZLimbType::Legacy); + sibling->DeclareVar(varPrefix, ""); + sibling->DeclareReferences(varPrefix); + parent->AddResource(sibling); + } + } + break; + + case ZLimbType::Curve: + case ZLimbType::LOD: + suffix = "Far"; + if (type == ZLimbType::Curve) + suffix = "Curve2"; + DeclareDList(dList2Ptr, varPrefix, suffix); + [[fallthrough]]; + case ZLimbType::Standard: + suffix = ""; + if (type == ZLimbType::Curve) + suffix = "Curve"; + DeclareDList(dListPtr, varPrefix, suffix); + break; + + case ZLimbType::Skin: + switch (skinSegmentType) + { + case ZLimbSkinType::SkinType_Animated: + if (skinSegment != 0 && GETSEGNUM(skinSegment) == parent->segment) + { + segmentStruct.DeclareReferences(varPrefix); + segmentStruct.GetSourceOutputCode(varPrefix); + } + break; + + case ZLimbSkinType::SkinType_Normal: + DeclareDList(skinSegment, varPrefix, ""); + break; + + default: + break; + } + break; + + case ZLimbType::Invalid: + break; + } +} + +size_t ZLimb::GetRawDataSize() const +{ + switch (type) + { + case ZLimbType::Standard: + case ZLimbType::Curve: + return 0x0C; + + case ZLimbType::LOD: + case ZLimbType::Skin: + return 0x10; + + case ZLimbType::Legacy: + return 0x20; + + case ZLimbType::Invalid: + break; + } + + return 0x0C; +} + +std::string ZLimb::GetBodySourceCode() const +{ + if (Globals::Instance->otrMode) + return ""; + + std::string dListStr; + std::string dListStr2; + Globals::Instance->GetSegmentedArrayIndexedName(dListPtr, 8, parent, "Gfx", dListStr, + parent->workerID); + Globals::Instance->GetSegmentedArrayIndexedName(dList2Ptr, 8, parent, "Gfx", dListStr2, + parent->workerID); + + std::string entryStr = "\n\t"; + if (type == ZLimbType::Legacy) + { + std::string childName; + std::string siblingName; + Globals::Instance->GetSegmentedPtrName(childPtr, parent, "LegacyLimb", childName, + parent->workerID); + Globals::Instance->GetSegmentedPtrName(siblingPtr, parent, "LegacyLimb", siblingName, + parent->workerID); + + entryStr += StringHelper::Sprintf("%s,\n", dListStr.c_str()); + entryStr += + StringHelper::Sprintf("\t{ %ff, %ff, %ff },\n", legTransX, legTransY, legTransZ); + entryStr += StringHelper::Sprintf("\t{ 0x%04X, 0x%04X, 0x%04X },\n", rotX, rotY, rotZ); + entryStr += StringHelper::Sprintf("\t%s,\n", childName.c_str()); + entryStr += StringHelper::Sprintf("\t%s\n", siblingName.c_str()); + } + else + { + std::string childStr; + std::string siblingStr; + if (limbsTable != nullptr) + { + childStr = limbsTable->GetLimbEnumName(childIndex); + siblingStr = limbsTable->GetLimbEnumName(siblingIndex); + } + else + { + childStr = StringHelper::Sprintf("0x%02X", childIndex); + siblingStr = StringHelper::Sprintf("0x%02X", siblingIndex); + } + + if (type != ZLimbType::Curve) + { + entryStr += StringHelper::Sprintf("{ %i, %i, %i }, ", transX, transY, transZ); + } + entryStr += StringHelper::Sprintf("%s, %s,\n", childStr.c_str(), siblingStr.c_str()); + + switch (type) + { + case ZLimbType::Standard: + entryStr += StringHelper::Sprintf("\t%s\n", dListStr.c_str()); + break; + + case ZLimbType::LOD: + case ZLimbType::Curve: + entryStr += + StringHelper::Sprintf("\t{ %s, %s }\n", dListStr.c_str(), dListStr2.c_str()); + break; + + case ZLimbType::Skin: + { + std::string skinSegmentStr; + Globals::Instance->GetSegmentedPtrName(skinSegment, parent, "", skinSegmentStr, + parent->workerID); + entryStr += + StringHelper::Sprintf("\t0x%02X, %s\n", skinSegmentType, skinSegmentStr.c_str()); + } + break; + + case ZLimbType::Legacy: + break; + + case ZLimbType::Invalid: + break; + } + } + + return entryStr; +} + +std::string ZLimb::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sLimb_%06X", prefix.c_str(), rawDataIndex); +} + +std::string ZLimb::GetSourceTypeName() const +{ + return GetSourceTypeName(type); +} + +ZResourceType ZLimb::GetResourceType() const +{ + return ZResourceType::Limb; +} + +ZLimbType ZLimb::GetLimbType() +{ + return type; +} + +void ZLimb::SetLimbType(ZLimbType value) +{ + type = value; +} + +const char* ZLimb::GetSourceTypeName(ZLimbType limbType) +{ + switch (limbType) + { + case ZLimbType::Standard: + return "StandardLimb"; + + case ZLimbType::LOD: + return "LodLimb"; + + case ZLimbType::Skin: + return "SkinLimb"; + + case ZLimbType::Curve: + return "SkelCurveLimb"; + + case ZLimbType::Legacy: + return "LegacyLimb"; + + default: + return "StandardLimb"; + } +} + +ZLimbType ZLimb::GetTypeByAttributeName(const std::string& attrName) +{ + if (attrName == "Standard") + { + return ZLimbType::Standard; + } + if (attrName == "LOD") + { + return ZLimbType::LOD; + } + if (attrName == "Skin") + { + return ZLimbType::Skin; + } + if (attrName == "Curve") + { + return ZLimbType::Curve; + } + if (attrName == "Legacy") + { + return ZLimbType::Legacy; + } + return ZLimbType::Invalid; +} + +void ZLimb::SetLimbIndex(uint8_t nLimbIndex) +{ + limbIndex = nLimbIndex; +} + +void ZLimb::DeclareDList(segptr_t dListSegmentedPtr, const std::string& prefix, + const std::string& limbSuffix) +{ + if (dListSegmentedPtr == 0 || GETSEGNUM(dListSegmentedPtr) != parent->segment) + return; + + uint32_t dlistOffset = Seg2Filespace(dListSegmentedPtr, parent->baseAddress); + if (parent->HasDeclaration(dlistOffset)) + return; + + if (!parent->IsOffsetInFileRange(dlistOffset) || dlistOffset >= parent->GetRawData().size()) + return; + + std::string dlistName; + bool declFound = Globals::Instance->GetSegmentedArrayIndexedName(dListSegmentedPtr, 8, parent, + "Gfx", dlistName, false, parent->workerID); + if (declFound) + return; + + int32_t dlistLength = ZDisplayList::GetDListLength( + parent->GetRawData(), dlistOffset, + Globals::Instance->game == ZGame::OOT_SW97 ? DListType::F3DEX : DListType::F3DZEX); + ZDisplayList* dlist = new ZDisplayList(parent); + dlist->ExtractFromBinary(dlistOffset, dlistLength); + + std::string dListStr = + StringHelper::Sprintf("%s%sDL_%06X", prefix.c_str(), limbSuffix.c_str(), dlistOffset); + dlist->SetName(dListStr); + dlist->DeclareVar(prefix, ""); + parent->AddResource(dlist); +} diff --git a/ZAPDTR/ZAPD/ZLimb.h b/ZAPDTR/ZAPD/ZLimb.h new file mode 100644 index 000000000..2da934103 --- /dev/null +++ b/ZAPDTR/ZAPD/ZLimb.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +#include "OtherStructs/SkinLimbStructs.h" +#include "ZDisplayList.h" +#include "ZFile.h" + +enum class ZLimbType +{ + Invalid, + Standard, + LOD, + Skin, + Curve, + Legacy, +}; + +class ZLimbTable; + +class ZLimb : public ZResource +{ +public: + std::string enumName; + ZLimbTable* limbsTable = nullptr; // borrowed pointer, do not delete! + + ZLimbType type = ZLimbType::Standard; + + ZLimbSkinType skinSegmentType = ZLimbSkinType::SkinType_Null; // Skin only + segptr_t skinSegment = 0; // Skin only + SkinAnimatedLimbData segmentStruct; // Skin only + + // Legacy only + float legTransX = 0, legTransY = 0, legTransZ = 0; // Vec3f + uint16_t rotX = 0, rotY = 0, rotZ = 0; // Vec3s + segptr_t childPtr = 0; // LegacyLimb* + segptr_t siblingPtr = 0; // LegacyLimb* + + segptr_t dListPtr = 0; + segptr_t dList2Ptr = 0; // LOD and Curve Only + + int16_t transX = 0, transY = 0, transZ = 0; + uint8_t childIndex = 0, siblingIndex = 0; + + uint8_t limbIndex = 0; + + ZLimb(ZFile* nParent); + + void ExtractFromBinary(uint32_t nRawDataIndex, ZLimbType nType); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + std::string GetDefaultName(const std::string& prefix) const override; + + size_t GetRawDataSize() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + ZLimbType GetLimbType(); + void SetLimbType(ZLimbType value); + static const char* GetSourceTypeName(ZLimbType limbType); + static ZLimbType GetTypeByAttributeName(const std::string& attrName); + + void SetLimbIndex(uint8_t nLimbIndex); + +protected: + void DeclareDList(segptr_t dListSegmentedPtr, const std::string& prefix, + const std::string& limbSuffix); +}; diff --git a/ZAPDTR/ZAPD/ZMtx.cpp b/ZAPDTR/ZAPD/ZMtx.cpp new file mode 100644 index 000000000..ffca3eba5 --- /dev/null +++ b/ZAPDTR/ZAPD/ZMtx.cpp @@ -0,0 +1,59 @@ +#include "ZMtx.h" + +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Mtx, ZMtx); + +ZMtx::ZMtx(ZFile* nParent) : ZResource(nParent) +{ + genOTRDef = true; +} + +void ZMtx::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + for (size_t i = 0; i < 4; ++i) + for (size_t j = 0; j < 4; ++j) + mtx[i][j] = BitConverter::ToInt32BE(rawData, rawDataIndex + (i * 4 + j) * 4); +} + +size_t ZMtx::GetRawDataSize() const +{ + return 64; +} + +std::string ZMtx::GetBodySourceCode() const +{ + std::string bodyStr = "\n"; + + for (const auto& row : mtx) + { + bodyStr += " "; + + for (int32_t val : row) + bodyStr += StringHelper::Sprintf("%-11i, ", val); + + bodyStr += "\n"; + } + + return bodyStr; +} + +std::string ZMtx::GetSourceTypeName() const +{ + return "Mtx"; +} + +ZResourceType ZMtx::GetResourceType() const +{ + return ZResourceType::Mtx; +} + +DeclarationAlignment ZMtx::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align8; +} diff --git a/ZAPDTR/ZAPD/ZMtx.h b/ZAPDTR/ZAPD/ZMtx.h new file mode 100644 index 000000000..1b5513cd4 --- /dev/null +++ b/ZAPDTR/ZAPD/ZMtx.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include "ZResource.h" + +class ZMtx : public ZResource +{ +public: + ZMtx(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; + +public: + std::array, 4> mtx; +}; diff --git a/ZAPDTR/ZAPD/ZPath.cpp b/ZAPDTR/ZAPD/ZPath.cpp new file mode 100644 index 000000000..8ea6d295b --- /dev/null +++ b/ZAPDTR/ZAPD/ZPath.cpp @@ -0,0 +1,218 @@ +#include "ZPath.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Path, ZPath); // Old name that is being kept for backwards compatability +REGISTER_ZFILENODE(PathList, ZPath); // New name that may be used in future XMLs + +ZPath::ZPath(ZFile* nParent) : ZResource(nParent) +{ + numPaths = 1; + RegisterOptionalAttribute("NumPaths", "1"); +} + +void ZPath::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + numPaths = StringHelper::StrToL(registeredAttributes.at("NumPaths").value); + + if (numPaths < 1) + { + HANDLE_ERROR_RESOURCE( + WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + StringHelper::Sprintf("invalid value '%d' found for 'NumPaths' attribute", numPaths), + "Should be at least '1'"); + } +} + +void ZPath::ParseRawData() +{ + ZResource::ParseRawData(); + + uint32_t currentPtr = rawDataIndex; + + pathways.reserve(numPaths); + for (size_t pathIndex = 0; pathIndex < numPaths; pathIndex++) + { + PathwayEntry path(parent); + path.ExtractFromFile(currentPtr); + + if (path.GetListAddress() == 0) + break; + + currentPtr += path.GetRawDataSize(); + pathways.push_back(path); + } +} + +void ZPath::DeclareReferences(const std::string& prefix) +{ + ZResource::DeclareReferences(prefix); + + for (auto& entry : pathways) + entry.DeclareReferences(prefix); +} + +Declaration* ZPath::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), name, pathways.size(), bodyStr); + decl->staticConf = staticConf; + return decl; +} + +std::string ZPath::GetBodySourceCode() const +{ + std::string declaration; + + size_t index = 0; + for (const auto& entry : pathways) + { + declaration += StringHelper::Sprintf("\t{ %s },", entry.GetBodySourceCode().c_str()); + + if (index < pathways.size() - 1) + declaration += "\n"; + + index++; + } + + return declaration; +} + +std::string ZPath::GetSourceTypeName() const +{ + return "Path"; +} + +ZResourceType ZPath::GetResourceType() const +{ + return ZResourceType::Path; +} + +size_t ZPath::GetRawDataSize() const +{ + return pathways.size() * pathways.at(0).GetRawDataSize(); +} + +void ZPath::SetNumPaths(uint32_t nNumPaths) +{ + numPaths = nNumPaths; +} + +/* PathwayEntry */ + +PathwayEntry::PathwayEntry(ZFile* nParent) : ZResource(nParent) +{ +} + +void PathwayEntry::ParseRawData() +{ + ZResource::ParseRawData(); + auto parentRawData = parent->GetRawData(); + numPoints = parentRawData.at(rawDataIndex + 0); + unk1 = parentRawData.at(rawDataIndex + 1); + unk2 = BitConverter::ToInt16BE(parentRawData, rawDataIndex + 2); + listSegmentAddress = BitConverter::ToInt32BE(parentRawData, rawDataIndex + 4); + + uint32_t currentPtr = GETSEGOFFSET(listSegmentAddress); + + points.reserve(numPoints); + for (int32_t i = 0; i < numPoints; i++) + { + ZVector vec(parent); + vec.ExtractFromBinary(currentPtr, ZScalarType::ZSCALAR_S16, 3); + + currentPtr += vec.GetRawDataSize(); + points.push_back(vec); + } +} + +void PathwayEntry::DeclareReferences(const std::string& prefix) +{ + ZResource::DeclareReferences(prefix); + if (points.empty()) + return; + + std::string pointsName; + bool addressFound = Globals::Instance->GetSegmentedPtrName(listSegmentAddress, parent, "Vec3s", + pointsName, false, parent->workerID); + if (addressFound) + return; + + std::string declaration = ""; + + size_t index = 0; + for (const auto& point : points) + { + declaration += StringHelper::Sprintf("\t{ %s },", point.GetBodySourceCode().c_str()); + + if (index < points.size() - 1) + declaration += "\n"; + + index++; + } + + uint32_t pointsOffset = Seg2Filespace(listSegmentAddress, parent->baseAddress); + Declaration* decl = parent->GetDeclaration(pointsOffset); + if (decl == nullptr) + { + pointsName = StringHelper::Sprintf("%sPathwayList_%06X", prefix.c_str(), pointsOffset); + parent->AddDeclarationArray(pointsOffset, points.at(0).GetDeclarationAlignment(), + points.size() * 6, points.at(0).GetSourceTypeName(), pointsName, + points.size(), declaration); + } + else + decl->declBody = declaration; +} + +std::string PathwayEntry::GetBodySourceCode() const +{ + std::string declaration; + std::string listName; + Globals::Instance->GetSegmentedPtrName(listSegmentAddress, parent, "Vec3s", listName, parent->workerID); + + if (Globals::Instance->game == ZGame::MM_RETAIL) + declaration += + StringHelper::Sprintf("%i, %i, %i, %s", numPoints, unk1, unk2, listName.c_str()); + else + { + if (numPoints > 0) + declaration += + StringHelper::Sprintf("ARRAY_COUNT(%s), %s", listName.c_str(), listName.c_str()); + else + declaration += StringHelper::Sprintf("%i, %s", numPoints, listName.c_str()); + } + + return declaration; +} + +std::string PathwayEntry::GetSourceTypeName() const +{ + return "Path"; +} + +ZResourceType PathwayEntry::GetResourceType() const +{ + return ZResourceType::Path; +} + +size_t PathwayEntry::GetRawDataSize() const +{ + return 0x08; +} + +segptr_t PathwayEntry::GetListAddress() const +{ + return listSegmentAddress; +} diff --git a/ZAPDTR/ZAPD/ZPath.h b/ZAPDTR/ZAPD/ZPath.h new file mode 100644 index 000000000..722e84afe --- /dev/null +++ b/ZAPDTR/ZAPD/ZPath.h @@ -0,0 +1,51 @@ +#pragma once + +#include "ZResource.h" +#include "ZVector.h" + +class PathwayEntry : public ZResource +{ +public: + PathwayEntry(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + segptr_t GetListAddress() const; + +public: + int32_t numPoints; + int8_t unk1; // (MM Only) + int16_t unk2; // (MM Only) + segptr_t listSegmentAddress; + std::vector points; +}; + +class ZPath : public ZResource +{ +public: + ZPath(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + void SetNumPaths(uint32_t nNumPaths); + +public: + uint32_t numPaths; + std::vector pathways; +}; diff --git a/ZAPDTR/ZAPD/ZPlayerAnimationData.cpp b/ZAPDTR/ZAPD/ZPlayerAnimationData.cpp new file mode 100644 index 000000000..d3bcb100c --- /dev/null +++ b/ZAPDTR/ZAPD/ZPlayerAnimationData.cpp @@ -0,0 +1,108 @@ +#include "ZPlayerAnimationData.h" + +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include + +REGISTER_ZFILENODE(PlayerAnimationData, ZPlayerAnimationData); + +ZPlayerAnimationData::ZPlayerAnimationData(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("FrameCount"); +} + +void ZPlayerAnimationData::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string& frameCountXml = registeredAttributes.at("FrameCount").value; + + frameCount = StringHelper::StrToL(frameCountXml); +} + +void ZPlayerAnimationData::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + + size_t totalSize = GetRawDataSize(); + // Divided by 2 because each value is an s16 + limbRotData.reserve(totalSize * frameCount / 2); + + for (size_t i = 0; i < totalSize; i += 2) + { + limbRotData.push_back(BitConverter::ToInt16BE(rawData, rawDataIndex + i)); + } +} + +Declaration* ZPlayerAnimationData::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (auxName == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), name, limbRotData.size(), bodyStr); + decl->staticConf = staticConf; + return decl; +} + +std::string ZPlayerAnimationData::GetBodySourceCode() const +{ + std::string declaration = ""; + + if (Globals::Instance->otrMode) + return ""; + + size_t index = 0; + for (const auto entry : limbRotData) + { + if (index % 8 == 0) + { + declaration += "\t"; + } + + if (entry < 0) + { + declaration += StringHelper::Sprintf("-0x%04X, ", -entry); + } + else + { + declaration += StringHelper::Sprintf("0x%04X, ", entry); + } + + if ((index + 1) % 8 == 0) + { + declaration += "\n"; + } + + index++; + } + + return declaration; +} + +std::string ZPlayerAnimationData::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sPlayerAnimationData_%06X", prefix.c_str(), rawDataIndex); +} + +std::string ZPlayerAnimationData::GetSourceTypeName() const +{ + return "s16"; +} + +ZResourceType ZPlayerAnimationData::GetResourceType() const +{ + return ZResourceType::PlayerAnimationData; +} + +size_t ZPlayerAnimationData::GetRawDataSize() const +{ + // (sizeof(Vec3s) * limbCount + 2) * frameCount + return (6 * 22 + 2) * frameCount; +} diff --git a/ZAPDTR/ZAPD/ZPlayerAnimationData.h b/ZAPDTR/ZAPD/ZPlayerAnimationData.h new file mode 100644 index 000000000..2923117ef --- /dev/null +++ b/ZAPDTR/ZAPD/ZPlayerAnimationData.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "ZResource.h" + +class ZPlayerAnimationData : public ZResource +{ +public: + int16_t frameCount = 0; + std::vector limbRotData; + + ZPlayerAnimationData(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + + std::string GetBodySourceCode() const override; + std::string GetDefaultName(const std::string& prefix) const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZPointer.cpp b/ZAPDTR/ZAPD/ZPointer.cpp new file mode 100644 index 000000000..61ad9ac1e --- /dev/null +++ b/ZAPDTR/ZAPD/ZPointer.cpp @@ -0,0 +1,57 @@ +#include "ZPointer.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Pointer, ZPointer); + +ZPointer::ZPointer(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("Type"); +} + +void ZPointer::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + type = registeredAttributes.at("Type").value; +} + +void ZPointer::ParseRawData() +{ + auto& rawData = parent->GetRawData(); + + ptr = BitConverter::ToUInt32BE(rawData, rawDataIndex); +} + +std::string ZPointer::GetBodySourceCode() const +{ + std::string ptrName; + + Globals::Instance->GetSegmentedPtrName(ptr, parent, "", ptrName, parent->workerID, false); + + return ptrName; +} + +bool ZPointer::DoesSupportArray() const +{ + return true; +} + +std::string ZPointer::GetSourceTypeName() const +{ + return type + "*"; +} + +ZResourceType ZPointer::GetResourceType() const +{ + return ZResourceType::Pointer; +} + +size_t ZPointer::GetRawDataSize() const +{ + return 0x04; +} diff --git a/ZAPDTR/ZAPD/ZPointer.h b/ZAPDTR/ZAPD/ZPointer.h new file mode 100644 index 000000000..8fb588933 --- /dev/null +++ b/ZAPDTR/ZAPD/ZPointer.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ZResource.h" + +class ZPointer : public ZResource +{ +public: + segptr_t ptr = SEGMENTED_NULL; + std::string type; + + ZPointer(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + std::string GetBodySourceCode() const override; + + bool DoesSupportArray() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZResource.cpp b/ZAPDTR/ZAPD/ZResource.cpp new file mode 100644 index 000000000..768a066a7 --- /dev/null +++ b/ZAPDTR/ZAPD/ZResource.cpp @@ -0,0 +1,450 @@ +#include "ZResource.h" + +#include +#include + +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" +#include +#include +#include +#include + +ZResource::ZResource(ZFile* nParent) +{ + // assert(nParent != nullptr); + parent = nParent; + name = ""; + outName = ""; + sourceOutput = ""; + rawDataIndex = 0; + outputDeclaration = true; + hash = 0; + + RegisterRequiredAttribute("Name"); + RegisterOptionalAttribute("OutName"); + RegisterOptionalAttribute("Offset"); + RegisterOptionalAttribute("Custom"); + RegisterOptionalAttribute("Static", "Global"); +} + +void ZResource::ExtractWithXML(tinyxml2::XMLElement* reader, offset_t nRawDataIndex) +{ + rawDataIndex = nRawDataIndex; + declaredInXml = true; + + if (reader != nullptr) + ParseXML(reader); + + // Don't parse raw data of external files + if (parent->GetMode() != ZFileMode::ExternalFile) + { + ParseRawData(); + CalcHash(); + } + + if (!isInner) + { + Declaration* decl = DeclareVar(parent->GetName(), ""); + if (decl != nullptr) + { + decl->declaredInXml = true; + decl->staticConf = staticConf; + } + } +} + +void ZResource::ExtractFromFile(offset_t nRawDataIndex) +{ + rawDataIndex = nRawDataIndex; + + // Don't parse raw data of external files + if (parent->GetMode() == ZFileMode::ExternalFile) + return; + + ParseRawData(); + CalcHash(); +} + +void ZResource::ParseXML(tinyxml2::XMLElement* reader) +{ + if (reader != nullptr) + { + // If it is an inner node, then 'Name' isn't required + if (isInner) + { + registeredAttributes.at("Name").isRequired = false; + } + + auto attrs = reader->FirstAttribute(); + while (attrs != nullptr) + { + std::string attrName = attrs->Name(); + bool attrDeclared = false; + + if (registeredAttributes.find(attrName) != registeredAttributes.end()) + { + registeredAttributes[attrName].value = attrs->Value(); + registeredAttributes[attrName].wasSet = true; + attrDeclared = true; + } + + if (!attrDeclared) + { + HANDLE_WARNING_RESOURCE( + WarningType::UnknownAttribute, parent, this, rawDataIndex, + StringHelper::Sprintf("unexpected '%s' attribute in resource <%s>", + attrName.c_str(), reader->Name()), + ""); + } + attrs = attrs->Next(); + } + + if (!Globals::Instance->otrMode) + { + if (!canHaveInner && !reader->NoChildren()) + { + std::string errorHeader = StringHelper::Sprintf( + "resource '%s' with inner element/child detected", reader->Name()); + HANDLE_ERROR_PROCESS(WarningType::InvalidXML, errorHeader, ""); + } + } + + for (const auto& attr : registeredAttributes) + { + if (attr.second.isRequired && attr.second.value == "") + { + std::string headerMsg = + StringHelper::Sprintf("missing required attribute '%s' in resource <%s>", + attr.first.c_str(), reader->Name()); + HANDLE_ERROR_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex, + headerMsg, ""); + } + } + + name = registeredAttributes.at("Name").value; + + // Disable this check for OTR file generation for now since it takes up a considerable amount of CPU time + if (!Globals::Instance->otrMode) + { + static std::regex r("[a-zA-Z_]+[a-zA-Z0-9_]*", + std::regex::icase | std::regex::optimize); + + if (!isInner || (isInner && name != "")) + { + if (!std::regex_match(name, r)) + { + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, + rawDataIndex, "invalid value found for 'Name' attribute", + ""); + } + } + } + + outName = registeredAttributes.at("OutName").value; + if (outName == "") + outName = name; + + isCustomAsset = registeredAttributes["Custom"].wasSet; + + std::string& staticXml = registeredAttributes["Static"].value; + if (staticXml == "Global") + { + staticConf = StaticConfig::Global; + } + else if (staticXml == "On") + { + staticConf = StaticConfig::On; + } + else if (staticXml == "Off") + { + staticConf = StaticConfig::Off; + } + else + { + HANDLE_ERROR_RESOURCE( + WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + StringHelper::Sprintf("invalid value '%s' for 'Static' attribute", staticConf), ""); + } + + declaredInXml = true; + } +} + +void ZResource::ParseRawData() +{ +} + +void ZResource::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ +} + +void ZResource::ParseRawDataLate() +{ +} + +void ZResource::DeclareReferencesLate([[maybe_unused]] const std::string& prefix) +{ +} + +Declaration* ZResource::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclaration(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), auxName, bodyStr); + decl->staticConf = staticConf; + return decl; +} + +void ZResource::Save([[maybe_unused]] const fs::path& outFolder) +{ +} + +const std::string& ZResource::GetName() const +{ + return name; +} + +const std::string& ZResource::GetOutName() const +{ + return outName; +} + +void ZResource::SetOutName(const std::string& nName) +{ + outName = nName; +} + +void ZResource::SetName(const std::string& nName) +{ + name = nName; +} + +bool ZResource::IsExternalResource() const +{ + return false; +} + +bool ZResource::DoesSupportArray() const +{ + return false; +} + +std::string ZResource::GetExternalExtension() const +{ + return ""; +} + +DeclarationAlignment ZResource::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align4; +} + +bool ZResource::WasDeclaredInXml() const +{ + return declaredInXml; +} + +StaticConfig ZResource::GetStaticConf() const +{ + return staticConf; +} + +offset_t ZResource::GetRawDataIndex() const +{ + return rawDataIndex; +} + +void ZResource::SetRawDataIndex(offset_t nRawDataIndex) +{ + rawDataIndex = nRawDataIndex; +} + +std::string ZResource::GetBodySourceCode() const +{ + return "ERROR"; +} + +std::string ZResource::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%s%s_%06X", prefix.c_str(), GetSourceTypeName().c_str(), + rawDataIndex); +} + +void ZResource::GetSourceOutputCode([[maybe_unused]] const std::string& prefix) +{ + std::string bodyStr = GetBodySourceCode(); + + if (bodyStr != "ERROR") + { + Declaration* decl = parent->GetDeclaration(rawDataIndex); + + if (decl == nullptr || decl->isPlaceholder) + decl = DeclareVar(prefix, bodyStr); + else + decl->declBody = bodyStr; + + // OTRTODO: This is a hack and we need something more elegant in the future... + if (GetResourceType() == ZResourceType::Array) + { + ZArray* arr = (ZArray*)this; + if (arr->resList[0]->GetResourceType() == ZResourceType::Vertex) + { + for (int i = 0; i < arr->resList.size(); i++) + { + ZVtx* vtx = (ZVtx*)arr->resList[i]; + decl->vertexHack.push_back(vtx); + + } + } + } + + if (decl != nullptr) + decl->staticConf = staticConf; + } +} + +std::string ZResource::GetSourceOutputHeader([[maybe_unused]] const std::string& prefix, std::set *nameSet) +{ + if (Globals::Instance->otrMode && genOTRDef) + { + std::string str = ""; + std::string nameStr = StringHelper::Strip(StringHelper::Strip(name, "\n"), "\r"); + + std::string outName = parent->GetOutName(); + std::string prefix = ""; + + if (GetResourceType() == ZResourceType::DisplayList || GetResourceType() == ZResourceType::Texture) + { + //ZDisplayList* dList = (ZDisplayList*)this; + + if (StringHelper::Contains(outName, "_room_")) + { + outName = StringHelper::Split(outName, "_room")[0] + "_scene"; + } + } + + std::string xmlPath = StringHelper::Replace(parent->GetXmlFilePath().string(), "\\", "/"); + + if (StringHelper::Contains(outName, "_room_") || + StringHelper::Contains(outName, "_scene") || + (StringHelper::Contains(parent->GetXmlFilePath().string(), "/scenes/") || + StringHelper::Contains(parent->GetXmlFilePath().string(), "\\scenes\\"))) { + prefix = "scenes/shared"; + + // Regex for xml paths that are dungeons with unique MQ variants (only the main dungeon, not boss rooms) + std::regex dungeonsWithMQ(R"(((ydan)|(ddan)|(bdan)|(Bmori1)|(HIDAN)|(MIZUsin)|(jyasinzou)|(HAKAdan)|(HAKAdanCH)|(ice_doukutu)|(men)|(ganontika))\.xml)"); + + if (StringHelper::Contains(xmlPath, "dungeons/") && std::regex_search(xmlPath, dungeonsWithMQ)) { + prefix = "scenes/nonmq"; + } + } + else if (StringHelper::Contains(xmlPath, "objects/")) + prefix = "objects"; + else if (StringHelper::Contains(xmlPath, "textures/")) + prefix = "textures"; + else if (StringHelper::Contains(xmlPath, "overlays/")) + prefix = "overlays"; + else if (StringHelper::Contains(xmlPath, "misc/")) + prefix = "misc"; + else if (StringHelper::Contains(xmlPath, "code/")) + prefix = "code"; + else if (StringHelper::Contains(xmlPath, "text/")) + prefix = "text"; + + if (prefix != "") { + str += StringHelper::Sprintf("#define d%s \"__OTR__%s/%s/%s\"", name.c_str(), prefix.c_str(), outName.c_str(), nameStr.c_str()); + } + else + str += StringHelper::Sprintf("#define d%s \"__OTR__%s/%s\"", name.c_str(), outName.c_str(), nameStr.c_str()); + + if (nameSet && nameSet->find(name) == nameSet->end()) { + str += StringHelper::Sprintf("\n"); + str += StringHelper::Sprintf(R"(static const ALIGN_ASSET(2) char %s[] = d%s;)", name.c_str(), name.c_str()); + + if (nameSet) { + nameSet->insert(name); + } + } + + if (name == "gTitleZeldaShieldLogoMQTex") + { + std::string addName = "gTitleZeldaShieldLogoTex"; + nameStr = StringHelper::Strip(StringHelper::Strip(addName, "\n"), "\r"); + str += StringHelper::Sprintf("\n\n#define d%s \"__OTR__%s/%s/%s\"", addName.c_str(), prefix.c_str(), outName.c_str(), nameStr.c_str()); + if (nameSet && nameSet->find(addName) == nameSet->end()) + { + str += StringHelper::Sprintf("\n"); + str += StringHelper::Sprintf(R"(static const ALIGN_ASSET(2) char %s[] = d%s;)", addName.c_str(), addName.c_str()); + + if (nameSet) + { + nameSet->insert(addName); + } + } + } + + return str; + } + else + return ""; +} + +ZResourceType ZResource::GetResourceType() const +{ + return ZResourceType::Error; +} + +void ZResource::CalcHash() +{ + hash = 0; +} + +void ZResource::SetInnerNode(bool inner) +{ + isInner = inner; +} + +void ZResource::RegisterRequiredAttribute(const std::string& attr) +{ + ResourceAttribute resAtrr; + resAtrr.key = attr; + resAtrr.isRequired = true; + registeredAttributes[attr] = resAtrr; +} + +void ZResource::RegisterOptionalAttribute(const std::string& attr, const std::string& defaultValue) +{ + ResourceAttribute resAtrr; + resAtrr.key = attr; + resAtrr.value = defaultValue; + registeredAttributes[attr] = resAtrr; +} + +offset_t Seg2Filespace(segptr_t segmentedAddress, uint32_t parentBaseAddress) +{ + offset_t currentPtr = GETSEGOFFSET(segmentedAddress); + + if (GETSEGNUM(segmentedAddress) == 0x80) // Is defined in code? + { + uint32_t parentBaseOffset = GETSEGOFFSET(parentBaseAddress); + if (parentBaseOffset > currentPtr) + { + HANDLE_ERROR(WarningType::Always, + StringHelper::Sprintf( + "resource address 0x%08X is smaller than 'BaseAddress' 0x%08X", + segmentedAddress, parentBaseAddress), + "Maybe your 'BaseAddress' is wrong?"); + } + + currentPtr -= parentBaseOffset; + } + + return currentPtr; +} diff --git a/ZAPDTR/ZAPD/ZResource.h b/ZAPDTR/ZAPD/ZResource.h new file mode 100644 index 000000000..33715739a --- /dev/null +++ b/ZAPDTR/ZAPD/ZResource.h @@ -0,0 +1,268 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "Declaration.h" +#include +#include +#include "tinyxml2.h" + +#define SEGMENT_SCENE 2 +#define SEGMENT_ROOM 3 +#define SEGMENT_KEEP 4 +#define SEGMENT_FIELDDANGEON_KEEP 5 +#define SEGMENT_OBJECT 6 +#define SEGMENT_LINKANIMETION 7 + +#define GETSEGOFFSET(x) (x & 0x00FFFFFF) +#define GETSEGNUM(x) ((x >> 24) & 0xFF) + +class ZFile; + +enum class ZResourceType +{ + Error, + Animation, + Array, + AltHeader, + Background, + Blob, + CollisionHeader, + Cutscene, + DisplayList, + Limb, + LimbTable, + Mtx, + Path, + PlayerAnimationData, + Room, + RoomCommand, + Scalar, + Scene, + Skeleton, + String, + Symbol, + Texture, + TextureAnimation, + TextureAnimationParams, + Vector, + Vertex, + Audio, + ActorList, + CollisionPoly, + Pointer, + SurfaceType, + Waterbox, + Text, + TextMM, + KeyFrameFlexLimb, + KeyFrameStandardLimb, + KeyFrameSkel, + KeyFrameAnimation, +}; + +class ResourceAttribute +{ +public: + std::string key; + std::string value; + bool isRequired = false; + bool wasSet = false; +}; + +class ZResource +{ +public: + ZFile* parent; + bool outputDeclaration = true; + uint32_t hash = 0; + bool genOTRDef = false; + + /** + * Constructor. + * Child classes should not declare any other constructor besides this one + */ + ZResource(ZFile* nParent); + virtual ~ZResource() = default; + + /// + /// Extracts/Parsees data from binary file using an XML to provide the needed metadata. + /// + /// XML Node we wish to parse from. + /// The offset within the binary file we are going to parse from as + /// indicated by the "Offset" parameter in the XML. + virtual void ExtractWithXML(tinyxml2::XMLElement* reader, offset_t nRawDataIndex); + + /// + /// Extracts/Parses the needed data straight from a binary without the use of an XML. + /// + /// The offset within the binary file we wish to parse from. + virtual void ExtractFromFile(offset_t nRawDataIndex); + + // Misc + /** + * Parses additional attributes of the XML node. + * Extra attritbutes have to be registered using `RegisterRequiredAttribute` or + * `RegisterOptionalAttribute` in the constructor of the ZResource + */ + virtual void ParseXML(tinyxml2::XMLElement* reader); + /** + * Extracts data from the binary file + */ + virtual void ParseRawData(); + /** + * Declares any data pointed by this resource that has not been declared already. + * For example, a Vtx referenced by a Dlist should be declared here if it wasn't + * declared previously by something else + */ + virtual void DeclareReferences(const std::string& prefix); + virtual void ParseRawDataLate(); + virtual void DeclareReferencesLate(const std::string& prefix); + + /** + * Adds this resource as a Declaration of its parent ZFile + */ + virtual Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr); + /** + * Returns the body of the variable of the extracted resource, without any side-effect + */ + [[nodiscard]] virtual std::string GetBodySourceCode() const; + /** + * Creates an automatically generated variable name for the current resource + */ + [[nodiscard]] virtual std::string GetDefaultName(const std::string& prefix) const; + + virtual void GetSourceOutputCode(const std::string& prefix); + virtual std::string GetSourceOutputHeader(const std::string& prefix, std::set *nameSet); + virtual void CalcHash(); + /** + * Exports the resource to binary format + */ + virtual void Save(const fs::path& outFolder); + + // Properties + /** + * Returns true if the resource will be externalized, and included back to the C file using + * `#include`s + */ + virtual bool IsExternalResource() const; + /** + * Can this type be wrapped in an node? + */ + virtual bool DoesSupportArray() const; + /** + * The type of the resource as a C struct + */ + [[nodiscard]] virtual std::string GetSourceTypeName() const = 0; + /** + * The type in the ZResource enum + */ + [[nodiscard]] virtual ZResourceType GetResourceType() const = 0; + /** + * The filename extension for assets extracted as standalone files + */ + [[nodiscard]] virtual std::string GetExternalExtension() const; + + // Getters/Setters + [[nodiscard]] const std::string& GetName() const; + void SetName(const std::string& nName); + [[nodiscard]] const std::string& GetOutName() const; + void SetOutName(const std::string& nName); + [[nodiscard]] offset_t GetRawDataIndex() const; + void SetRawDataIndex(offset_t nRawDataIndex); + + /** + * The size of the current struct being extracted, not counting data referenced by it + */ + [[nodiscard]] virtual size_t GetRawDataSize() const = 0; + /** + * The alignment of the extracted struct + */ + [[nodiscard]] virtual DeclarationAlignment GetDeclarationAlignment() const; + void SetInnerNode(bool inner); + /** + * Returns `true` if this ZResource was declared using an XML node, + * `false` otherwise (for example, a Vtx extracted indirectly by a DList) + */ + [[nodiscard]] bool WasDeclaredInXml() const; + [[nodiscard]] StaticConfig GetStaticConf() const; + +protected: + std::string name; + std::string outName; + offset_t rawDataIndex; + std::string sourceOutput; + + // Inner is used mostly for nodes + /** + * Is this resource an inner node of another resource? + * (namely inside an ) + */ + bool isInner = false; + /** + * Can this type have an inner node? + */ + bool canHaveInner = false; + + /** + * If set to true, create a reference for the asset in the file, but don't + * actually try to extract it from the file + */ + bool isCustomAsset; + bool declaredInXml = false; + StaticConfig staticConf = StaticConfig::Global; + + // Reading from this XMLs attributes should be performed in the overrided `ParseXML` method. + std::map registeredAttributes; + + // XML attributes registers. + // Registering XML attributes should be done in constructors. + + // The resource needs this attribute. If it is not provided, then the program will throw an + // exception. + void RegisterRequiredAttribute(const std::string& attr); + // Optional attribute. The resource has to do manual checks and manual warnings. It may or may + // not have a value. + void RegisterOptionalAttribute(const std::string& attr, const std::string& defaultValue = ""); +}; + +class ZResourceExporter +{ +public: + ZResourceExporter() = default; + virtual ~ZResourceExporter() = default; + + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) = 0; +}; + +offset_t Seg2Filespace(segptr_t segmentedAddress, uint32_t parentBaseAddress); + +typedef ZResource*(ZResourceFactoryFunc)(ZFile* nParent); + +#define REGISTER_ZFILENODE(nodeName, zResClass) \ + static ZResource* ZResourceFactory_##zResClass_##nodeName(ZFile* nParent) \ + { \ + return static_cast(new zResClass(nParent)); \ + } \ + \ + class ZRes_##nodeName \ + { \ + public: \ + ZRes_##nodeName() \ + { \ + ZFile::RegisterNode(#nodeName, &ZResourceFactory_##zResClass_##nodeName); \ + } \ + }; \ + static ZRes_##nodeName inst_ZRes_##nodeName + +#define REGISTER_EXPORTER(expFunc) \ + class ZResExp_##expFunc \ + { \ + public: \ + ZResExp_##expFunc() { expFunc(); } \ + }; \ + static ZResExp_##expFunc inst_ZResExp_##expFunc diff --git a/ZAPDTR/ZAPD/ZRom.cpp b/ZAPDTR/ZAPD/ZRom.cpp new file mode 100644 index 000000000..ae5343b85 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRom.cpp @@ -0,0 +1,290 @@ +#include "ZRom.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/Directory.h" +#include "yaz0/yaz0.h" + +#ifdef __linux__ +#include +#endif +#include + +namespace fs = std::filesystem; + +#define DMA_ENTRY_SIZE 16 + +#if defined(_MSC_VER) +#define __bswap_32 _byteswap_ulong +#define bswap_32 _byteswap_ulong +#endif +#if defined __APPLE__ +#define __bswap32 __builtin_bswap32 +#define bswap32 __builtin_bswap32 +#endif + +// ROM DMA Table Start +#define OOT_OFF_NTSC_10_RC 0x7430 +#define OOT_OFF_NTSC_10 0x7430 +#define OOT_OFF_NTSC_11 0x7430 +#define OOT_OFF_PAL_10 0x7950 +#define OOT_OFF_NTSC_12 0x7960 +#define OOT_OFF_PAL_11 0x7950 +#define OOT_OFF_JP_GC 0x7170 +#define OOT_OFF_JP_MQ 0x7170 +#define OOT_OFF_US_GC 0x7170 +#define OOT_OFF_US_MQ 0x7170 +#define OOT_OFF_PAL_GC_DBG1 0x12F70 +#define OOT_OFF_PAL_MQ_DBG 0x12F70 +#define OOT_OFF_PAL_GC_DBG2 0x12F70 +#define OOT_OFF_PAL_GC 0x7170 +#define OOT_OFF_PAL_MQ 0x7170 +#define OOT_OFF_JP_GC_CE 0x7170 +#define OOT_OFF_CN_IQUE 0xB7A0 +#define OOT_OFF_TW_IQUE 0xB240 + +#define MM_OFF_US_10 0x1A500 +#define MM_OFF_US_GC 0x1AE90 +#define MM_OFF_JP_GC 0x1AE90 +#define MM_OFF_JP_10 0x1C110 +#define MM_OFF_JP_11 0x1C050 +#define MM_OFF_DBG 0x24F60 + +#define OOT_NTSC_10 0xEC7011B7 +#define OOT_NTSC_11 0xD43DA81F +#define OOT_NTSC_12 0x693BA2AE +#define OOT_PAL_10 0xB044B569 +#define OOT_PAL_11 0xB2055FBD +#define OOT_NTSC_JP_GC_CE 0xF7F52DB8 +#define OOT_NTSC_JP_GC 0xF611F4BA +#define OOT_NTSC_US_GC 0xF3DD35BA +#define OOT_PAL_GC 0x09465AC3 +#define OOT_NTSC_JP_MQ 0xF43B45BA +#define OOT_NTSC_US_MQ 0xF034001A +#define OOT_PAL_MQ 0x1D4136F3 +#define OOT_PAL_GC_DBG1 0x871E1C92 // 03-21-2002 build +#define OOT_PAL_GC_DBG2 0x87121EFE // 03-13-2002 build +#define OOT_PAL_GC_MQ_DBG 0x917D18F6 +#define OOT_IQUE_TW 0x3D81FB3E +#define OOT_IQUE_CN 0xB1E1E07B +#define UNKNOWN 0xFFFFFFFF + +#define MM_NTSC_10 0x5354631C +#define MM_NTSC_10_UNCOMPRESSED 0xDA6983E7 +#define MM_NTSC_GC 0xB443EB08 +#define MM_NTSC_JP_GC 0x8473D0C1 + +bool ZRom::IsMQ() { + int crc = BitConverter::ToInt32BE(romData, 0x10); + switch (crc) { + case OOT_NTSC_10: + case OOT_NTSC_11: + case OOT_NTSC_12: + case OOT_PAL_10: + case OOT_PAL_11: + case OOT_NTSC_JP_GC: + case OOT_NTSC_JP_GC_CE: + case OOT_NTSC_US_GC: + case OOT_PAL_GC: + case OOT_PAL_GC_DBG1: + case OOT_PAL_GC_DBG2: + case OOT_IQUE_CN: + case OOT_IQUE_TW: + // MM - Always not MQ + case MM_NTSC_10: + case MM_NTSC_10_UNCOMPRESSED: + case MM_NTSC_GC: + case MM_NTSC_JP_GC: + default: + return false; + case OOT_NTSC_JP_MQ: + case OOT_NTSC_US_MQ: + case OOT_PAL_MQ: + case OOT_PAL_GC_MQ_DBG: + return true; + } +} + +ZRom::ZRom(std::string romPath) +{ + RomVersion version; + romData = DiskFile::ReadAllBytes(romPath); + + BitConverter::RomToBigEndian(romData.data(), romData.size()); + + version.crc = BitConverter::ToInt32BE(romData, 0x10); + + switch (version.crc) + { + case OOT_NTSC_10: + version.version = "N64 NTSC 1.0"; + version.listPath = "ntsc_oot.txt"; + version.offset = OOT_OFF_NTSC_10; + break; + case OOT_NTSC_11: + version.version = "N64 NTSC 1.1"; + version.listPath = "ntsc_oot.txt"; + version.offset = OOT_OFF_NTSC_11; + break; + case OOT_NTSC_12: + version.version = "N64 NTSC 1.2"; + version.listPath = "ntsc_12_oot.txt"; + version.offset = OOT_OFF_NTSC_12; + break; + case OOT_PAL_10: + version.version = "N64 PAL 1.0"; + version.listPath = "pal_oot.txt"; + version.offset = OOT_OFF_PAL_10; + break; + case OOT_PAL_11: + version.version = "N64 PAL 1.1"; + version.listPath = "pal_oot.txt"; + version.offset = OOT_OFF_PAL_11; + break; + case OOT_NTSC_JP_GC: + version.version = "JP GameCube (MQ Disk)"; + version.listPath = "gamecube.txt"; + version.offset = OOT_OFF_JP_GC; + break; + case OOT_NTSC_JP_GC_CE: + version.version = "GameCube (Collectors Edition Disk)"; + version.listPath = "gamecube.txt"; + version.offset = OOT_OFF_JP_GC_CE; + break; + case OOT_NTSC_JP_MQ: + version.version = "JP Master Quest"; + version.listPath = "gamecube.txt"; + version.offset = OOT_OFF_JP_MQ; + break; + case OOT_NTSC_US_MQ: + version.version = "NTSC Master Quest"; + version.listPath = "gamecube.txt"; + version.offset = OOT_OFF_JP_MQ; + break; + case OOT_NTSC_US_GC: + version.version = "NTSC GameCube"; + version.listPath = "gamecube.txt"; + version.offset = OOT_OFF_US_MQ; + break; + case OOT_PAL_GC: + version.version = "PAL GameCube"; + version.listPath = "gamecube_pal.txt"; + version.offset = OOT_OFF_PAL_GC; + break; + case OOT_PAL_MQ: + version.version = "PAL Master Quest"; + version.listPath = "gamecube_pal.txt"; + version.offset = OOT_OFF_PAL_MQ; + break; + case OOT_PAL_GC_DBG1: + version.version = "GameCube Debug 1.0"; + version.listPath = "dbg.txt"; + version.offset = OOT_OFF_PAL_GC_DBG1; + break; + case OOT_PAL_GC_DBG2: + version.version = "GameCube Debug 2.0"; + version.listPath = "dbg.txt"; + version.offset = OOT_OFF_PAL_GC_DBG2; + break; + case OOT_PAL_GC_MQ_DBG: + version.version = "GameCube MQ-Debug"; + version.listPath = "dbg.txt"; + version.offset = OOT_OFF_PAL_MQ_DBG; + break; + case OOT_IQUE_CN: + version.version = "OoT IQue"; + version.listPath = "ique.txt"; + version.offset = OOT_OFF_CN_IQUE; + break; + case OOT_IQUE_TW: + version.version = "TW IQue"; + version.listPath = "ique.txt"; + version.offset = OOT_OFF_TW_IQUE; + break; + case MM_NTSC_10: + version.version = "MM US 1.0"; + version.listPath = "mm.txt"; + version.offset = MM_OFF_US_10; + break; + case MM_NTSC_10_UNCOMPRESSED: + version.version = "MM US 1.0"; + version.listPath = "mm.txt"; + version.offset = MM_OFF_US_10; + break; + case MM_NTSC_GC: + version.version = "MM US GC"; + version.listPath = "mm_gc.txt"; + version.offset = MM_OFF_US_GC; + break; + case MM_NTSC_JP_GC: + version.version = "MM JP GC"; + version.listPath = "mm_gc_jp.txt"; + version.offset = MM_OFF_JP_GC; + break; + } + + auto path = StringHelper::Sprintf("%s/%s", Globals::Instance->fileListPath.string().c_str(), version.listPath.c_str()); + auto txt = DiskFile::ReadAllText(path); + std::vector lines = StringHelper::Split(txt, "\n"); + + std::vector decompressedData(1); + + for (unsigned int i = 0; i < lines.size(); i++) + { + lines[i] = StringHelper::Strip(lines[i], "\r"); + bool yarCompressed = false; + const int romOffset = version.offset + (DMA_ENTRY_SIZE * i); + + const int virtStart = BitConverter::ToInt32BE(romData, romOffset + 0); + const int virtEnd = BitConverter::ToInt32BE(romData, romOffset + 4); + const int physStart = BitConverter::ToInt32BE(romData, romOffset + 8); + const int physEnd = BitConverter::ToInt32BE(romData, romOffset + 12); + // File Deleted + if (physEnd == 0xFFFFFFFF && physStart == 0xFFFFFFFF) + { + // MM has some other checks that we might need to do + //if (virtEnd - virtStart == 0) + continue; + } + + const bool compressed = physEnd != 0; + int size = compressed ? physEnd - physStart : virtEnd - virtStart; + + auto outData = std::vector(); + outData.resize(size); + memcpy(outData.data(), romData.data() + physStart, size); + // An ifdef is used here because at this point the XMLs haven't been parsed, and we don't + // know if this is MM or OOT +#ifdef GAME_MM + if ((i >= 15 && i <= 20) || i == 22) + { + yarCompressed = true; + } +#endif + + if (compressed) + { + int decSize = virtEnd - virtStart; + decompressedData = std::vector(); + decompressedData.resize(decSize); + yaz0_decode(outData.data(), decompressedData.data(), decSize); + files[lines[i]] = decompressedData; + } + else if (yarCompressed) + { + //int decSize = virtEnd - virtStart; + decompressedData = std::vector(); + decompressedData.resize(1024*1024); //TODO FIX THIS PLEASE + yaz0_decodeYarArchive(outData.data(), decompressedData.data(), size); + files[lines[i]] = decompressedData; + } + else + files[lines[i]] = outData; + + //DiskFile::WriteAllBytes(StringHelper::Sprintf("baserom/%s", lines[i].c_str()), files[lines[i]]); + } +} + +std::vector ZRom::GetFile(std::string fileName) +{ + return files[fileName]; +} diff --git a/ZAPDTR/ZAPD/ZRom.h b/ZAPDTR/ZAPD/ZRom.h new file mode 100644 index 000000000..6225a1ada --- /dev/null +++ b/ZAPDTR/ZAPD/ZRom.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +class ZRom +{ +public: + ZRom(std::string romPath); + + std::vector GetFile(std::string fileName); + bool IsMQ(); + +protected: + std::vector romData; + std::map> files; +}; + +struct RomVersion +{ + std::string version = "None"; + std::string error = "None"; + std::string listPath = "None"; + int offset; + uint32_t crc; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.cpp new file mode 100644 index 000000000..3f64af168 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.cpp @@ -0,0 +1,20 @@ +#include "EndMarker.h" + +EndMarker::EndMarker(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +std::string EndMarker::GetBodySourceCode() const +{ + return "SCENE_CMD_END()"; +} + +std::string EndMarker::GetCommandCName() const +{ + return "SCmdEndMarker"; +} + +RoomCommand EndMarker::GetRoomCommand() const +{ + return RoomCommand::EndMarker; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.h b/ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.h new file mode 100644 index 000000000..daa477c94 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/EndMarker.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class EndMarker : public ZRoomCommand +{ +public: + EndMarker(ZFile* nParent); + + std::string GetBodySourceCode() const override; + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; \ No newline at end of file diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.cpp new file mode 100644 index 000000000..0e667c058 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.cpp @@ -0,0 +1,59 @@ +#include "SetActorList.h" + +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZRoom/ZRoom.h" + +SetActorList::SetActorList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetActorList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + numActors = cmdArg1; + + actorList = new ZActorList(parent); + actorList->ExtractFromBinary(segmentOffset, numActors); +} + +void SetActorList::DeclareReferences(const std::string& prefix) +{ + if (parent->HasDeclaration(segmentOffset)) + { + delete actorList; + actorList = static_cast(parent->FindResource(segmentOffset)); + assert(actorList != nullptr); + assert(actorList->GetResourceType() == ZResourceType::ActorList); + return; + } + + if (actorList->GetName() == "") + { + actorList->SetName(actorList->GetDefaultName(prefix)); + } + actorList->DeclareVar(prefix, ""); + parent->AddResource(actorList); +} + +std::string SetActorList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "ActorEntry", listName, + parent->workerID); + + return StringHelper::Sprintf("SCENE_CMD_ACTOR_LIST(%i, %s)", numActors, listName.c_str()); +} + +std::string SetActorList::GetCommandCName() const +{ + return "SCmdActorList"; +} + +RoomCommand SetActorList::GetRoomCommand() const +{ + return RoomCommand::SetActorList; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.h new file mode 100644 index 000000000..9122c15bb --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetActorList.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ZActorList.h" +#include "ZRoom/ZRoomCommand.h" + +class SetActorList : public ZRoomCommand +{ +public: + uint32_t numActors; + ZActorList* actorList = nullptr; + + SetActorList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; + +protected: + size_t GetActorListArraySize() const; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.cpp new file mode 100644 index 000000000..7418f2994 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.cpp @@ -0,0 +1,84 @@ +#include "SetAlternateHeaders.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +SetAlternateHeaders::SetAlternateHeaders(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetAlternateHeaders::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ + if (cmdArg2 != 0) + { + std::string varName = + StringHelper::Sprintf("%sAlternateHeaders0x%06X", prefix.c_str(), segmentOffset); + parent->AddDeclarationPlaceholder(segmentOffset, varName); + } +} + +void SetAlternateHeaders::ParseRawDataLate() +{ + size_t numHeaders = zRoom->parent->GetDeclarationSizeFromNeighbor(segmentOffset) / 4; + + headers.reserve(numHeaders); + for (uint32_t i = 0; i < numHeaders; i++) + { + int32_t address = BitConverter::ToInt32BE(parent->GetRawData(), segmentOffset + (i * 4)); + headers.push_back(address); + + if (address != 0 && parent->GetDeclaration(GETSEGOFFSET(address)) == nullptr) + { + ZRoom* altheader = new ZRoom(parent); + altheader->ExtractFromBinary(GETSEGOFFSET(address), zRoom->GetResourceType()); + altheader->DeclareReferences(parent->GetName()); + + parent->resources.push_back(altheader); + } + } +} + +void SetAlternateHeaders::DeclareReferencesLate(const std::string& prefix) +{ + if (!headers.empty()) + { + std::string declaration; + + for (size_t i = 0; i < headers.size(); i++) + { + std::string altHeaderName; + Globals::Instance->GetSegmentedPtrName(headers.at(i), parent, "", altHeaderName, + parent->workerID); + + declaration += StringHelper::Sprintf("\t%s,", altHeaderName.c_str()); + + if (i + 1 < headers.size()) + declaration += "\n"; + } + + std::string varName = + StringHelper::Sprintf("%sAlternateHeaders0x%06X", prefix.c_str(), segmentOffset); + parent->AddDeclarationArray(segmentOffset, GetDeclarationAlignment(), headers.size() * 4, + "SceneCmd*", varName, headers.size(), declaration); + } +} + +std::string SetAlternateHeaders::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "SceneCmd*", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ALTERNATE_HEADER_LIST(%s)", listName.c_str()); +} + +std::string SetAlternateHeaders::GetCommandCName() const +{ + return "SCmdAltHeaders"; +} + +RoomCommand SetAlternateHeaders::GetRoomCommand() const +{ + return RoomCommand::SetAlternateHeaders; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.h new file mode 100644 index 000000000..e66df936d --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetAlternateHeaders.h @@ -0,0 +1,23 @@ +#pragma once + +#include "ZRoom/ZRoom.h" +#include "ZRoom/ZRoomCommand.h" + +class SetAlternateHeaders : public ZRoomCommand +{ +public: + std::vector headers; + + SetAlternateHeaders(ZFile* nParent); + + void DeclareReferences(const std::string& prefix) override; + void ParseRawDataLate() override; + void DeclareReferencesLate(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; + +private: +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.cpp new file mode 100644 index 000000000..73f72136c --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.cpp @@ -0,0 +1,49 @@ +/** + * File: SetAnimatedMaterialList.cpp + * Description: Defines a class SetAnimatedMaterialList to enable ZRoom to declare + * ZTextureAnimations, using that ZResource to do the work. + */ +#include "SetAnimatedMaterialList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" +#include "ZTextureAnimation.h" + +SetAnimatedMaterialList::SetAnimatedMaterialList(ZFile* nParent) + : ZRoomCommand(nParent), textureAnimation(nParent) +{ +} + +void SetAnimatedMaterialList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + textureAnimation.ExtractFromFile(segmentOffset); +} + +void SetAnimatedMaterialList::DeclareReferences(const std::string& prefix) +{ + textureAnimation.SetName(textureAnimation.GetDefaultName(prefix.c_str())); + textureAnimation.DeclareReferences(prefix); + textureAnimation.GetSourceOutputCode(prefix); +} + +std::string SetAnimatedMaterialList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "AnimatedMaterial", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ANIMATED_MATERIAL_LIST(%s)", listName.c_str()); +} + +std::string SetAnimatedMaterialList::GetCommandCName() const +{ + return "SCmdTextureAnimations"; +} + +RoomCommand SetAnimatedMaterialList::GetRoomCommand() const +{ + return RoomCommand::SetAnimatedMaterialList; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.h new file mode 100644 index 000000000..896ee188c --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetAnimatedMaterialList.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" +#include "ZTextureAnimation.h" + +class SetAnimatedMaterialList : public ZRoomCommand +{ +public: + SetAnimatedMaterialList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; + + ZTextureAnimation textureAnimation; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.cpp new file mode 100644 index 000000000..e30e1b682 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.cpp @@ -0,0 +1,36 @@ +#include "SetCameraSettings.h" + +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "Globals.h" + +SetCameraSettings::SetCameraSettings(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetCameraSettings::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + cameraMovement = cmdArg1; + mapHighlight = BitConverter::ToUInt32BE(parent->GetRawData(), rawDataIndex + 4); +} + +std::string SetCameraSettings::GetBodySourceCode() const +{ + if (Globals::Instance->game == ZGame::MM_RETAIL) + return StringHelper::Sprintf("SCENE_CMD_SET_REGION_VISITED(0x%02X, 0x%08X)", cameraMovement, + mapHighlight); + else + return StringHelper::Sprintf("SCENE_CMD_MISC_SETTINGS(0x%02X, 0x%08X)", cameraMovement, + mapHighlight); +} + +std::string SetCameraSettings::GetCommandCName() const +{ + return "SCmdMiscSettings"; +} + +RoomCommand SetCameraSettings::GetRoomCommand() const +{ + return RoomCommand::SetCameraSettings; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.h new file mode 100644 index 000000000..a8fed44ec --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCameraSettings.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetCameraSettings : public ZRoomCommand +{ +public: + uint8_t cameraMovement; + uint32_t mapHighlight; + + SetCameraSettings(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; + +private: +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.cpp new file mode 100644 index 000000000..4aafb7c70 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.cpp @@ -0,0 +1,45 @@ +#include "SetCollisionHeader.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetCollisionHeader::SetCollisionHeader(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetCollisionHeader::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + + collisionHeader = new ZCollisionHeader(parent); + collisionHeader->SetName( + StringHelper::Sprintf("%sCollisionHeader_%06X", parent->GetName().c_str(), segmentOffset)); + collisionHeader->ExtractFromFile(segmentOffset); + parent->AddResource(collisionHeader); +} + +void SetCollisionHeader::DeclareReferences(const std::string& prefix) +{ + collisionHeader->DeclareVar(prefix, ""); +} + +std::string SetCollisionHeader::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "CollisionHeader", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_COL_HEADER(%s)", listName.c_str()); +} + +std::string SetCollisionHeader::GetCommandCName() const +{ + return "SCmdColHeader"; +} + +RoomCommand SetCollisionHeader::GetRoomCommand() const +{ + return RoomCommand::SetCollisionHeader; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.h new file mode 100644 index 000000000..f7d5ecf35 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCollisionHeader.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ZCollision.h" +#include "ZRoom/ZRoomCommand.h" + +class SetCollisionHeader : public ZRoomCommand +{ +public: + ZCollisionHeader* collisionHeader; + + SetCollisionHeader(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.cpp new file mode 100644 index 000000000..7e36f3e8c --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.cpp @@ -0,0 +1,154 @@ +#include "SetCsCamera.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetCsCamera::SetCsCamera(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetCsCamera::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + int numCameras = cmdArg1; + + uint32_t currentPtr = segmentOffset; + int32_t numPoints = 0; + + cameras.reserve(numCameras); + for (int32_t i = 0; i < numCameras; i++) + { + ActorCsCamInfo entry(parent->GetRawData(), currentPtr); + numPoints += entry.GetNumPoints(); + + currentPtr += entry.GetRawDataSize(); + cameras.push_back(entry); + } + + if (numPoints > 0) + { + uint32_t currentPtr = cameras.at(0).GetSegmentOffset(); + + points.reserve(numPoints); + for (int32_t i = 0; i < numPoints; i++) + { + ZVector vec(parent); + vec.ExtractFromBinary(currentPtr, ZScalarType::ZSCALAR_S16, 3); + + currentPtr += vec.GetRawDataSize(); + points.push_back(vec); + } + } +} + +void SetCsCamera::DeclareReferences(const std::string& prefix) +{ + if (points.size() > 0) + { + std::string declaration; + size_t index = 0; + for (auto& point : points) + { + declaration += StringHelper::Sprintf("\t{ %s },", point.GetBodySourceCode().c_str()); + + if (index < points.size() - 1) + declaration += "\n"; + + index++; + } + + uint32_t segOffset = cameras.at(0).GetSegmentOffset(); + + parent->AddDeclarationArray( + segOffset, DeclarationAlignment::Align4, points.size() * points.at(0).GetRawDataSize(), + points.at(0).GetSourceTypeName().c_str(), + StringHelper::Sprintf("%sCsCameraPoints_%06X", prefix.c_str(), segOffset), + points.size(), declaration); + } + + if (!cameras.empty()) + { + std::string camPointsName; + Globals::Instance->GetSegmentedPtrName(cameras.at(0).GetCamAddress(), parent, "Vec3s", + camPointsName, parent->workerID); + std::string declaration; + + size_t index = 0; + size_t pointsIndex = 0; + for (const auto& entry : cameras) + { + declaration += + StringHelper::Sprintf("\t{ %i, %i, &%s[%i] },", entry.type, entry.numPoints, + camPointsName.c_str(), pointsIndex); + + if (index < cameras.size() - 1) + declaration += "\n"; + + index++; + pointsIndex += entry.GetNumPoints(); + } + + const auto& entry = cameras.front(); + std::string camTypename = entry.GetSourceTypeName(); + + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, cameras.size() * entry.GetRawDataSize(), + camTypename, + StringHelper::Sprintf("%s%s_%06X", prefix.c_str(), camTypename.c_str(), segmentOffset), + cameras.size(), declaration); + } +} + +std::string SetCsCamera::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "ActorCsCamInfo", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ACTOR_CUTSCENE_CAM_LIST(%i, %s)", cameras.size(), + listName.c_str()); +} + +std::string SetCsCamera::GetCommandCName() const +{ + return "SCmdCsCameraList"; +} + +RoomCommand SetCsCamera::GetRoomCommand() const +{ + return RoomCommand::SetCsCamera; +} + +ActorCsCamInfo::ActorCsCamInfo(const std::vector& rawData, uint32_t rawDataIndex) + : baseOffset(rawDataIndex), type(BitConverter::ToInt16BE(rawData, rawDataIndex + 0)), + numPoints(BitConverter::ToInt16BE(rawData, rawDataIndex + 2)) +{ + camAddress = BitConverter::ToInt32BE(rawData, rawDataIndex + 4); + segmentOffset = GETSEGOFFSET(camAddress); +} + +std::string ActorCsCamInfo::GetSourceTypeName() const +{ + return "ActorCsCamInfo"; +} + +int32_t ActorCsCamInfo::GetRawDataSize() const +{ + return 8; +} + +int16_t ActorCsCamInfo::GetNumPoints() const +{ + return numPoints; +} + +segptr_t ActorCsCamInfo::GetCamAddress() const +{ + return camAddress; +} + +uint32_t ActorCsCamInfo::GetSegmentOffset() const +{ + return segmentOffset; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.h new file mode 100644 index 000000000..2ce64e4e0 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCsCamera.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" +#include "ZVector.h" + +class ActorCsCamInfo +{ +public: + ActorCsCamInfo(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetSourceTypeName() const; + int32_t GetRawDataSize() const; + + int16_t GetNumPoints() const; + segptr_t GetCamAddress() const; + uint32_t GetSegmentOffset() const; + + int baseOffset; + int16_t type; + int16_t numPoints; + segptr_t camAddress; + uint32_t segmentOffset; +}; + +class SetCsCamera : public ZRoomCommand +{ +public: + std::vector cameras; + std::vector points; + + SetCsCamera(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.cpp new file mode 100644 index 000000000..ac64a6456 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.cpp @@ -0,0 +1,103 @@ +#include "SetCutsceneEntryList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetActorCutsceneList::SetActorCutsceneList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetActorCutsceneList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + int numCutscenes = cmdArg1; + offset_t currentPtr = segmentOffset; + + cutscenes.reserve(numCutscenes); + for (int32_t i = 0; i < numCutscenes; i++) + { + CutsceneEntry entry(parent->GetRawData(), currentPtr); + cutscenes.push_back(entry); + + currentPtr += 16; + } +} + +void SetActorCutsceneList::DeclareReferences(const std::string& prefix) +{ + if (cutscenes.size() > 0) + { + std::string declaration; + + for (size_t i = 0; i < cutscenes.size(); i++) + { + const auto& entry = cutscenes.at(i); + declaration += StringHelper::Sprintf(" { %s },", entry.GetBodySourceCode().c_str()); + + if (i + 1 < cutscenes.size()) + { + declaration += "\n"; + } + } + + std::string typeName = cutscenes.at(0).GetSourceTypeName(); + + parent->AddDeclarationArray( + segmentOffset, GetDeclarationAlignment(), cutscenes.size() * 16, typeName, + StringHelper::Sprintf("%s%sList_%06X", prefix.c_str(), typeName.c_str(), segmentOffset), + cutscenes.size(), declaration); + } +} + +std::string SetActorCutsceneList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "CutsceneEntry", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ACTOR_CUTSCENE_LIST(%i, %s)", cutscenes.size(), + listName.c_str()); +} + +std::string SetActorCutsceneList::GetCommandCName() const +{ + return "SCmdCutsceneActorList"; +} + +RoomCommand SetActorCutsceneList::GetRoomCommand() const +{ + return RoomCommand::SetActorCutsceneList; +} + +CutsceneEntry::CutsceneEntry(const std::vector& rawData, uint32_t rawDataIndex) + : priority(BitConverter::ToInt16BE(rawData, rawDataIndex + 0)), + length(BitConverter::ToInt16BE(rawData, rawDataIndex + 2)), + csCamId(BitConverter::ToInt16BE(rawData, rawDataIndex + 4)), + scriptIndex(BitConverter::ToInt16BE(rawData, rawDataIndex + 6)), + additionalCsId(BitConverter::ToInt16BE(rawData, rawDataIndex + 8)), + endSfx(rawData[rawDataIndex + 0xA]), customValue(rawData[rawDataIndex + 0xB]), + hudVisibility(BitConverter::ToInt16BE(rawData, rawDataIndex + 0xC)), + endCam(rawData[rawDataIndex + 0xE]), letterboxSize(rawData[rawDataIndex + 0xF]) +{ +} + +std::string CutsceneEntry::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + + if (enumData->endSfx.find(endSfx) != enumData->endSfx.end()) + return StringHelper::Sprintf("%i, %i, %i, %i, %i, %s, %i, %i, %i, %i", priority, length, + csCamId, scriptIndex, additionalCsId, + enumData->endSfx[endSfx].c_str(), customValue, hudVisibility, + endCam, letterboxSize); + else + return StringHelper::Sprintf("%i, %i, %i, %i, %i, %i, %i, %i, %i, %i", priority, length, + csCamId, scriptIndex, additionalCsId, endSfx, customValue, + hudVisibility, endCam, letterboxSize); +} + +std::string CutsceneEntry::GetSourceTypeName() const +{ + return "CutsceneEntry"; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.h new file mode 100644 index 000000000..df1e751e4 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutsceneEntryList.h @@ -0,0 +1,42 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class CutsceneEntry +{ +public: + int16_t priority; + int16_t length; + int16_t csCamId; + int16_t scriptIndex; + int16_t additionalCsId; + uint8_t endSfx; + uint8_t customValue; + int16_t hudVisibility; + uint8_t endCam; + uint8_t letterboxSize; + + + CutsceneEntry(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetBodySourceCode() const; + std::string GetSourceTypeName() const; +}; + +class SetActorCutsceneList : public ZRoomCommand +{ +public: + std::vector cutscenes; + + SetActorCutsceneList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; + +private: +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.cpp new file mode 100644 index 000000000..e9192f0c1 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.cpp @@ -0,0 +1,150 @@ +#include "SetCutscenes.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetCutscenes::SetCutscenes(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetCutscenes::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + + numCutscenes = cmdArg1; + + if (Globals::Instance->game == ZGame::MM_RETAIL) + { + int32_t currentPtr = segmentOffset; + + cutsceneEntries.reserve(numCutscenes); + for (uint8_t i = 0; i < numCutscenes; i++) + { + CutsceneScriptEntry entry(parent->GetRawData(), currentPtr); + cutsceneEntries.push_back(entry); + currentPtr += 8; + } + } + else if (Globals::Instance->game == ZGame::OOT_RETAIL || Globals::Instance->game == ZGame::OOT_SW97) + { + ZCutscene* cutscene = new ZCutscene(parent); + cutscene->ExtractFromFile(segmentOffset); + + auto decl = parent->GetDeclaration(segmentOffset); + if (decl == nullptr) + { + cutscene->DeclareVar(zRoom->GetName().c_str(), ""); + } + + cutscenes.push_back(cutscene); + } +} + +void SetCutscenes::DeclareReferences(const std::string& prefix) +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + std::string varPrefix = name; + if (varPrefix == "") + varPrefix = prefix; + + if (Globals::Instance->game == ZGame::MM_RETAIL) + { + std::string declaration; + size_t i = 0; + + for (const auto& entry : cutsceneEntries) + { + if (entry.segmentPtr != SEGMENTED_NULL && + GETSEGNUM(entry.segmentPtr) == parent->segment) + { + offset_t csOffset = Seg2Filespace(entry.segmentPtr, parent->baseAddress); + if (!parent->HasDeclaration(csOffset)) + { + auto* cutscene = new ZCutscene(parent); + cutscene->ExtractFromFile(csOffset); + cutscene->SetName(cutscene->GetDefaultName(varPrefix)); + cutscene->DeclareVar(varPrefix, ""); + cutscene->DeclareReferences(varPrefix); + parent->AddResource(cutscene); + } + } + + std::string csName; + Globals::Instance->GetSegmentedPtrName(entry.segmentPtr, parent, "CutsceneData", + csName, parent->workerID); + + if (enumData->spawnFlag.find(entry.flag) != enumData->spawnFlag.end()) + declaration += StringHelper::Sprintf(" { %s, 0x%04X, 0x%02X, %s },", + csName.c_str(), entry.exit, entry.entrance, + enumData->spawnFlag[entry.flag].c_str()); + else + declaration += + StringHelper::Sprintf(" { %s, 0x%04X, 0x%02X, 0x%02X },", csName.c_str(), + entry.exit, entry.entrance, entry.flag); + if (i + 1 < numCutscenes) + declaration += "\n"; + + i++; + } + + parent->AddDeclarationArray(segmentOffset, DeclarationAlignment::Align4, + cutsceneEntries.size() * 8, "CutsceneScriptEntry", + StringHelper::Sprintf("%sCutsceneScriptEntryList_%06X", + zRoom->GetName().c_str(), segmentOffset), + cutsceneEntries.size(), declaration); + } + else + { + if (cmdArg2 != SEGMENTED_NULL && GETSEGNUM(cmdArg2) == parent->segment) + { + offset_t csOffset = Seg2Filespace(cmdArg2, parent->baseAddress); + if (!parent->HasDeclaration(csOffset)) + { + auto* cutscene = new ZCutscene(parent); + cutscene->ExtractFromFile(csOffset); + cutscene->SetName(cutscene->GetDefaultName(varPrefix)); + cutscene->DeclareVar(varPrefix, ""); + cutscene->DeclareReferences(varPrefix); + parent->AddResource(cutscene); + } + } + } +} + +std::string SetCutscenes::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "CutsceneData", listName, + parent->workerID); + + if (Globals::Instance->game == ZGame::MM_RETAIL) + { + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "CutsceneScriptEntry", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_CUTSCENE_SCRIPT_LIST(%i, %s)", numCutscenes, + listName.c_str()); + } + + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "CutsceneData", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_CUTSCENE_DATA(%s)", listName.c_str()); +} + +std::string SetCutscenes::GetCommandCName() const +{ + return "SCmdCutsceneData"; +} + +RoomCommand SetCutscenes::GetRoomCommand() const +{ + return RoomCommand::SetCutscenes; +} + +CutsceneScriptEntry::CutsceneScriptEntry(const std::vector& rawData, uint32_t rawDataIndex) + : segmentPtr(BitConverter::ToInt32BE(rawData, rawDataIndex + 0)), + exit(BitConverter::ToInt16BE(rawData, rawDataIndex + 4)), entrance(rawData[rawDataIndex + 6]), + flag(rawData[rawDataIndex + 7]) +{ +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.h new file mode 100644 index 000000000..69ef2367d --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetCutscenes.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ZCutscene.h" +#include "ZRoom/ZRoomCommand.h" + +class CutsceneScriptEntry +{ +public: + CutsceneScriptEntry(const std::vector& rawData, uint32_t rawDataIndex); + + segptr_t segmentPtr; + uint16_t exit; + uint8_t entrance; + uint8_t flag; +}; + +class SetCutscenes : public ZRoomCommand +{ +public: + std::vector cutscenes; + std::vector cutsceneEntries; // (MM Only) + uint8_t numCutscenes; // (MM Only) + + SetCutscenes(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.cpp new file mode 100644 index 000000000..79a278bc6 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.cpp @@ -0,0 +1,27 @@ +#include "SetEchoSettings.h" +#include "Utils/StringHelper.h" + +SetEchoSettings::SetEchoSettings(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetEchoSettings::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + echo = parent->GetRawData().at(rawDataIndex + 0x07); +} + +std::string SetEchoSettings::GetBodySourceCode() const +{ + return StringHelper::Sprintf("SCENE_CMD_ECHO_SETTINGS(%i)", echo); +} + +std::string SetEchoSettings::GetCommandCName() const +{ + return "SCmdEchoSettings"; +} + +RoomCommand SetEchoSettings::GetRoomCommand() const +{ + return RoomCommand::SetEchoSettings; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.h new file mode 100644 index 000000000..82b8c27db --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetEchoSettings.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetEchoSettings : public ZRoomCommand +{ +public: + uint8_t echo; + + SetEchoSettings(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.cpp new file mode 100644 index 000000000..c5d57df79 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.cpp @@ -0,0 +1,99 @@ +#include "SetEntranceList.h" + +#include "Globals.h" +#include "SetStartPositionList.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetEntranceList::SetEntranceList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetEntranceList::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ + if (segmentOffset != 0) + { + std::string varName = + StringHelper::Sprintf("%sEntranceList0x%06X", prefix.c_str(), segmentOffset); + parent->AddDeclarationPlaceholder(segmentOffset, varName); + } +} + +void SetEntranceList::ParseRawDataLate() +{ + // Parse Entrances and Generate Declaration + uint32_t numEntrances = zRoom->parent->GetDeclarationSizeFromNeighbor(segmentOffset) / 2; + uint32_t currentPtr = segmentOffset; + + entrances.reserve(numEntrances); + for (uint32_t i = 0; i < numEntrances; i++) + { + Spawn entry(parent->GetRawData(), currentPtr); + entrances.push_back(entry); + + currentPtr += 2; + } +} + +void SetEntranceList::DeclareReferencesLate([[maybe_unused]] const std::string& prefix) +{ + if (!entrances.empty()) + { + std::string declaration; + + size_t index = 0; + for (const auto& entry : entrances) + { + declaration += StringHelper::Sprintf(" { %s },", entry.GetBodySourceCode().c_str()); + if (index + 1 < entrances.size()) + declaration += "\n"; + + index++; + } + + std::string varName = + StringHelper::Sprintf("%sEntranceList0x%06X", prefix.c_str(), segmentOffset); + + if (Globals::Instance->game != ZGame::MM_RETAIL) + parent->AddDeclarationArray(segmentOffset, DeclarationAlignment::Align4, + entrances.size() * 2, "Spawn", varName, entrances.size(), + declaration); + else + parent->AddDeclarationArray(segmentOffset, DeclarationAlignment::Align4, + entrances.size() * 2, "EntranceEntry", varName, + entrances.size(), declaration); + } +} + +std::string SetEntranceList::GetBodySourceCode() const +{ + std::string listName; + if (Globals::Instance->game != ZGame::MM_RETAIL) + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "Spawn", listName, parent->workerID); + else + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "EntranceEntry", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ENTRANCE_LIST(%s)", listName.c_str()); +} + +std::string SetEntranceList::GetCommandCName() const +{ + return "SCmdEntranceList"; +} + +RoomCommand SetEntranceList::GetRoomCommand() const +{ + return RoomCommand::SetEntranceList; +} + +Spawn::Spawn(const std::vector& rawData, uint32_t rawDataIndex) +{ + startPositionIndex = rawData.at(rawDataIndex + 0); + roomToLoad = rawData.at(rawDataIndex + 1); +} + +std::string Spawn::GetBodySourceCode() const +{ + return StringHelper::Sprintf("0x%02X, 0x%02X", startPositionIndex, roomToLoad); +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.h new file mode 100644 index 000000000..832232e06 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetEntranceList.h @@ -0,0 +1,31 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class Spawn +{ +public: + uint8_t startPositionIndex; + uint8_t roomToLoad; + + Spawn(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetBodySourceCode() const; +}; + +class SetEntranceList : public ZRoomCommand +{ +public: + std::vector entrances; + + SetEntranceList(ZFile* nParent); + + void DeclareReferences(const std::string& prefix) override; + void ParseRawDataLate() override; + void DeclareReferencesLate(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.cpp new file mode 100644 index 000000000..3068d5ced --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.cpp @@ -0,0 +1,76 @@ +#include "SetExitList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZNames.h" +#include "ZRoom/ZRoom.h" + +SetExitList::SetExitList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetExitList::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ + if (segmentOffset != 0) + { + std::string varName = + StringHelper::Sprintf("%sExitList_%06X", prefix.c_str(), segmentOffset); + parent->AddDeclarationPlaceholder(segmentOffset, varName); + } +} + +void SetExitList::ParseRawDataLate() +{ + // Parse Entrances and Generate Declaration + uint32_t numEntrances = zRoom->parent->GetDeclarationSizeFromNeighbor(segmentOffset) / 2; + uint32_t currentPtr = segmentOffset; + + exits.reserve(numEntrances); + for (uint32_t i = 0; i < numEntrances; i++) + { + uint16_t exit = BitConverter::ToUInt16BE(parent->GetRawData(), currentPtr); + exits.push_back(exit); + + currentPtr += 2; + } +} + +void SetExitList::DeclareReferencesLate([[maybe_unused]] const std::string& prefix) +{ + if (!exits.empty()) + { + std::string declaration; + + for (size_t i = 0; i < exits.size(); i++) + { + declaration += + StringHelper::Sprintf(" %s,", ZNames::GetEntranceName(exits[i]).c_str()); + if (i + 1 < exits.size()) + declaration += "\n"; + } + + std::string varName = + StringHelper::Sprintf("%sExitList_%06X", prefix.c_str(), segmentOffset); + parent->AddDeclarationArray(segmentOffset, DeclarationAlignment::Align4, exits.size() * 2, + "u16", varName, exits.size(), declaration); + } +} + +std::string SetExitList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "u16", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_EXIT_LIST(%s)", listName.c_str()); +} + +std::string SetExitList::GetCommandCName() const +{ + return "SCmdExitList"; +} + +RoomCommand SetExitList::GetRoomCommand() const +{ + return RoomCommand::SetExitList; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.h new file mode 100644 index 000000000..9d2791a83 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetExitList.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetExitList : public ZRoomCommand +{ +public: + std::vector exits; + + SetExitList(ZFile* nParent); + + void DeclareReferences(const std::string& prefix) override; + void ParseRawDataLate() override; + void DeclareReferencesLate(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.cpp new file mode 100644 index 000000000..4c551be13 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.cpp @@ -0,0 +1,100 @@ +#include "SetLightList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +SetLightList::SetLightList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetLightList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + std::string declarations; + + numLights = cmdArg1; + int32_t currentPtr = segmentOffset; + + lights.reserve(this->numLights); + for (int i = 0; i < this->numLights; i++) + { + LightInfo light(parent->GetRawData(), currentPtr); + + currentPtr += light.GetRawDataSize(); + lights.push_back(light); + } +} + +void SetLightList::DeclareReferences(const std::string& prefix) +{ + if (!lights.empty()) + { + std::string declarations; + + for (size_t i = 0; i < lights.size(); i++) + { + declarations += + StringHelper::Sprintf("\t{ %s },", lights.at(i).GetBodySourceCode().c_str()); + + if (i < lights.size() - 1) + declarations += "\n"; + } + + const auto& light = lights.front(); + + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, lights.size() * light.GetRawDataSize(), + light.GetSourceTypeName(), + StringHelper::Sprintf("%sLightInfo0x%06X", prefix.c_str(), segmentOffset), + lights.size(), declarations); + } +} + +std::string SetLightList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "LightInfo", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_LIGHT_LIST(%i, %s)", numLights, listName.c_str()); +} + +std::string SetLightList::GetCommandCName() const +{ + return "SCmdLightList"; +} + +RoomCommand SetLightList::GetRoomCommand() const +{ + return RoomCommand::SetLightList; +} + +LightInfo::LightInfo(const std::vector& rawData, uint32_t rawDataIndex) +{ + type = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0); + x = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + y = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + z = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + r = BitConverter::ToUInt8BE(rawData, rawDataIndex + 8); + g = BitConverter::ToUInt8BE(rawData, rawDataIndex + 9); + b = BitConverter::ToUInt8BE(rawData, rawDataIndex + 10); + drawGlow = BitConverter::ToUInt8BE(rawData, rawDataIndex + 11); + radius = BitConverter::ToInt16BE(rawData, rawDataIndex + 12); +} + +std::string LightInfo::GetBodySourceCode() const +{ + return StringHelper::Sprintf( + "0x%02X, { %i, %i, %i, { 0x%02X, 0x%02X, 0x%02X }, 0x%02X, 0x%04X }", type, x, y, z, r, g, + b, drawGlow, radius); +} + +std::string LightInfo::GetSourceTypeName() const +{ + return "LightInfo"; +} + +size_t LightInfo::GetRawDataSize() const +{ + return 0x0E; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.h new file mode 100644 index 000000000..894a21530 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightList.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "ZFile.h" +#include "ZRoom/ZRoom.h" +#include "ZRoom/ZRoomCommand.h" + +class LightInfo +{ +public: + LightInfo(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetBodySourceCode() const; + + std::string GetSourceTypeName() const; + size_t GetRawDataSize() const; + +public: + uint8_t type; + int16_t x, y, z; + uint8_t r, g, b; + uint8_t drawGlow; + int16_t radius; +}; + +class SetLightList : public ZRoomCommand +{ +public: + uint8_t numLights; + std::vector lights; + + SetLightList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.cpp new file mode 100644 index 000000000..796e267eb --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.cpp @@ -0,0 +1,116 @@ +#include "SetLightingSettings.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetLightingSettings::SetLightingSettings(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetLightingSettings::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + uint8_t numLights = cmdArg1; + + settings.reserve(numLights); + for (int i = 0; i < numLights; i++) + settings.push_back(LightingSettings(parent->GetRawData(), segmentOffset + (i * 22))); +} + +void SetLightingSettings::DeclareReferences(const std::string& prefix) +{ + if (settings.size() > 0) + { + std::string declaration; + + for (size_t i = 0; i < settings.size(); i++) + { + declaration += + StringHelper::Sprintf("\t{ %s },", settings.at(i).GetBodySourceCode().c_str()); + if (i + 1 < settings.size()) + declaration += "\n"; + } + + if (Globals::Instance->game != ZGame::MM_RETAIL) + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, + settings.size() * settings.front().GetRawDataSize(), "EnvLightSettings", + StringHelper::Sprintf("%sLightSettings0x%06X", prefix.c_str(), segmentOffset), + settings.size(), declaration); + else + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, + settings.size() * settings.front().GetRawDataSize(), "LightSettings", + StringHelper::Sprintf("%sLightSettings0x%06X", prefix.c_str(), segmentOffset), + settings.size(), declaration); + } +} + +std::string SetLightingSettings::GetBodySourceCode() const +{ + std::string listName; + if (Globals::Instance->game != ZGame::MM_RETAIL) + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "EnvLightSettings", listName, parent->workerID); + else + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "LightSettings", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ENV_LIGHT_SETTINGS(%i, %s)", settings.size(), + listName.c_str()); +} + +std::string SetLightingSettings::GetCommandCName() const +{ + return "SCmdLightSettingList"; +} + +RoomCommand SetLightingSettings::GetRoomCommand() const +{ + return RoomCommand::SetLightingSettings; +} + +LightingSettings::LightingSettings(const std::vector& rawData, uint32_t rawDataIndex) +{ + ambientClrR = rawData.at(rawDataIndex + 0); + ambientClrG = rawData.at(rawDataIndex + 1); + ambientClrB = rawData.at(rawDataIndex + 2); + + diffuseClrA_R = rawData.at(rawDataIndex + 3); + diffuseClrA_G = rawData.at(rawDataIndex + 4); + diffuseClrA_B = rawData.at(rawDataIndex + 5); + + diffuseDirA_X = rawData.at(rawDataIndex + 6); + diffuseDirA_Y = rawData.at(rawDataIndex + 7); + diffuseDirA_Z = rawData.at(rawDataIndex + 8); + + diffuseClrB_R = rawData.at(rawDataIndex + 9); + diffuseClrB_G = rawData.at(rawDataIndex + 10); + diffuseClrB_B = rawData.at(rawDataIndex + 11); + + diffuseDirB_X = rawData.at(rawDataIndex + 12); + diffuseDirB_Y = rawData.at(rawDataIndex + 13); + diffuseDirB_Z = rawData.at(rawDataIndex + 14); + + fogClrR = rawData.at(rawDataIndex + 15); + fogClrG = rawData.at(rawDataIndex + 16); + fogClrB = rawData.at(rawDataIndex + 17); + + unk = BitConverter::ToInt16BE(rawData, rawDataIndex + 18); + drawDistance = BitConverter::ToInt16BE(rawData, rawDataIndex + 20); +} + +std::string LightingSettings::GetBodySourceCode() const +{ + return StringHelper::Sprintf( + "0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, " + "0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%04X, 0x%04X", + ambientClrR, ambientClrG, ambientClrB, diffuseClrA_R, diffuseClrA_G, diffuseClrA_B, + diffuseDirA_X, diffuseDirA_Y, diffuseDirA_Z, diffuseClrB_R, diffuseClrB_G, diffuseClrB_B, + diffuseDirB_X, diffuseDirB_Y, diffuseDirB_Z, fogClrR, fogClrG, fogClrB, unk, drawDistance); +} + +size_t LightingSettings::GetRawDataSize() const +{ + return 0x16; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.h new file mode 100644 index 000000000..29caf4aa6 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetLightingSettings.h @@ -0,0 +1,38 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class LightingSettings +{ +public: + uint8_t ambientClrR, ambientClrG, ambientClrB; + uint8_t diffuseClrA_R, diffuseClrA_G, diffuseClrA_B; + uint8_t diffuseDirA_X, diffuseDirA_Y, diffuseDirA_Z; + uint8_t diffuseClrB_R, diffuseClrB_G, diffuseClrB_B; + uint8_t diffuseDirB_X, diffuseDirB_Y, diffuseDirB_Z; + uint8_t fogClrR, fogClrG, fogClrB; + uint16_t unk; + uint16_t drawDistance; + + LightingSettings(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetBodySourceCode() const; + + size_t GetRawDataSize() const; +}; + +class SetLightingSettings : public ZRoomCommand +{ +public: + std::vector settings; + + SetLightingSettings(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.cpp new file mode 100644 index 000000000..2a397664e --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.cpp @@ -0,0 +1,617 @@ +#include "SetMesh.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZBackground.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +void GenDListDeclarations(ZRoom* zRoom, ZFile* parent, ZDisplayList* dList); + +SetMesh::SetMesh(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetMesh::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + auto& parentRawData = parent->GetRawData(); + meshHeaderType = parentRawData.at(segmentOffset); + + switch (meshHeaderType) + { + case 0: + polyType = std::make_shared(parent, segmentOffset, zRoom); + break; + + case 1: + polyType = std::make_shared(parent, segmentOffset, zRoom); + break; + + case 2: + polyType = std::make_shared(parent, segmentOffset, zRoom); + break; + + default: + HANDLE_ERROR(WarningType::InvalidExtractedData, + StringHelper::Sprintf("unknown meshHeaderType: %i", meshHeaderType), ""); + } + + polyType->ParseRawData(); +} + +void SetMesh::DeclareReferences(const std::string& prefix) +{ + polyType->SetName(polyType->GetDefaultName(prefix)); + polyType->DeclareReferences(prefix); + polyType->DeclareAndGenerateOutputCode(prefix); +} + +// TODO: is this really needed? +void GenDListDeclarations(ZRoom* zRoom, ZFile* parent, ZDisplayList* dList) +{ + if (dList == nullptr) + return; + + dList->DeclareReferences(zRoom->GetName()); + + for (ZDisplayList* otherDList : dList->otherDLists) + GenDListDeclarations(zRoom, parent, otherDList); +} + +std::string SetMesh::GenDListExterns(ZDisplayList* dList) +{ + std::string sourceOutput; + + sourceOutput += StringHelper::Sprintf("extern Gfx %sDL_%06X[];\n", zRoom->GetName().c_str(), + dList->GetRawDataIndex()); + + for (ZDisplayList* otherDList : dList->otherDLists) + sourceOutput += GenDListExterns(otherDList); + + return sourceOutput; +} + +std::string SetMesh::GetBodySourceCode() const +{ + std::string list; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "", list, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ROOM_SHAPE(%s)", list.c_str()); +} + +std::string SetMesh::GetCommandCName() const +{ + return "SCmdMesh"; +} + +RoomCommand SetMesh::GetRoomCommand() const +{ + return RoomCommand::SetMesh; +} + +RoomShapeDListsEntry::RoomShapeDListsEntry(ZFile* nParent) : ZResource(nParent) +{ +} + +void RoomShapeDListsEntry::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + switch (polyType) + { + case 2: + x = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + y = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + z = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + unk_06 = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + + opa = BitConverter::ToUInt32BE(rawData, rawDataIndex + 8); + xlu = BitConverter::ToUInt32BE(rawData, rawDataIndex + 12); + break; + + default: + opa = BitConverter::ToUInt32BE(rawData, rawDataIndex); + xlu = BitConverter::ToUInt32BE(rawData, rawDataIndex + 4); + break; + } +} + +void RoomShapeDListsEntry::DeclareReferences(const std::string& prefix) +{ + opaDList = MakeDlist(opa, prefix); + xluDList = MakeDlist(xlu, prefix); +} + +std::string RoomShapeDListsEntry::GetBodySourceCode() const +{ + std::string bodyStr; + std::string opaStr; + std::string xluStr; + Globals::Instance->GetSegmentedPtrName(opa, parent, "Gfx", opaStr, parent->workerID); + Globals::Instance->GetSegmentedPtrName(xlu, parent, "Gfx", xluStr, parent->workerID); + + if (polyType == 2) + { + bodyStr += StringHelper::Sprintf("{ %6i, %6i, %6i }, %6i, ", x, y, z, unk_06); + } + + bodyStr += StringHelper::Sprintf("%s, %s", opaStr.c_str(), xluStr.c_str()); + + return bodyStr; +} + +void RoomShapeDListsEntry::GetSourceOutputCode(const std::string& prefix) +{ + std::string bodyStr = StringHelper::Sprintf("\n\t%s\n", GetBodySourceCode().c_str()); + + Declaration* decl = parent->GetDeclaration(rawDataIndex); + + if (decl == nullptr) + DeclareVar(prefix, bodyStr); + else + decl->declBody = bodyStr; +} + +std::string RoomShapeDListsEntry::GetSourceTypeName() const +{ + switch (polyType) + { + case 2: + return "RoomShapeCullableEntry"; + + default: + return "RoomShapeDListsEntry"; + } +} + +ZResourceType RoomShapeDListsEntry::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +size_t RoomShapeDListsEntry::GetRawDataSize() const +{ + switch (polyType) + { + case 2: + return 0x10; + + default: + return 0x08; + } +} + +void RoomShapeDListsEntry::SetPolyType(uint8_t nPolyType) +{ + polyType = nPolyType; +} + +ZDisplayList* RoomShapeDListsEntry::MakeDlist(segptr_t ptr, + [[maybe_unused]] const std::string& prefix) +{ + if (ptr == 0) + { + return nullptr; + } + + uint32_t dlistAddress = Seg2Filespace(ptr, parent->baseAddress); + + int32_t dlistLength = ZDisplayList::GetDListLength( + parent->GetRawData(), dlistAddress, + Globals::Instance->game == ZGame::OOT_SW97 ? DListType::F3DEX : DListType::F3DZEX); + ZDisplayList* dlist = new ZDisplayList(parent); + parent->AddResource(dlist); + dlist->ExtractFromBinary(dlistAddress, dlistLength); + dlist->SetName(dlist->GetDefaultName(prefix)); + GenDListDeclarations(zRoom, parent, dlist); + + return dlist; +} + +/* RoomShapeImageMultiBgEntry */ + +RoomShapeImageMultiBgEntry::RoomShapeImageMultiBgEntry(ZFile* nParent) : ZResource(nParent) +{ +} + +RoomShapeImageMultiBgEntry::RoomShapeImageMultiBgEntry(bool nIsSubStruct, const std::string& prefix, + uint32_t nRawDataIndex, ZFile* nParent) + : RoomShapeImageMultiBgEntry(nParent) +{ + rawDataIndex = nRawDataIndex; + parent = nParent; + isSubStruct = nIsSubStruct; + + name = GetDefaultName(prefix); + + ParseRawData(); + sourceBackground = MakeBackground(source, prefix); +} + +void RoomShapeImageMultiBgEntry::ParseRawData() +{ + size_t pad = 0x00; + const auto& rawData = parent->GetRawData(); + if (!isSubStruct) + { + pad = 0x04; + + unk_00 = BitConverter::ToUInt16BE(rawData, rawDataIndex + 0x00); + id = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x02); + } + source = BitConverter::ToUInt32BE(rawData, rawDataIndex + pad + 0x00); + unk_0C = BitConverter::ToUInt32BE(rawData, rawDataIndex + pad + 0x04); + tlut = BitConverter::ToUInt32BE(rawData, rawDataIndex + pad + 0x08); + width = BitConverter::ToUInt16BE(rawData, rawDataIndex + pad + 0x0C); + height = BitConverter::ToUInt16BE(rawData, rawDataIndex + pad + 0x0E); + fmt = BitConverter::ToUInt8BE(rawData, rawDataIndex + pad + 0x10); + siz = BitConverter::ToUInt8BE(rawData, rawDataIndex + pad + 0x11); + mode0 = BitConverter::ToUInt16BE(rawData, rawDataIndex + pad + 0x12); + tlutCount = BitConverter::ToUInt16BE(rawData, rawDataIndex + pad + 0x14); +} + +ZBackground* RoomShapeImageMultiBgEntry::MakeBackground(segptr_t ptr, const std::string& prefix) +{ + if (ptr == 0) + return nullptr; + + uint32_t backAddress = Seg2Filespace(ptr, parent->baseAddress); + + ZBackground* background = new ZBackground(parent); + background->ExtractFromFile(backAddress); + + std::string defaultName = background->GetDefaultName(prefix); + background->SetName(defaultName); + background->SetOutName(defaultName); + + background->DeclareVar(prefix, ""); + parent->resources.push_back(background); + + return background; +} + +size_t RoomShapeImageMultiBgEntry::GetRawDataSize() const +{ + return 0x1C; +} + +std::string RoomShapeImageMultiBgEntry::GetBodySourceCode() const +{ + std::string bodyStr = " "; + if (!isSubStruct) + { + bodyStr += "{ \n "; + } + + if (!isSubStruct) + { + bodyStr += StringHelper::Sprintf("0x%04X, ", unk_00); + bodyStr += StringHelper::Sprintf("%i, ", id); + bodyStr += "\n "; + bodyStr += " "; + } + + std::string backgroundName; + Globals::Instance->GetSegmentedPtrName(source, parent, "", backgroundName, parent->workerID); + bodyStr += StringHelper::Sprintf("%s, ", backgroundName.c_str()); + bodyStr += "\n "; + if (!isSubStruct) + { + bodyStr += " "; + } + + bodyStr += StringHelper::Sprintf("0x%08X, ", unk_0C); + bodyStr += StringHelper::Sprintf("0x%08X, ", tlut); + bodyStr += "\n "; + if (!isSubStruct) + { + bodyStr += " "; + } + + bodyStr += StringHelper::Sprintf("%i, ", width); + bodyStr += StringHelper::Sprintf("%i, ", height); + bodyStr += "\n "; + if (!isSubStruct) + { + bodyStr += " "; + } + + bodyStr += StringHelper::Sprintf("%i, ", fmt); + bodyStr += StringHelper::Sprintf("%i, ", siz); + bodyStr += "\n "; + if (!isSubStruct) + { + bodyStr += " "; + } + + bodyStr += StringHelper::Sprintf("0x%04X, ", mode0); + bodyStr += StringHelper::Sprintf("0x%04X, ", tlutCount); + if (!isSubStruct) + { + bodyStr += " \n }, "; + } + else + { + bodyStr += "\n"; + } + + return bodyStr; +} + +std::string RoomShapeImageMultiBgEntry::GetSourceTypeName() const +{ + return "RoomShapeImageMultiBgEntry"; +} + +ZResourceType RoomShapeImageMultiBgEntry::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +/* PolygonType section */ + +PolygonTypeBase::PolygonTypeBase(ZFile* nParent, uint32_t nRawDataIndex, ZRoom* nRoom) + : ZResource(nParent), zRoom{nRoom} +{ + rawDataIndex = nRawDataIndex; + type = BitConverter::ToUInt8BE(parent->GetRawData(), rawDataIndex); +} + +void PolygonTypeBase::DeclareAndGenerateOutputCode(const std::string& prefix) +{ + std::string bodyStr = GetBodySourceCode(); + + Declaration* decl = parent->GetDeclaration(rawDataIndex); + if (decl == nullptr) + { + DeclareVar(prefix, bodyStr); + } + else + { + decl->declBody = bodyStr; + } +} + +std::string PolygonTypeBase::GetSourceTypeName() const +{ + switch (type) + { + case 2: + return "RoomShapeCullable"; + + case 1: + return "PolygonType1"; + + default: + return "RoomShapeNormal"; + } +} + +ZResourceType PolygonTypeBase::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +PolygonType1::PolygonType1(ZFile* nParent, uint32_t nRawDataIndex, ZRoom* nRoom) + : PolygonTypeBase(nParent, nRawDataIndex, nRoom), single(nParent) +{ +} + +void PolygonType1::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + format = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x01); + dlist = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x04); + + if (format == 2) + { + count = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x08); + list = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x0C); + } + + if (dlist != 0) + { + RoomShapeDListsEntry polyGfxList(parent); + polyGfxList.zRoom = zRoom; + polyGfxList.SetPolyType(type); + polyGfxList.ExtractFromFile(Seg2Filespace(dlist, parent->baseAddress)); + polyGfxList.DeclareReferences(zRoom->GetName()); + polyDLists.push_back(polyGfxList); + } +} + +void PolygonType1::DeclareReferences(const std::string& prefix) +{ + polyDLists.at(0).GetSourceOutputCode(prefix); + + uint32_t listAddress; + std::string bgImageArrayBody; + switch (format) + { + case 1: + single = RoomShapeImageMultiBgEntry(true, prefix, rawDataIndex + 0x08, parent); + break; + + case 2: + if (list != 0) + { + listAddress = Seg2Filespace(list, parent->baseAddress); + uint32_t auxPtr = listAddress; + + multiList.reserve(count); + for (size_t i = 0; i < count; ++i) + { + RoomShapeImageMultiBgEntry bg(false, prefix, auxPtr, parent); + multiList.push_back(bg); + auxPtr += bg.GetRawDataSize(); + bgImageArrayBody += bg.GetBodySourceCode(); + if (i + 1 < count) + { + bgImageArrayBody += "\n"; + } + } + + Declaration* decl = parent->GetDeclaration(listAddress); + if (decl == nullptr) + { + parent->AddDeclarationArray( + listAddress, DeclarationAlignment::Align4, + count * multiList.at(0).GetRawDataSize(), multiList.at(0).GetSourceTypeName(), + multiList.at(0).GetName().c_str(), count, bgImageArrayBody); + } + } + break; + + default: + HANDLE_ERROR(WarningType::InvalidExtractedData, + StringHelper::Sprintf("unknown format: %i", format), ""); + break; + } +} + +size_t PolygonType1::GetRawDataSize() const +{ + switch (format) + { + case 1: + return 0x20; + + case 2: + return 0x10; + } + return 0x20; +} + +std::string PolygonType1::GetBodySourceCode() const +{ + std::string bodyStr = "\n "; + + bodyStr += "{ "; + bodyStr += StringHelper::Sprintf("%i, %i, ", type, format); + + std::string dlistStr; + Globals::Instance->GetSegmentedPtrName(dlist, parent, "", dlistStr, parent->workerID); + + bodyStr += StringHelper::Sprintf("%s, ", dlistStr.c_str()); + bodyStr += "}, \n"; + + std::string listStr = "NULL"; + switch (format) + { + case 1: + bodyStr += single.GetBodySourceCode(); + break; + case 2: + Globals::Instance->GetSegmentedPtrName(list, parent, "RoomShapeImageMultiBgEntry", listStr, parent->workerID); + bodyStr += StringHelper::Sprintf(" %i, %s, \n", count, listStr.c_str()); + break; + + default: + break; + } + + return bodyStr; +} + +std::string PolygonType1::GetSourceTypeName() const +{ + switch (format) + { + case 1: + return "RoomShapeImageSingle"; + + case 2: + return "RoomShapeImageMulti"; + } + return "ERROR"; + // return "PolygonType1"; +} + +RoomShapeCullable::RoomShapeCullable(ZFile* nParent, uint32_t nRawDataIndex, ZRoom* nRoom) + : PolygonTypeBase(nParent, nRawDataIndex, nRoom) +{ +} + +void RoomShapeCullable::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + num = BitConverter::ToUInt8BE(rawData, rawDataIndex + 0x01); + + start = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x04); + end = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0x08); + + uint32_t currentPtr = GETSEGOFFSET(start); + + polyDLists.reserve(num); + for (size_t i = 0; i < num; i++) + { + RoomShapeDListsEntry entry(parent); + entry.zRoom = zRoom; + entry.SetPolyType(type); + entry.ExtractFromFile(currentPtr); + entry.DeclareReferences(zRoom->GetName()); + polyDLists.push_back(entry); + currentPtr += entry.GetRawDataSize(); + } +} + +void RoomShapeCullable::DeclareReferences(const std::string& prefix) +{ + if (num > 0) + { + std::string declaration; + + for (size_t i = 0; i < polyDLists.size(); i++) + { + declaration += + StringHelper::Sprintf("\t{ %s },", polyDLists.at(i).GetBodySourceCode().c_str()); + if (i + 1 < polyDLists.size()) + declaration += "\n"; + } + + std::string polyDlistType = polyDLists.at(0).GetSourceTypeName(); + std::string polyDListName; + polyDListName = StringHelper::Sprintf("%s%s_%06X", prefix.c_str(), polyDlistType.c_str(), + GETSEGOFFSET(start)); + + Declaration* decl = parent->AddDeclarationArray( + GETSEGOFFSET(start), DeclarationAlignment::Align4, + polyDLists.size() * polyDLists.at(0).GetRawDataSize(), polyDlistType, polyDListName, + polyDLists.size(), declaration); + decl->forceArrayCnt = true; + } + + parent->AddDeclaration(GETSEGOFFSET(end), DeclarationAlignment::Align4, 4, "s32", + StringHelper::Sprintf("%s_terminatorMaybe_%06X", + parent->GetName().c_str(), GETSEGOFFSET(end)), + "0x01000000"); +} + +std::string RoomShapeCullable::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(start, parent, "", listName, 0, true); + + std::string body = StringHelper::Sprintf("\n %i, %i,\n", type, polyDLists.size()); + body += StringHelper::Sprintf(" %s,\n", listName.c_str()); + body += + StringHelper::Sprintf(" %s + ARRAY_COUNTU(%s)\n", listName.c_str(), listName.c_str()); + return body; +} + +size_t RoomShapeCullable::GetRawDataSize() const +{ + return 0x0C; +} + +DeclarationAlignment RoomShapeCullable::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align4; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.h new file mode 100644 index 000000000..c0f15da9d --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetMesh.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include "ZBackground.h" +#include "ZDisplayList.h" +#include "ZRoom/ZRoomCommand.h" + +class RoomShapeDListsEntry : public ZResource +{ +public: + ZRoom* zRoom; + + uint8_t polyType; + + int16_t x, y, z; // polyType == 2 + int16_t unk_06; // polyType == 2 + + segptr_t opa = 0; // Gfx* + segptr_t xlu = 0; // Gfx* + + ZDisplayList* opaDList = nullptr; // Gfx* + ZDisplayList* xluDList = nullptr; // Gfx* + + RoomShapeDListsEntry(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + void GetSourceOutputCode(const std::string& prefix) override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + + void SetPolyType(uint8_t nPolyType); + +protected: + ZDisplayList* MakeDlist(segptr_t ptr, const std::string& prefix); +}; + +class RoomShapeImageMultiBgEntry : public ZResource +{ +public: + uint16_t unk_00; + uint8_t id; + segptr_t source; + uint32_t unk_0C; + uint32_t tlut; + uint16_t width; + uint16_t height; + uint8_t fmt; + uint8_t siz; + uint16_t mode0; + uint16_t tlutCount; + + ZBackground* sourceBackground; + + bool isSubStruct; + + RoomShapeImageMultiBgEntry(ZFile* nParent); + RoomShapeImageMultiBgEntry(bool nIsSubStruct, const std::string& prefix, uint32_t nRawDataIndex, + ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +protected: + ZBackground* MakeBackground(segptr_t ptr, const std::string& prefix); +}; + +class PolygonTypeBase : public ZResource +{ +public: + uint8_t type; + std::vector polyDLists; + + PolygonTypeBase(ZFile* nParent, uint32_t nRawDataIndex, ZRoom* nRoom); + + void DeclareAndGenerateOutputCode(const std::string& prefix); + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + +protected: + ZRoom* zRoom; +}; + +class PolygonType1 : public PolygonTypeBase +{ +public: + uint8_t format; + segptr_t dlist; + + // single + RoomShapeImageMultiBgEntry single; + + // multi + uint8_t count; + segptr_t list; // RoomShapeImageMultiBgEntry* + std::vector multiList; + + PolygonType1(ZFile* nParent, uint32_t nRawDataIndex, ZRoom* nRoom); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + + size_t GetRawDataSize() const override; +}; + +class RoomShapeCullable : public PolygonTypeBase +{ +public: + uint8_t num; + segptr_t start; + segptr_t end; + + RoomShapeCullable(ZFile* nParent, uint32_t nRawDataIndex, ZRoom* nRoom); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; +}; + +class SetMesh : public ZRoomCommand +{ +public: + uint8_t data; + uint8_t meshHeaderType; + std::shared_ptr polyType; + + SetMesh(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; + +private: + std::string GenDListExterns(ZDisplayList* dList); +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.cpp new file mode 100644 index 000000000..25bfae703 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.cpp @@ -0,0 +1,83 @@ +#include "SetMinimapChests.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetMinimapChests::SetMinimapChests(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetMinimapChests::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + int numChests = cmdArg1; + + offset_t currentPtr = segmentOffset; + + chests.reserve(numChests); + for (int32_t i = 0; i < numChests; i++) + { + MinimapChest chest(parent->GetRawData(), currentPtr); + chests.push_back(chest); + + currentPtr += 10; + } +} + +void SetMinimapChests::DeclareReferences(const std::string& prefix) +{ + std::string declaration; + + size_t index = 0; + for (const auto& chest : chests) + { + declaration += StringHelper::Sprintf(" { %s },", chest.GetBodySourceCode().c_str()); + + if (index < chests.size() - 1) + declaration += "\n"; + + index++; + } + + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, chests.size() * 10, "MinimapChest", + StringHelper::Sprintf("%sMinimapChests0x%06X", prefix.c_str(), segmentOffset), + chests.size(), declaration); +} + +std::string SetMinimapChests::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "MinimapChest", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_MINIMAP_COMPASS_ICON_INFO(0x%02X, %s)", chests.size(), + listName.c_str()); +} + +std::string SetMinimapChests::GetCommandCName() const +{ + return "SCmdMinimapChests"; +} + +RoomCommand SetMinimapChests::GetRoomCommand() const +{ + return RoomCommand::SetMinimapChests; +} + +MinimapChest::MinimapChest(const std::vector& rawData, uint32_t rawDataIndex) + : unk0(BitConverter::ToUInt16BE(rawData, rawDataIndex + 0)), + unk2(BitConverter::ToUInt16BE(rawData, rawDataIndex + 2)), + unk4(BitConverter::ToUInt16BE(rawData, rawDataIndex + 4)), + unk6(BitConverter::ToUInt16BE(rawData, rawDataIndex + 6)), + unk8(BitConverter::ToUInt16BE(rawData, rawDataIndex + 8)) +{ +} + +std::string MinimapChest::GetBodySourceCode() const +{ + return StringHelper::Sprintf("0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X", unk0, unk2, unk4, unk6, + unk8); +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.h new file mode 100644 index 000000000..c2021453c --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapChests.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class MinimapChest +{ +public: + MinimapChest(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetBodySourceCode() const; + + uint16_t unk0; + uint16_t unk2; + uint16_t unk4; + uint16_t unk6; + uint16_t unk8; +}; + +class SetMinimapChests : public ZRoomCommand +{ +public: + std::vector chests; + + SetMinimapChests(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.cpp new file mode 100644 index 000000000..68f27717a --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.cpp @@ -0,0 +1,97 @@ +#include "SetMinimapList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetMinimapList::SetMinimapList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetMinimapList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + listSegmentAddr = BitConverter::ToInt32BE(parent->GetRawData(), segmentOffset); + listSegmentOffset = GETSEGOFFSET(listSegmentAddr); + scale = BitConverter::ToInt16BE(parent->GetRawData(), segmentOffset + 4); + + uint32_t currentPtr = listSegmentOffset; + + minimaps.reserve(zRoom->roomCount); + for (int32_t i = 0; i < zRoom->roomCount; i++) + { + MinimapEntry entry(parent->GetRawData(), currentPtr); + minimaps.push_back(entry); + + currentPtr += 10; + } +} + +void SetMinimapList::DeclareReferences(const std::string& prefix) +{ + { + std::string declaration; + + size_t index = 0; + for (const auto& entry : minimaps) + { + declaration += StringHelper::Sprintf(" { %s },", entry.GetBodySourceCode().c_str()); + + if (index < minimaps.size() - 1) + declaration += "\n"; + + index++; + } + + parent->AddDeclarationArray( + listSegmentOffset, DeclarationAlignment::Align4, minimaps.size() * 10, "MinimapEntry", + StringHelper::Sprintf("%sMinimapEntryList0x%06X", prefix.c_str(), listSegmentOffset), + minimaps.size(), declaration); + } + + { + std::string listName; + Globals::Instance->GetSegmentedPtrName(listSegmentAddr, parent, "MinimapEntry", listName, parent->workerID); + std::string declaration = StringHelper::Sprintf("\n\t%s, %d\n", listName.c_str(), scale); + + parent->AddDeclaration( + segmentOffset, DeclarationAlignment::Align4, 8, "MinimapList", + StringHelper::Sprintf("%sMinimapList0x%06X", prefix.c_str(), segmentOffset), + declaration); + } +} + +std::string SetMinimapList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "MinimapList", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_MINIMAP_INFO(%s)", listName.c_str()); +} + +std::string SetMinimapList::GetCommandCName() const +{ + return "SCmdMinimapSettings"; +} + +RoomCommand SetMinimapList::GetRoomCommand() const +{ + return RoomCommand::SetMinimapList; +} + +MinimapEntry::MinimapEntry(const std::vector& rawData, uint32_t rawDataIndex) + : unk0(BitConverter::ToUInt16BE(rawData, rawDataIndex + 0)), + unk2(BitConverter::ToUInt16BE(rawData, rawDataIndex + 2)), + unk4(BitConverter::ToUInt16BE(rawData, rawDataIndex + 4)), + unk6(BitConverter::ToUInt16BE(rawData, rawDataIndex + 6)), + unk8(BitConverter::ToUInt16BE(rawData, rawDataIndex + 8)) +{ +} + +std::string MinimapEntry::GetBodySourceCode() const +{ + return StringHelper::Sprintf("0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X", unk0, unk2, unk4, unk6, + unk8); +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.h new file mode 100644 index 000000000..68d2e4a14 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetMinimapList.h @@ -0,0 +1,37 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class MinimapEntry +{ +public: + MinimapEntry(const std::vector& rawData, uint32_t rawDataIndex); + + std::string GetBodySourceCode() const; + + uint16_t unk0; + uint16_t unk2; + uint16_t unk4; + uint16_t unk6; + uint16_t unk8; +}; + +class SetMinimapList : public ZRoomCommand +{ +public: + std::vector minimaps; + + SetMinimapList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; + + segptr_t listSegmentAddr; + uint32_t listSegmentOffset; + int16_t scale; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.cpp new file mode 100644 index 000000000..a8bb76a08 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.cpp @@ -0,0 +1,67 @@ +#include "SetObjectList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZNames.h" +#include "ZRoom/ZRoom.h" + +SetObjectList::SetObjectList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetObjectList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + uint8_t objectCnt = parent->GetRawData().at(rawDataIndex + 1); + uint32_t currentPtr = segmentOffset; + + objects.reserve(objectCnt); + for (uint8_t i = 0; i < objectCnt; i++) + { + uint16_t objectIndex = BitConverter::ToInt16BE(parent->GetRawData(), currentPtr); + objects.push_back(objectIndex); + currentPtr += 2; + } +} + +void SetObjectList::DeclareReferences(const std::string& prefix) +{ + if (!objects.empty()) + { + std::string declaration; + + for (size_t i = 0; i < objects.size(); i++) + { + uint16_t objectIndex = objects[i]; + declaration += + StringHelper::Sprintf(" %s,", ZNames::GetObjectName(objectIndex).c_str()); + + if (i < objects.size() - 1) + declaration += "\n"; + } + + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, objects.size() * 2, "s16", + StringHelper::Sprintf("%sObjectList_%06X", prefix.c_str(), segmentOffset), + objects.size(), declaration); + } +} + +std::string SetObjectList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "s16", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_OBJECT_LIST(%i, %s)", objects.size(), listName.c_str()); +} + +std::string SetObjectList::GetCommandCName() const +{ + return "SCmdObjectList"; +} + +RoomCommand SetObjectList::GetRoomCommand() const +{ + return RoomCommand::SetObjectList; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.h new file mode 100644 index 000000000..03c159483 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetObjectList.h @@ -0,0 +1,19 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetObjectList : public ZRoomCommand +{ +public: + std::vector objects; + + SetObjectList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.cpp new file mode 100644 index 000000000..429a369a8 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.cpp @@ -0,0 +1,65 @@ +#include "SetPathways.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetPathways::SetPathways(ZFile* nParent) : ZRoomCommand(nParent), pathwayList(nParent) +{ +} + +void SetPathways::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ + if (segmentOffset != 0) + { + std::string varName = + StringHelper::Sprintf("%sPathway_%06X", prefix.c_str(), segmentOffset); + parent->AddDeclarationPlaceholder(segmentOffset, varName); + } +} + +void SetPathways::ParseRawDataLate() +{ + if (Globals::Instance->game == ZGame::MM_RETAIL) + { + auto numPaths = zRoom->parent->GetDeclarationSizeFromNeighbor(segmentOffset) / 8; + pathwayList.SetNumPaths(numPaths); + } + + if (Globals::Instance->otrMode) + { + auto zPath = (ZPath*)parent->FindResource(segmentOffset); + + if (zPath != nullptr) + pathwayList = *zPath; + } + + pathwayList.ExtractFromFile(segmentOffset); +} + +void SetPathways::DeclareReferencesLate(const std::string& prefix) +{ + std::string varName = StringHelper::Sprintf("%sPathway_%06X", prefix.c_str(), segmentOffset); + pathwayList.SetName(varName); + pathwayList.DeclareReferences(prefix); + pathwayList.GetSourceOutputCode(prefix); +} + +std::string SetPathways::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "Path", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_PATH_LIST(%s)", listName.c_str()); +} + +std::string SetPathways::GetCommandCName() const +{ + return "SCmdPathList"; +} + +RoomCommand SetPathways::GetRoomCommand() const +{ + return RoomCommand::SetPathways; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.h new file mode 100644 index 000000000..b9d2a2fff --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetPathways.h @@ -0,0 +1,23 @@ +#pragma once + +#include "ZPath.h" +#include "ZResource.h" +#include "ZRoom/ZRoomCommand.h" + +class SetPathways : public ZRoomCommand +{ +public: + ZPath pathwayList; + + SetPathways(ZFile* nParent); + + void DeclareReferences(const std::string& prefix) override; + + void ParseRawDataLate() override; + void DeclareReferencesLate(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.cpp new file mode 100644 index 000000000..8c6f4bf51 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.cpp @@ -0,0 +1,50 @@ +#include "SetRoomBehavior.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +SetRoomBehavior::SetRoomBehavior(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetRoomBehavior::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + gameplayFlags = cmdArg1; + gameplayFlags2 = BitConverter::ToInt32BE(parent->GetRawData(), rawDataIndex + 0x04); + + currRoomUnk2 = gameplayFlags2 & 0xFF; + + currRoomUnk5 = showInvisActors = (gameplayFlags2 >> 8) & 1; + + msgCtxUnk = (gameplayFlags2 >> 10) & 1; + + enablePosLights = (gameplayFlags2 >> 11) & 1; + kankyoContextUnkE2 = (gameplayFlags2 >> 12) & 1; +} + +std::string SetRoomBehavior::GetBodySourceCode() const +{ + if (Globals::Instance->game == ZGame::MM_RETAIL) + { + std::string enableLights = StringHelper::BoolStr(enablePosLights); + return StringHelper::Sprintf("SCENE_CMD_ROOM_BEHAVIOR(0x%02X, 0x%02X, %i, %i, %s, %i)", + gameplayFlags, currRoomUnk2, currRoomUnk5, msgCtxUnk, + enableLights.c_str(), kankyoContextUnkE2); + } + std::string showInvisible = StringHelper::BoolStr(showInvisActors); + std::string disableWarps = StringHelper::BoolStr(msgCtxUnk); + return StringHelper::Sprintf("SCENE_CMD_ROOM_BEHAVIOR(0x%02X, 0x%02X, %s, %s)", gameplayFlags, + currRoomUnk2, showInvisible.c_str(), disableWarps.c_str()); +} + +std::string SetRoomBehavior::GetCommandCName() const +{ + return "SCmdRoomBehavior"; +} + +RoomCommand SetRoomBehavior::GetRoomCommand() const +{ + return RoomCommand::SetRoomBehavior; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.h new file mode 100644 index 000000000..47b772d77 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomBehavior.h @@ -0,0 +1,29 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetRoomBehavior : public ZRoomCommand +{ +public: + uint8_t gameplayFlags; + uint32_t gameplayFlags2; + + uint8_t currRoomUnk2; + + uint8_t showInvisActors; + uint8_t currRoomUnk5; + + uint8_t msgCtxUnk; + + uint8_t enablePosLights; + uint8_t kankyoContextUnkE2; + + SetRoomBehavior(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.cpp new file mode 100644 index 000000000..abb2b003c --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.cpp @@ -0,0 +1,155 @@ +#include "SetRoomList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZRoom.h" + +SetRoomList::SetRoomList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetRoomList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + int numRooms = cmdArg1; + + romfile = new RomFile(parent); + romfile->numRooms = numRooms; + romfile->ExtractFromFile(segmentOffset); + + parent->resources.push_back(romfile); + + zRoom->roomCount = numRooms; +} + +void SetRoomList::DeclareReferences(const std::string& prefix) +{ + ZRoomCommand::DeclareReferences(prefix); + + romfile->DeclareVar(prefix, ""); +} + +std::string SetRoomList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "RomFile", listName, parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_ROOM_LIST(%i, %s)", romfile->rooms.size(), + listName.c_str()); +} + +std::string SetRoomList::GetCommandCName() const +{ + return "SCmdRoomList"; +} + +RoomCommand SetRoomList::GetRoomCommand() const +{ + return RoomCommand::SetRoomList; +} + +RomFile::RomFile(ZFile* nParent) : ZResource(nParent) +{ +} + +void RomFile::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + if (reader->Attribute("NumRooms") != nullptr) + { + numRooms = StringHelper::StrToL(std::string(reader->Attribute("NumRooms"))); + } +} + +void RomFile::ParseRawData() +{ + ZResource::ParseRawData(); + + uint32_t currentPtr = rawDataIndex; + + rooms.reserve(numRooms); + for (int32_t i = 0; i < numRooms; i++) + { + RoomEntry entry(parent->GetRawData(), currentPtr); + rooms.push_back(entry); + + currentPtr += 8; + } +} + +Declaration* RomFile::DeclareVar(const std::string& prefix, const std::string& body) +{ + std::string auxName = name; + if (name == "") + auxName = StringHelper::Sprintf("%sRoomList0x%06X", prefix.c_str(), rawDataIndex); + + return parent->AddDeclarationArray(rawDataIndex, DeclarationAlignment::Align4, + rooms.size() * rooms.at(0).GetRawDataSize(), + GetSourceTypeName(), auxName, rooms.size(), body); +} + +std::string RomFile::GetBodySourceCode() const +{ + std::string declaration; + bool isFirst = true; + + for (ZFile* file : Globals::Instance->files) + { + for (ZResource* res : file->resources) + { + if (res->GetResourceType() == ZResourceType::Room) + { + std::string roomName = res->GetName(); + if (!isFirst) + declaration += "\n"; + + declaration += StringHelper::Sprintf( + "\t{ (uintptr_t)_%sSegmentRomStart, (uintptr_t)_%sSegmentRomEnd },", + roomName.c_str(), roomName.c_str()); + isFirst = false; + } + } + } + + return declaration; +} + +void RomFile::GetSourceOutputCode(const std::string& prefix) +{ + DeclareVar(prefix, GetBodySourceCode()); +} + +std::string RomFile::GetSourceTypeName() const +{ + return "RomFile"; +} + +ZResourceType RomFile::GetResourceType() const +{ + // TODO + return ZResourceType::Error; +} + +size_t RomFile::GetRawDataSize() const +{ + return 8 * rooms.size(); +} + +RoomEntry::RoomEntry(uint32_t nVAS, uint32_t nVAE) +{ + virtualAddressStart = nVAS; + virtualAddressEnd = nVAE; +} + +RoomEntry::RoomEntry(const std::vector& rawData, uint32_t rawDataIndex) + : RoomEntry(BitConverter::ToInt32BE(rawData, rawDataIndex + 0), + BitConverter::ToInt32BE(rawData, rawDataIndex + 4)) +{ +} + +size_t RoomEntry::GetRawDataSize() const +{ + return 0x08; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.h new file mode 100644 index 000000000..2ae48b68d --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetRoomList.h @@ -0,0 +1,53 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class RoomEntry +{ +public: + int32_t virtualAddressStart; + int32_t virtualAddressEnd; + + RoomEntry(uint32_t nVAS, uint32_t nVAE); + RoomEntry(const std::vector& rawData, uint32_t rawDataIndex); + + size_t GetRawDataSize() const; +}; + +class RomFile : public ZResource +{ +public: + RomFile(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& body) override; + std::string GetBodySourceCode() const override; + void GetSourceOutputCode(const std::string& prefix) override; + + std::string GetSourceTypeName() const override; + virtual ZResourceType GetResourceType() const override; + + virtual size_t GetRawDataSize() const override; + + uint8_t numRooms = 0; + std::vector rooms; +}; + +class SetRoomList : public ZRoomCommand +{ +public: + // Borrowed reference. Don't delete. + RomFile* romfile = nullptr; + + SetRoomList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.cpp new file mode 100644 index 000000000..b16f1355b --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.cpp @@ -0,0 +1,32 @@ +#include "SetSkyboxModifier.h" + +#include "Utils/StringHelper.h" + +SetSkyboxModifier::SetSkyboxModifier(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetSkyboxModifier::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + disableSky = parent->GetRawData().at(rawDataIndex + 0x04); + disableSunMoon = parent->GetRawData().at(rawDataIndex + 0x05); +} + +std::string SetSkyboxModifier::GetBodySourceCode() const +{ + std::string sky = StringHelper::BoolStr(disableSky); + std::string soonMoon = StringHelper::BoolStr(disableSunMoon); + return StringHelper::Sprintf("SCENE_CMD_SKYBOX_DISABLES(%s, %s)", sky.c_str(), + soonMoon.c_str()); +} + +std::string SetSkyboxModifier::GetCommandCName() const +{ + return "SCmdSkyboxDisables"; +} + +RoomCommand SetSkyboxModifier::GetRoomCommand() const +{ + return RoomCommand::SetSkyboxModifier; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.h new file mode 100644 index 000000000..65935bf92 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxModifier.h @@ -0,0 +1,19 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetSkyboxModifier : public ZRoomCommand +{ +public: + uint8_t disableSky; + uint8_t disableSunMoon; + + SetSkyboxModifier(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.cpp new file mode 100644 index 000000000..d5ae7ef76 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.cpp @@ -0,0 +1,36 @@ +#include "SetSkyboxSettings.h" +#include "Globals.h" +#include "Utils/StringHelper.h" + +SetSkyboxSettings::SetSkyboxSettings(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetSkyboxSettings::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + unk1 = cmdArg1; + skyboxNumber = parent->GetRawData().at(rawDataIndex + 0x04); + cloudsType = parent->GetRawData().at(rawDataIndex + 0x05); + isIndoors = parent->GetRawData().at(rawDataIndex + 0x06); +} + +std::string SetSkyboxSettings::GetBodySourceCode() const +{ + std::string indoors = StringHelper::BoolStr(isIndoors); + if (Globals::Instance->game == ZGame::MM_RETAIL) + return StringHelper::Sprintf("SCENE_CMD_SKYBOX_SETTINGS(0x%02X, %i, %i, %s)", unk1, + skyboxNumber, cloudsType, indoors.c_str()); + return StringHelper::Sprintf("SCENE_CMD_SKYBOX_SETTINGS(%i, %i, %s)", skyboxNumber, cloudsType, + indoors.c_str()); +} + +std::string SetSkyboxSettings::GetCommandCName() const +{ + return "SCmdSkyboxSettings"; +} + +RoomCommand SetSkyboxSettings::GetRoomCommand() const +{ + return RoomCommand::SetSkyboxSettings; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.h new file mode 100644 index 000000000..1c7e01ad4 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSkyboxSettings.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetSkyboxSettings : public ZRoomCommand +{ +public: + uint8_t unk1; // (MM Only) + uint8_t skyboxNumber; + uint8_t cloudsType; + uint8_t isIndoors; + + SetSkyboxSettings(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.cpp new file mode 100644 index 000000000..54b91328f --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.cpp @@ -0,0 +1,30 @@ +#include "SetSoundSettings.h" +#include "Utils/StringHelper.h" + +SetSoundSettings::SetSoundSettings(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetSoundSettings::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + reverb = cmdArg1; + nightTimeSFX = parent->GetRawData().at(rawDataIndex + 0x06); + musicSequence = parent->GetRawData().at(rawDataIndex + 0x07); +} + +std::string SetSoundSettings::GetBodySourceCode() const +{ + return StringHelper::Sprintf("SCENE_CMD_SOUND_SETTINGS(%i, %i, %i)", reverb, nightTimeSFX, + musicSequence); +} + +std::string SetSoundSettings::GetCommandCName() const +{ + return "SCmdSoundSettings"; +} + +RoomCommand SetSoundSettings::GetRoomCommand() const +{ + return RoomCommand::SetSoundSettings; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.h new file mode 100644 index 000000000..f378e5e4e --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSoundSettings.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetSoundSettings : public ZRoomCommand +{ +public: + uint8_t reverb; + uint8_t nightTimeSFX; + uint8_t musicSequence; + + SetSoundSettings(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.cpp new file mode 100644 index 000000000..34edf5ae6 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.cpp @@ -0,0 +1,40 @@ +#include "SetSpecialObjects.h" + +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZRoom/ZNames.h" + +SetSpecialObjects::SetSpecialObjects(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetSpecialObjects::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + elfMessage = cmdArg1; + globalObject = BitConverter::ToUInt16BE(parent->GetRawData(), rawDataIndex + 0x06); +} + +std::string SetSpecialObjects::GetBodySourceCode() const +{ + EnumData* enumData = &Globals::Instance->cfg.enumData; + std::string objectName = ZNames::GetObjectName(globalObject); + + if (enumData->naviQuestHintType.find(elfMessage) != enumData->naviQuestHintType.end()) + return StringHelper::Sprintf("SCENE_CMD_SPECIAL_FILES(%s, %s)", + enumData->naviQuestHintType[elfMessage].c_str(), + objectName.c_str()); + + return StringHelper::Sprintf("SCENE_CMD_SPECIAL_FILES(0x%02X, %s)", elfMessage, + objectName.c_str()); +} + +std::string SetSpecialObjects::GetCommandCName() const +{ + return "SCmdSpecialFiles"; +} + +RoomCommand SetSpecialObjects::GetRoomCommand() const +{ + return RoomCommand::SetSpecialObjects; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.h new file mode 100644 index 000000000..3327d44c3 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetSpecialObjects.h @@ -0,0 +1,19 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetSpecialObjects : public ZRoomCommand +{ +public: + uint8_t elfMessage; + uint16_t globalObject; + + SetSpecialObjects(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.cpp new file mode 100644 index 000000000..03856d23b --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.cpp @@ -0,0 +1,68 @@ +#include "SetStartPositionList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZNames.h" +#include "ZRoom/ZRoom.h" + +SetStartPositionList::SetStartPositionList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetStartPositionList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + uint8_t numActors = cmdArg1; + + offset_t currentPtr = segmentOffset; + + actors.reserve(numActors); + for (uint32_t i = 0; i < numActors; i++) + { + actors.push_back(ActorSpawnEntry(parent->GetRawData(), currentPtr)); + currentPtr += 16; + } +} + +void SetStartPositionList::DeclareReferences(const std::string& prefix) +{ + if (!actors.empty()) + { + std::string declaration; + + size_t index = 0; + for (const auto& entry : actors) + { + declaration += StringHelper::Sprintf(" { %s },", entry.GetBodySourceCode().c_str()); + if (index + 1 < actors.size()) + declaration += "\n"; + + index++; + } + + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, actors.size() * 16, "ActorEntry", + StringHelper::Sprintf("%sStartPositionList0x%06X", prefix.c_str(), segmentOffset), + actors.size(), declaration); + } +} + +std::string SetStartPositionList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "ActorEntry", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_SPAWN_LIST(%i, %s)", actors.size(), listName.c_str()); +} + +std::string SetStartPositionList::GetCommandCName() const +{ + return "SCmdSpawnList"; +} + +RoomCommand SetStartPositionList::GetRoomCommand() const +{ + return RoomCommand::SetStartPositionList; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.h new file mode 100644 index 000000000..10075adf3 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetStartPositionList.h @@ -0,0 +1,20 @@ +#pragma once + +#include "SetActorList.h" +#include "ZRoom/ZRoomCommand.h" + +class SetStartPositionList : public ZRoomCommand +{ +public: + std::vector actors; + + SetStartPositionList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.cpp new file mode 100644 index 000000000..9d4d356df --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.cpp @@ -0,0 +1,30 @@ +#include "SetTimeSettings.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +SetTimeSettings::SetTimeSettings(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetTimeSettings::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + hour = parent->GetRawData().at(rawDataIndex + 4); + min = parent->GetRawData().at(rawDataIndex + 5); + unk = parent->GetRawData().at(rawDataIndex + 6); +} + +std::string SetTimeSettings::GetBodySourceCode() const +{ + return StringHelper::Sprintf("SCENE_CMD_TIME_SETTINGS(%i, %i, %i)", hour, min, unk); +} + +std::string SetTimeSettings::GetCommandCName() const +{ + return "SCmdTimeSettings"; +} + +RoomCommand SetTimeSettings::GetRoomCommand() const +{ + return RoomCommand::SetTimeSettings; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.h new file mode 100644 index 000000000..7ff2711d4 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetTimeSettings.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetTimeSettings : public ZRoomCommand +{ +public: + uint8_t hour; + uint8_t min; + uint8_t unk; + + SetTimeSettings(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.cpp new file mode 100644 index 000000000..051ddd875 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.cpp @@ -0,0 +1,93 @@ +#include "SetTransitionActorList.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" +#include "ZRoom/ZNames.h" +#include "ZRoom/ZRoom.h" + +SetTransitionActorList::SetTransitionActorList(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetTransitionActorList::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + int numActors = cmdArg1; + uint32_t currentPtr = segmentOffset; + + transitionActors.reserve(numActors); + for (int32_t i = 0; i < numActors; i++) + { + TransitionActorEntry entry(parent->GetRawData(), currentPtr); + transitionActors.push_back(entry); + + currentPtr += 16; + } +} + +void SetTransitionActorList::DeclareReferences(const std::string& prefix) +{ + std::string declaration; + + size_t index = 0; + for (const auto& entry : transitionActors) + { + declaration += StringHelper::Sprintf(" { %s },", entry.GetBodySourceCode().c_str()); + if (index + 1 < transitionActors.size()) + { + declaration += "\n"; + } + + index++; + } + + parent->AddDeclarationArray( + segmentOffset, DeclarationAlignment::Align4, transitionActors.size() * 16, + "TransitionActorEntry", + StringHelper::Sprintf("%sTransitionActorList_%06X", prefix.c_str(), segmentOffset), + transitionActors.size(), declaration); +} + +std::string SetTransitionActorList::GetBodySourceCode() const +{ + std::string listName; + Globals::Instance->GetSegmentedPtrName(cmdArg2, parent, "TransitionActorEntry", listName, + parent->workerID); + return StringHelper::Sprintf("SCENE_CMD_TRANSITION_ACTOR_LIST(%i, %s)", transitionActors.size(), + listName.c_str()); +} + +std::string SetTransitionActorList::GetCommandCName() const +{ + return "SCmdTransiActorList"; +} + +RoomCommand SetTransitionActorList::GetRoomCommand() const +{ + return RoomCommand::SetTransitionActorList; +} + +TransitionActorEntry::TransitionActorEntry(const std::vector& rawData, int rawDataIndex) +{ + frontObjectRoom = rawData[rawDataIndex + 0]; + frontTransitionReaction = rawData[rawDataIndex + 1]; + backObjectRoom = rawData[rawDataIndex + 2]; + backTransitionReaction = rawData[rawDataIndex + 3]; + actorNum = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + posX = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + posY = BitConverter::ToInt16BE(rawData, rawDataIndex + 8); + posZ = BitConverter::ToInt16BE(rawData, rawDataIndex + 10); + rotY = BitConverter::ToInt16BE(rawData, rawDataIndex + 12); + initVar = BitConverter::ToInt16BE(rawData, rawDataIndex + 14); +} + +std::string TransitionActorEntry::GetBodySourceCode() const +{ + std::string actorStr = ZNames::GetActorName(actorNum); + + return StringHelper::Sprintf("%i, %i, %i, %i, %s, %i, %i, %i, %i, 0x%04X", frontObjectRoom, + frontTransitionReaction, backObjectRoom, backTransitionReaction, + actorStr.c_str(), posX, posY, posZ, rotY, initVar); +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.h new file mode 100644 index 000000000..a5ce2e66f --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetTransitionActorList.h @@ -0,0 +1,36 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class TransitionActorEntry +{ +public: + uint8_t frontObjectRoom; + uint8_t frontTransitionReaction; + uint8_t backObjectRoom; + uint8_t backTransitionReaction; + uint16_t actorNum; + int16_t posX, posY, posZ; + int16_t rotY; + uint16_t initVar; + + TransitionActorEntry(const std::vector& rawData, int rawDataIndex); + + std::string GetBodySourceCode() const; +}; + +class SetTransitionActorList : public ZRoomCommand +{ +public: + std::vector transitionActors; + + SetTransitionActorList(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetWind.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetWind.cpp new file mode 100644 index 000000000..1893b5601 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetWind.cpp @@ -0,0 +1,32 @@ +#include "SetWind.h" +#include "Utils/StringHelper.h" + +SetWind::SetWind(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +void SetWind::ParseRawData() +{ + ZRoomCommand::ParseRawData(); + auto& parentRawData = parent->GetRawData(); + windWest = parentRawData.at(rawDataIndex + 0x04); + windVertical = parentRawData.at(rawDataIndex + 0x05); + windSouth = parentRawData.at(rawDataIndex + 0x06); + clothFlappingStrength = parentRawData.at(rawDataIndex + 0x07); +} + +std::string SetWind::GetBodySourceCode() const +{ + return StringHelper::Sprintf("SCENE_CMD_WIND_SETTINGS(%i, %i, %i, %i)", windWest, windVertical, + windSouth, clothFlappingStrength); +} + +std::string SetWind::GetCommandCName() const +{ + return "SCmdWindSettings"; +} + +RoomCommand SetWind::GetRoomCommand() const +{ + return RoomCommand::SetWind; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetWind.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetWind.h new file mode 100644 index 000000000..05eaafeda --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetWind.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetWind : public ZRoomCommand +{ +public: + uint8_t windWest; + uint8_t windVertical; + uint8_t windSouth; + uint8_t clothFlappingStrength; + + SetWind(ZFile* nParent); + + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + std::string GetCommandCName() const override; + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.cpp new file mode 100644 index 000000000..fa28547e6 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.cpp @@ -0,0 +1,26 @@ +#include "SetWorldMapVisited.h" + +#include "Utils/StringHelper.h" +#include "Globals.h" + +SetWorldMapVisited::SetWorldMapVisited(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +std::string SetWorldMapVisited::GetBodySourceCode() const +{ + if (Globals::Instance->game == ZGame::MM_RETAIL) + return "SCENE_CMD_SET_REGION_VISITED()"; + else + return "SCENE_CMD_MISC_SETTINGS()"; +} + +std::string SetWorldMapVisited::GetCommandCName() const +{ + return "SCmdWorldMapVisited"; +} + +RoomCommand SetWorldMapVisited::GetRoomCommand() const +{ + return RoomCommand::SetWorldMapVisited; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.h b/ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.h new file mode 100644 index 000000000..a7ad93154 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/SetWorldMapVisited.h @@ -0,0 +1,14 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class SetWorldMapVisited : public ZRoomCommand +{ +public: + SetWorldMapVisited(ZFile* nParent); + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/Unused09.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/Unused09.cpp new file mode 100644 index 000000000..9dfcaa5c2 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/Unused09.cpp @@ -0,0 +1,21 @@ +#include "Unused09.h" +#include "Utils/StringHelper.h" + +Unused09::Unused09(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +std::string Unused09::GetBodySourceCode() const +{ + return "SCENE_CMD_UNK_09()"; +} + +std::string Unused09::GetCommandCName() const +{ + return "SceneCmd"; +} + +RoomCommand Unused09::GetRoomCommand() const +{ + return RoomCommand::Unused09; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/Unused09.h b/ZAPDTR/ZAPD/ZRoom/Commands/Unused09.h new file mode 100644 index 000000000..a38985eea --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/Unused09.h @@ -0,0 +1,14 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class Unused09 : public ZRoomCommand +{ +public: + Unused09(ZFile* nParent); + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.cpp new file mode 100644 index 000000000..b8ea13652 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.cpp @@ -0,0 +1,21 @@ +#include "Unused1D.h" +#include "Utils/StringHelper.h" + +Unused1D::Unused1D(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +std::string Unused1D::GetBodySourceCode() const +{ + return StringHelper::Sprintf("{ %s, 0x00, 0x00 }", GetCommandHex().c_str()); +} + +std::string Unused1D::GetCommandCName() const +{ + return "SceneCmd"; +} + +RoomCommand Unused1D::GetRoomCommand() const +{ + return RoomCommand::Unused1D; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.h b/ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.h new file mode 100644 index 000000000..dea0f98ee --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/Unused1D.h @@ -0,0 +1,14 @@ +#pragma once + +#include "ZRoom/ZRoomCommand.h" + +class Unused1D : public ZRoomCommand +{ +public: + Unused1D(ZFile* nParent); + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; + std::string GetCommandCName() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.cpp b/ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.cpp new file mode 100644 index 000000000..a892086c2 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.cpp @@ -0,0 +1,20 @@ +#include "ZRoomCommandUnk.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZRoom/ZRoom.h" + +ZRoomCommandUnk::ZRoomCommandUnk(ZFile* nParent) : ZRoomCommand(nParent) +{ +} + +std::string ZRoomCommandUnk::GetBodySourceCode() const +{ + return StringHelper::Sprintf("{ %s, 0x%02X, 0x%06X } /* WARNING: " + "UNIMPLEMENTED ROOM COMMAND */", + GetCommandHex().c_str(), cmdArg1, cmdArg2); +} + +RoomCommand ZRoomCommandUnk::GetRoomCommand() const +{ + return RoomCommand::Error; +} diff --git a/ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.h b/ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.h new file mode 100644 index 000000000..e381fe5a5 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/Commands/ZRoomCommandUnk.h @@ -0,0 +1,12 @@ +#pragma once +#include "ZRoom/ZRoomCommand.h" + +class ZRoomCommandUnk : public ZRoomCommand +{ +public: + ZRoomCommandUnk(ZFile* nParent); + + std::string GetBodySourceCode() const override; + + RoomCommand GetRoomCommand() const override; +}; diff --git a/ZAPDTR/ZAPD/ZRoom/ZNames.h b/ZAPDTR/ZAPD/ZRoom/ZNames.h new file mode 100644 index 000000000..83c217e99 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/ZNames.h @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include "Globals.h" +#include "Utils/StringHelper.h" + +class ZNames +{ +public: + static std::string GetObjectName(size_t id) + { + if (id >= Globals::Instance->cfg.objectList.size()) + return StringHelper::Sprintf("0x%04X", id); + return Globals::Instance->cfg.objectList[id]; + } + + static std::string GetActorName(uint16_t id) + { + switch (Globals::Instance->game) + { + case ZGame::OOT_RETAIL: + case ZGame::OOT_SW97: + if (id < ZNames::GetNumActors()) + return Globals::Instance->cfg.actorList[id]; + else + return StringHelper::Sprintf("0x%04X", id); + case ZGame::MM_RETAIL: + { + int32_t flags = id & 0xF000; + id &= 0xFFF; + std::string name; + if (id < ZNames::GetNumActors()) + name = Globals::Instance->cfg.actorList[id]; + else + name = StringHelper::Sprintf("0x%04X", id); + + if (flags == 0) + return name; + else + return StringHelper::Sprintf("%s | 0x%04X", name.c_str(), flags); + } + } + + return ""; + } + + static std::string GetEntranceName(uint16_t id) + { + if (ZNames::GetNumEntrances() == 0 || ZNames::GetNumSpecialEntrances() == 0) + return StringHelper::Sprintf("0x%04X", id); + + if (id < ZNames::GetNumEntrances()) + return Globals::Instance->cfg.entranceList[id]; + else if ((id >= 0x7FF9 && id <= 0x7FFF) && + !((id - 0x7FF9U) > GetNumSpecialEntrances())) // Special entrances + return Globals::Instance->cfg.specialEntranceList[id - 0x7FF9]; + else + return StringHelper::Sprintf("0x%04X", id); + } + + static size_t GetNumActors() { return Globals::Instance->cfg.actorList.size(); } + static size_t GetNumEntrances() { return Globals::Instance->cfg.entranceList.size(); } + static size_t GetNumSpecialEntrances() + { + return Globals::Instance->cfg.specialEntranceList.size(); + } +}; diff --git a/ZAPDTR/ZAPD/ZRoom/ZRoom.cpp b/ZAPDTR/ZAPD/ZRoom/ZRoom.cpp new file mode 100644 index 000000000..9b5f3101f --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/ZRoom.cpp @@ -0,0 +1,409 @@ +#include "ZRoom.h" +#include +#include +#include +#include +#include + +#include "Commands/EndMarker.h" +#include "Commands/SetCutsceneEntryList.h" +#include "Commands/SetActorList.h" +#include "Commands/SetAlternateHeaders.h" +#include "Commands/SetAnimatedMaterialList.h" +#include "Commands/SetCameraSettings.h" +#include "Commands/SetCollisionHeader.h" +#include "Commands/SetCsCamera.h" +#include "Commands/SetCutscenes.h" +#include "Commands/SetEchoSettings.h" +#include "Commands/SetEntranceList.h" +#include "Commands/SetExitList.h" +#include "Commands/SetLightList.h" +#include "Commands/SetLightingSettings.h" +#include "Commands/SetMesh.h" +#include "Commands/SetMinimapChests.h" +#include "Commands/SetMinimapList.h" +#include "Commands/SetObjectList.h" +#include "Commands/SetPathways.h" +#include "Commands/SetRoomBehavior.h" +#include "Commands/SetRoomList.h" +#include "Commands/SetSkyboxModifier.h" +#include "Commands/SetSkyboxSettings.h" +#include "Commands/SetSoundSettings.h" +#include "Commands/SetSpecialObjects.h" +#include "Commands/SetStartPositionList.h" +#include "Commands/SetTimeSettings.h" +#include "Commands/SetTransitionActorList.h" +#include "Commands/SetWind.h" +#include "Commands/SetWorldMapVisited.h" +#include "Commands/Unused09.h" +#include "Commands/Unused1D.h" +#include "Commands/ZRoomCommandUnk.h" +#include "Globals.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZBlob.h" +#include "ZCutscene.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Room, ZRoom); +REGISTER_ZFILENODE(Scene, ZRoom); +REGISTER_ZFILENODE(AltHeader, ZRoom); + +ZRoom::ZRoom(ZFile* nParent) : ZResource(nParent) +{ + roomCount = -1; + canHaveInner = true; + RegisterOptionalAttribute("HackMode"); +} + +ZRoom::~ZRoom() +{ + for (ZRoomCommand* cmd : commands) + delete cmd; +} + +void ZRoom::ExtractWithXML(tinyxml2::XMLElement* reader, uint32_t nRawDataIndex) +{ + ZResource::ExtractWithXML(reader, nRawDataIndex); + + if (hackMode == "syotes_room") + SyotesRoomFix(); + else + DeclareVar(name, ""); +} + +void ZRoom::ExtractFromBinary(uint32_t nRawDataIndex, ZResourceType parentType) +{ + rawDataIndex = nRawDataIndex; + name = GetDefaultName(parent->GetName()); + + zroomType = ZResourceType::AltHeader; + switch (parentType) + { + case ZResourceType::Scene: + case ZResourceType::Room: + case ZResourceType::AltHeader: + parentZroomType = parentType; + break; + + default: + // TODO: error message or something + assert(false); + break; + } + + ParseRawData(); + DeclareVar(name, ""); +} + +void ZRoom::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + // TODO: HACK: remove this specific check when the repo uses the correct HackMode="syotes_room" + if (name == "syotes_room_0") + { + hackMode = "syotes_room"; + } + + std::string nodeName = std::string(reader->Name()); + if (nodeName == "Scene") + { + zroomType = ZResourceType::Scene; + } + else if (nodeName == "Room") + zroomType = ZResourceType::Room; + else if (nodeName == "AltHeader") + zroomType = ZResourceType::AltHeader; + + if (reader->Attribute("HackMode") != nullptr) + { + hackMode = std::string(reader->Attribute("HackMode")); + if (hackMode != "syotes_room") + { + std::string headerError = StringHelper::Sprintf( + "invalid value found for 'HackMode' attribute: '%s'", hackMode.c_str()); + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + headerError, ""); + } + } +} + +void ZRoom::ParseRawData() +{ + if (hackMode == "syotes_room") + return; + + bool shouldContinue = true; + uint32_t currentIndex = 0; + uint32_t currentPtr = rawDataIndex; + + const auto& rawData = parent->GetRawData(); + while (shouldContinue) + { + RoomCommand opcode = static_cast(rawData.at(currentPtr)); + + ZRoomCommand* cmd = nullptr; + + auto start = std::chrono::steady_clock::now(); + + switch (opcode) + { + case RoomCommand::SetStartPositionList: + cmd = new SetStartPositionList(parent); + break; // 0x00 + case RoomCommand::SetActorList: + cmd = new SetActorList(parent); + break; // 0x01 + case RoomCommand::SetCsCamera: + cmd = new SetCsCamera(parent); + break; // 0x02 (MM-ONLY) + case RoomCommand::SetCollisionHeader: + cmd = new SetCollisionHeader(parent); + break; // 0x03 + case RoomCommand::SetRoomList: + cmd = new SetRoomList(parent); + break; // 0x04 + case RoomCommand::SetWind: + cmd = new SetWind(parent); + break; // 0x05 + case RoomCommand::SetEntranceList: + cmd = new SetEntranceList(parent); + break; // 0x06 + case RoomCommand::SetSpecialObjects: + cmd = new SetSpecialObjects(parent); + break; // 0x07 + case RoomCommand::SetRoomBehavior: + cmd = new SetRoomBehavior(parent); + break; // 0x08 + case RoomCommand::Unused09: + cmd = new Unused09(parent); + break; // 0x09 + case RoomCommand::SetMesh: + cmd = new SetMesh(parent); + break; // 0x0A + case RoomCommand::SetObjectList: + cmd = new SetObjectList(parent); + break; // 0x0B + case RoomCommand::SetLightList: + cmd = new SetLightList(parent); + break; // 0x0C (MM-ONLY) + case RoomCommand::SetPathways: + cmd = new SetPathways(parent); + break; // 0x0D + case RoomCommand::SetTransitionActorList: + cmd = new SetTransitionActorList(parent); + break; // 0x0E + case RoomCommand::SetLightingSettings: + cmd = new SetLightingSettings(parent); + break; // 0x0F + case RoomCommand::SetTimeSettings: + cmd = new SetTimeSettings(parent); + break; // 0x10 + case RoomCommand::SetSkyboxSettings: + cmd = new SetSkyboxSettings(parent); + break; // 0x11 + case RoomCommand::SetSkyboxModifier: + cmd = new SetSkyboxModifier(parent); + break; // 0x12 + case RoomCommand::SetExitList: + cmd = new SetExitList(parent); + break; // 0x13 + case RoomCommand::EndMarker: + cmd = new EndMarker(parent); + break; // 0x14 + case RoomCommand::SetSoundSettings: + cmd = new SetSoundSettings(parent); + break; // 0x15 + case RoomCommand::SetEchoSettings: + cmd = new SetEchoSettings(parent); + break; // 0x16 + case RoomCommand::SetCutscenes: + cmd = new SetCutscenes(parent); + break; // 0x17 + case RoomCommand::SetAlternateHeaders: + cmd = new SetAlternateHeaders(parent); + break; // 0x18 + case RoomCommand::SetCameraSettings: + if (Globals::Instance->game == ZGame::MM_RETAIL) + cmd = new SetWorldMapVisited(parent); + else + cmd = new SetCameraSettings(parent); + break; // 0x19 + case RoomCommand::SetAnimatedMaterialList: + cmd = new SetAnimatedMaterialList(parent); + break; // 0x1A (MM-ONLY) + case RoomCommand::SetActorCutsceneList: + cmd = new SetActorCutsceneList(parent); + break; // 0x1B (MM-ONLY) + case RoomCommand::SetMinimapList: + cmd = new SetMinimapList(parent); + break; // 0x1C (MM-ONLY) + case RoomCommand::Unused1D: + cmd = new Unused1D(parent); + break; // 0x1D + case RoomCommand::SetMinimapChests: + cmd = new SetMinimapChests(parent); + break; // 0x1E (MM-ONLY) + default: + cmd = new ZRoomCommandUnk(parent); + } + + cmd->commandSet = rawDataIndex; + cmd->ExtractCommandFromRoom(this, currentPtr); + + if (Globals::Instance->profile) + { + auto end = std::chrono::steady_clock::now(); + int64_t diff = + std::chrono::duration_cast(end - start).count(); + if (diff > 50) + printf("OP: %s, TIME: %" PRIi64 "ms\n", cmd->GetCommandCName().c_str(), diff); + } + + cmd->cmdIndex = currentIndex; + + commands.push_back(cmd); + + if (opcode == RoomCommand::EndMarker) + shouldContinue = false; + + currentPtr += 8; + currentIndex++; + } +} + +void ZRoom::DeclareReferences(const std::string& prefix) +{ + for (auto& cmd : commands) + cmd->DeclareReferences(prefix); +} + +void ZRoom::ParseRawDataLate() +{ + for (auto& cmd : commands) + cmd->ParseRawDataLate(); +} + +void ZRoom::DeclareReferencesLate(const std::string& prefix) +{ + for (auto& cmd : commands) + cmd->DeclareReferencesLate(prefix); +} + +Declaration* ZRoom::DeclareVar(const std::string& prefix, const std::string& body) +{ + std::string auxName = name; + if (auxName == "") + auxName = GetDefaultName(prefix); + if (zroomType == ZResourceType::Scene || zroomType == ZResourceType::Room) + auxName = StringHelper::Sprintf("%sCommands", name.c_str()); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), auxName, commands.size(), body); + decl->staticConf = staticConf; + return decl; +} + +std::string ZRoom::GetBodySourceCode() const +{ + std::string declaration; + + for (size_t i = 0; i < commands.size(); i++) + { + ZRoomCommand* cmd = commands[i]; + declaration += StringHelper::Sprintf("\t%s,", cmd->GetBodySourceCode().c_str()); + + if (i + 1 < commands.size()) + declaration += "\n"; + } + + return declaration; +} + +std::string ZRoom::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sSet_%06X", prefix.c_str(), rawDataIndex); +} + +/* + * There is one room in Ocarina of Time that lacks a header. Room 120, "Syotes", dates + * back to very early in the game's development. Since this room is a special case, + * declare automatically the data its contains whitout the need of a header. + */ +void ZRoom::SyotesRoomFix() +{ + RoomShapeCullable poly(parent, 0, this); + + poly.ParseRawData(); + poly.DeclareReferences(GetName()); + parent->AddDeclaration(0, poly.GetDeclarationAlignment(), poly.GetRawDataSize(), + poly.GetSourceTypeName(), poly.GetDefaultName(GetName()), + poly.GetBodySourceCode()); +} + +ZRoomCommand* ZRoom::FindCommandOfType(RoomCommand cmdType) +{ + for (size_t i = 0; i < commands.size(); i++) + { + if (commands[i]->GetRoomCommand() == cmdType) + return commands[i]; + } + + return nullptr; +} + +size_t ZRoom::GetCommandSizeFromNeighbor(ZRoomCommand* cmd) +{ + int32_t cmdIndex = -1; + + for (size_t i = 0; i < commands.size(); i++) + { + if (commands[i] == cmd) + { + cmdIndex = i; + break; + } + } + + if (cmdIndex != -1) + { + if (cmdIndex + 1 < (int32_t)commands.size()) + return commands[cmdIndex + 1]->cmdAddress - commands[cmdIndex]->cmdAddress; + else + return parent->GetRawData().size() - commands[cmdIndex]->cmdAddress; + } + + return 0; +} + +void ZRoom::GetSourceOutputCode([[maybe_unused]] const std::string& prefix) +{ + if (hackMode != "syotes_room") + DeclareVar(prefix, GetBodySourceCode()); +} + +size_t ZRoom::GetRawDataSize() const +{ + size_t size = 0; + + for (ZRoomCommand* cmd : commands) + size += cmd->GetRawDataSize(); + + return size; +} + +std::string ZRoom::GetSourceTypeName() const +{ + return "SceneCmd"; +} + +ZResourceType ZRoom::GetResourceType() const +{ + assert(zroomType == ZResourceType::Scene || zroomType == ZResourceType::Room || + zroomType == ZResourceType::AltHeader); + return zroomType; +} diff --git a/ZAPDTR/ZAPD/ZRoom/ZRoom.h b/ZAPDTR/ZAPD/ZRoom/ZRoom.h new file mode 100644 index 000000000..950dbbb18 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/ZRoom.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +#include "ZResource.h" +#include "ZRoomCommand.h" +#include "tinyxml2.h" + +class ZRoom : public ZResource +{ +public: + std::vector commands; + int32_t roomCount; // Only valid for scenes + + std::string hackMode; + + ZResourceType zroomType = ZResourceType::Error; + ZResourceType parentZroomType = ZResourceType::Error; + + ZRoom(ZFile* nParent); + virtual ~ZRoom(); + + void ExtractWithXML(tinyxml2::XMLElement* reader, uint32_t nRawDataIndex) override; + void ExtractFromBinary(uint32_t nRawDataIndex, ZResourceType parentType); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + void ParseRawDataLate() override; + void DeclareReferencesLate(const std::string& prefix) override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& body) override; + std::string GetBodySourceCode() const override; + + void GetSourceOutputCode(const std::string& prefix) override; + + std::string GetDefaultName(const std::string& prefix) const override; + size_t GetCommandSizeFromNeighbor(ZRoomCommand* cmd); + ZRoomCommand* FindCommandOfType(RoomCommand cmdType); + + size_t GetRawDataSize() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + +protected: + void SyotesRoomFix(); +}; diff --git a/ZAPDTR/ZAPD/ZRoom/ZRoomCommand.cpp b/ZAPDTR/ZAPD/ZRoom/ZRoomCommand.cpp new file mode 100644 index 000000000..711c86a40 --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/ZRoomCommand.cpp @@ -0,0 +1,58 @@ +#include "ZRoomCommand.h" + +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZRoom.h" + +ZRoomCommand::ZRoomCommand(ZFile* nParent) : ZResource(nParent) +{ +} + +void ZRoomCommand::ExtractCommandFromRoom(ZRoom* nZRoom, uint32_t nRawDataIndex) +{ + zRoom = nZRoom; + rawDataIndex = nRawDataIndex; + + ParseRawData(); +} + +void ZRoomCommand::ParseRawData() +{ + auto& parentRawData = parent->GetRawData(); + cmdID = static_cast(parentRawData.at(rawDataIndex)); + cmdAddress = rawDataIndex; + + cmdArg1 = parentRawData.at(rawDataIndex + 1); + cmdArg2 = BitConverter::ToUInt32BE(parentRawData, rawDataIndex + 4); + segmentOffset = Seg2Filespace(cmdArg2, parent->baseAddress); +} + +RoomCommand ZRoomCommand::GetRoomCommand() const +{ + return RoomCommand::Error; +} + +size_t ZRoomCommand::GetRawDataSize() const +{ + return 0x08; +} + +std::string ZRoomCommand::GetSourceTypeName() const +{ + return GetCommandCName(); +} + +ZResourceType ZRoomCommand::GetResourceType() const +{ + return ZResourceType::RoomCommand; +} + +std::string ZRoomCommand::GetCommandCName() const +{ + return "SceneCmd"; +} + +std::string ZRoomCommand::GetCommandHex() const +{ + return StringHelper::Sprintf("0x%02X", static_cast(cmdID)); +} diff --git a/ZAPDTR/ZAPD/ZRoom/ZRoomCommand.h b/ZAPDTR/ZAPD/ZRoom/ZRoomCommand.h new file mode 100644 index 000000000..279f08d7a --- /dev/null +++ b/ZAPDTR/ZAPD/ZRoom/ZRoomCommand.h @@ -0,0 +1,84 @@ +#pragma once + +#include "tinyxml2.h" + +#include +#include + +#include "ZFile.h" +#include "ZResource.h" + +class ZRoom; + +enum class RoomCommand : uint8_t +{ + SetStartPositionList = 0x00, + SetActorList = 0x01, + SetCsCamera = 0x02, + SetCollisionHeader = 0x03, + SetRoomList = 0x04, + SetWind = 0x05, + SetEntranceList = 0x06, + SetSpecialObjects = 0x07, + SetRoomBehavior = 0x08, + Unused09 = 0x09, + SetMesh = 0x0A, + SetObjectList = 0x0B, + SetLightList = 0x0C, + SetPathways = 0x0D, + SetTransitionActorList = 0x0E, + SetLightingSettings = 0x0F, + SetTimeSettings = 0x10, + SetSkyboxSettings = 0x11, + SetSkyboxModifier = 0x12, + SetExitList = 0x13, + EndMarker = 0x14, + SetSoundSettings = 0x15, + SetEchoSettings = 0x16, + SetCutscenes = 0x17, + SetAlternateHeaders = 0x18, + SetCameraSettings = 0x19, + + // MM Commands + SetWorldMapVisited = 0x19, + SetAnimatedMaterialList = 0x1A, + SetActorCutsceneList = 0x1B, + SetMinimapList = 0x1C, + Unused1D = 0x1D, + SetMinimapChests = 0x1E, + SetCutscenesMM = 0x1F, // This opcode is not in the original game. Its a special command for OTRs. + Error = 0xFF +}; + +class ZRoomCommand : public ZResource +{ +public: + int32_t cmdAddress; + uint32_t cmdIndex; + uint32_t commandSet; + RoomCommand cmdID; + offset_t segmentOffset; + + ZRoomCommand(ZFile* nParent); + virtual ~ZRoomCommand() = default; + + virtual void ExtractCommandFromRoom(ZRoom* nZRoom, uint32_t nRawDataIndex); + + void ParseRawData() override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + // Getters/Setters + virtual RoomCommand GetRoomCommand() const = 0; + size_t GetRawDataSize() const final override; + virtual std::string GetCommandCName() const; + + virtual std::string GetCommandHex() const; + +public: + ZRoom* zRoom; + + uint8_t cmdArg1; + segptr_t cmdArg2; +}; diff --git a/ZAPDTR/ZAPD/ZScalar.cpp b/ZAPDTR/ZAPD/ZScalar.cpp new file mode 100644 index 000000000..6c4cf121a --- /dev/null +++ b/ZAPDTR/ZAPD/ZScalar.cpp @@ -0,0 +1,280 @@ +#include "ZScalar.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Scalar, ZScalar); + +ZScalar::ZScalar(ZFile* nParent) : ZResource(nParent) +{ + memset(&scalarData, 0, sizeof(ZScalarData)); + scalarType = ZScalarType::ZSCALAR_NONE; + RegisterRequiredAttribute("Type"); +} + +void ZScalar::ExtractFromBinary(uint32_t nRawDataIndex, ZScalarType nScalarType) +{ + rawDataIndex = nRawDataIndex; + scalarType = nScalarType; + + // Don't parse raw data of external files + if (parent->GetMode() == ZFileMode::ExternalFile) + return; + + ParseRawData(); +} + +void ZScalar::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + scalarType = ZScalar::MapOutputTypeToScalarType(registeredAttributes.at("Type").value); +} + +ZScalarType ZScalar::MapOutputTypeToScalarType(const std::string& type) +{ + if (type == "s8") + { + return ZScalarType::ZSCALAR_S8; + } + else if (type == "u8") + { + return ZScalarType::ZSCALAR_U8; + } + else if (type == "x8") + { + return ZScalarType::ZSCALAR_X8; + } + else if (type == "s16") + { + return ZScalarType::ZSCALAR_S16; + } + else if (type == "u16") + { + return ZScalarType::ZSCALAR_U16; + } + else if (type == "x16") + { + return ZScalarType::ZSCALAR_X16; + } + else if (type == "s32") + { + return ZScalarType::ZSCALAR_S32; + } + else if (type == "u32") + { + return ZScalarType::ZSCALAR_U32; + } + else if (type == "x32") + { + return ZScalarType::ZSCALAR_X32; + } + else if (type == "s64") + { + return ZScalarType::ZSCALAR_S64; + } + else if (type == "u64") + { + return ZScalarType::ZSCALAR_U64; + } + else if (type == "x64") + { + return ZScalarType::ZSCALAR_X64; + } + else if (type == "f32") + { + return ZScalarType::ZSCALAR_F32; + } + else if (type == "f64") + { + return ZScalarType::ZSCALAR_F64; + } + + return ZScalarType::ZSCALAR_NONE; +} + +std::string ZScalar::MapScalarTypeToOutputType(const ZScalarType scalarType) +{ + switch (scalarType) + { + case ZScalarType::ZSCALAR_S8: + return "s8"; + case ZScalarType::ZSCALAR_U8: + case ZScalarType::ZSCALAR_X8: + return "u8"; + case ZScalarType::ZSCALAR_S16: + return "s16"; + case ZScalarType::ZSCALAR_U16: + case ZScalarType::ZSCALAR_X16: + return "u16"; + case ZScalarType::ZSCALAR_S32: + return "s32"; + case ZScalarType::ZSCALAR_U32: + case ZScalarType::ZSCALAR_X32: + return "u32"; + case ZScalarType::ZSCALAR_S64: + return "s64"; + case ZScalarType::ZSCALAR_U64: + case ZScalarType::ZSCALAR_X64: + return "u64"; + case ZScalarType::ZSCALAR_F32: + return "f32"; + case ZScalarType::ZSCALAR_F64: + return "f64"; + default: + return ""; + } +} + +size_t ZScalar::MapTypeToSize(const ZScalarType scalarType) +{ + switch (scalarType) + { + case ZScalarType::ZSCALAR_S8: + return sizeof(scalarData.s8); + case ZScalarType::ZSCALAR_U8: + case ZScalarType::ZSCALAR_X8: + return sizeof(scalarData.u8); + case ZScalarType::ZSCALAR_S16: + return sizeof(scalarData.s16); + case ZScalarType::ZSCALAR_U16: + case ZScalarType::ZSCALAR_X16: + return sizeof(scalarData.u16); + case ZScalarType::ZSCALAR_S32: + return sizeof(scalarData.s32); + case ZScalarType::ZSCALAR_U32: + case ZScalarType::ZSCALAR_X32: + return sizeof(scalarData.u32); + case ZScalarType::ZSCALAR_S64: + return sizeof(scalarData.s64); + case ZScalarType::ZSCALAR_U64: + case ZScalarType::ZSCALAR_X64: + return sizeof(scalarData.u64); + case ZScalarType::ZSCALAR_F32: + return sizeof(scalarData.f32); + case ZScalarType::ZSCALAR_F64: + return sizeof(scalarData.f64); + default: + return 0; + } +} + +size_t ZScalar::GetRawDataSize() const +{ + return ZScalar::MapTypeToSize(scalarType); +} + +void ZScalar::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + switch (scalarType) + { + case ZScalarType::ZSCALAR_S8: + scalarData.s8 = BitConverter::ToInt8BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_U8: + case ZScalarType::ZSCALAR_X8: + scalarData.u8 = BitConverter::ToUInt8BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_S16: + scalarData.s16 = BitConverter::ToInt16BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_U16: + case ZScalarType::ZSCALAR_X16: + scalarData.u16 = BitConverter::ToUInt16BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_S32: + scalarData.s32 = BitConverter::ToInt32BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_U32: + case ZScalarType::ZSCALAR_X32: + scalarData.u32 = BitConverter::ToUInt32BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_S64: + scalarData.s64 = BitConverter::ToInt64BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_U64: + case ZScalarType::ZSCALAR_X64: + scalarData.u64 = BitConverter::ToUInt64BE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_F32: + scalarData.f32 = BitConverter::ToFloatBE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_F64: + scalarData.f64 = BitConverter::ToDoubleBE(rawData, rawDataIndex); + break; + case ZScalarType::ZSCALAR_NONE: + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + "invalid value found for 'Type' attribute", "Defaulting to ''"); + break; + } +} + +std::string ZScalar::GetSourceTypeName() const +{ + return ZScalar::MapScalarTypeToOutputType(scalarType); +} + +std::string ZScalar::GetBodySourceCode() const +{ + switch (scalarType) + { + case ZScalarType::ZSCALAR_S8: + return StringHelper::Sprintf("%hhd", scalarData.s8); + case ZScalarType::ZSCALAR_U8: + return StringHelper::Sprintf("%hhu", scalarData.u8); + case ZScalarType::ZSCALAR_X8: + return StringHelper::Sprintf("0x%02X", scalarData.u8); + case ZScalarType::ZSCALAR_S16: + return StringHelper::Sprintf("%hd", scalarData.s16); + case ZScalarType::ZSCALAR_U16: + return StringHelper::Sprintf("%hu", scalarData.u16); + case ZScalarType::ZSCALAR_X16: + return StringHelper::Sprintf("0x%04X", scalarData.u16); + case ZScalarType::ZSCALAR_S32: + return StringHelper::Sprintf("%d", scalarData.s32); + case ZScalarType::ZSCALAR_U32: + return StringHelper::Sprintf("%u", scalarData.u32); + case ZScalarType::ZSCALAR_X32: + return StringHelper::Sprintf("0x%08X", scalarData.u32); + case ZScalarType::ZSCALAR_S64: + return StringHelper::Sprintf("%lld", scalarData.s64); + case ZScalarType::ZSCALAR_U64: + return StringHelper::Sprintf("%llu", scalarData.u64); + case ZScalarType::ZSCALAR_X64: + return StringHelper::Sprintf("0x%016X", scalarData.u64); + case ZScalarType::ZSCALAR_F32: + return StringHelper::Sprintf("%f", scalarData.f32); + case ZScalarType::ZSCALAR_F64: + return StringHelper::Sprintf("%lf", scalarData.f64); + default: + return "SCALAR_ERROR"; + } +} + +ZResourceType ZScalar::GetResourceType() const +{ + return ZResourceType::Scalar; +} + +bool ZScalar::DoesSupportArray() const +{ + return true; +} + +DeclarationAlignment ZScalar::GetDeclarationAlignment() const +{ + switch (scalarType) + { + case ZScalarType::ZSCALAR_S64: + case ZScalarType::ZSCALAR_U64: + case ZScalarType::ZSCALAR_F64: + return DeclarationAlignment::Align8; + default: + return DeclarationAlignment::Align4; + } +} diff --git a/ZAPDTR/ZAPD/ZScalar.h b/ZAPDTR/ZAPD/ZScalar.h new file mode 100644 index 000000000..8f98f261d --- /dev/null +++ b/ZAPDTR/ZAPD/ZScalar.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include "ZResource.h" +#include "tinyxml2.h" + +enum class ZScalarType +{ + ZSCALAR_NONE, + ZSCALAR_S8, + ZSCALAR_U8, + ZSCALAR_X8, + ZSCALAR_S16, + ZSCALAR_U16, + ZSCALAR_X16, + ZSCALAR_S32, + ZSCALAR_U32, + ZSCALAR_X32, + ZSCALAR_S64, + ZSCALAR_U64, + ZSCALAR_X64, + ZSCALAR_F32, + ZSCALAR_F64 +}; + +typedef union ZScalarData +{ + uint8_t u8; + int8_t s8; + uint16_t u16; + int16_t s16; + uint32_t u32; + int32_t s32; + uint64_t u64; + int64_t s64; + float f32; + double f64; +} ZScalarData; + +class ZScalar : public ZResource +{ + friend class ZVector; + +public: + ZScalarData scalarData; + ZScalarType scalarType; + + ZScalar(ZFile* nParent); + + void ExtractFromBinary(uint32_t nRawDataIndex, ZScalarType nScalarType); + + void ParseRawData() override; + void ParseXML(tinyxml2::XMLElement* reader) override; + std::string GetBodySourceCode() const override; + + bool DoesSupportArray() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; + + static size_t MapTypeToSize(const ZScalarType scalarType); + static ZScalarType MapOutputTypeToScalarType(const std::string& type); + static std::string MapScalarTypeToOutputType(const ZScalarType scalarType); +}; diff --git a/ZAPDTR/ZAPD/ZSkeleton.cpp b/ZAPDTR/ZAPD/ZSkeleton.cpp new file mode 100644 index 000000000..8fa4945d7 --- /dev/null +++ b/ZAPDTR/ZAPD/ZSkeleton.cpp @@ -0,0 +1,478 @@ +#include "ZSkeleton.h" + +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "WarningHandler.h" + +REGISTER_ZFILENODE(Skeleton, ZSkeleton); +REGISTER_ZFILENODE(LimbTable, ZLimbTable); + +ZSkeleton::ZSkeleton(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("Type"); + RegisterRequiredAttribute("LimbType"); + genOTRDef = true; + + RegisterOptionalAttribute("EnumName"); + RegisterOptionalAttribute("LimbNone"); + RegisterOptionalAttribute("LimbMax"); + + genOTRDef = true; +} + +void ZSkeleton::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string skelTypeXml = registeredAttributes.at("Type").value; + + if (skelTypeXml == "Flex") + type = ZSkeletonType::Flex; + else if (skelTypeXml == "Curve") + type = ZSkeletonType::Curve; + else if (skelTypeXml != "Normal") + { + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + "invalid value found for 'Type' attribute", ""); + } + + std::string limbTypeXml = registeredAttributes.at("LimbType").value; + limbType = ZLimb::GetTypeByAttributeName(limbTypeXml); + if (limbType == ZLimbType::Invalid) + { + HANDLE_ERROR_RESOURCE( + WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + StringHelper::Sprintf("invalid value '%s' found for 'LimbType' attribute", + limbTypeXml.c_str()), + "Defaulting to 'Standard'."); + } + + enumName = registeredAttributes.at("EnumName").value; + limbNoneName = registeredAttributes.at("LimbNone").value; + limbMaxName = registeredAttributes.at("LimbMax").value; + + if (enumName != "") + { + if (limbNoneName == "" || limbMaxName == "") + { + HANDLE_ERROR_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex, + "'EnumName' attribute was used but either 'LimbNone' or " + "'LimbMax' attribute is missing", + ""); + } + } + + if (limbNoneName != "") + { + if (limbMaxName == "") + { + HANDLE_ERROR_RESOURCE( + WarningType::MissingAttribute, parent, this, rawDataIndex, + "'LimbNone' attribute was used but 'LimbMax' attribute is missing", ""); + } + } + + if (limbMaxName != "") + { + if (limbNoneName == "") + { + HANDLE_ERROR_RESOURCE( + WarningType::MissingAttribute, parent, this, rawDataIndex, + "'LimbMax' attribute was used but 'LimbNone' attribute is missing", ""); + } + } +} + +void ZSkeleton::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + limbsArrayAddress = BitConverter::ToUInt32BE(rawData, rawDataIndex); + limbCount = BitConverter::ToUInt8BE(rawData, rawDataIndex + 4); + + if (type == ZSkeletonType::Flex) + { + dListCount = BitConverter::ToUInt8BE(rawData, rawDataIndex + 8); + } +} + +void ZSkeleton::DeclareReferences(const std::string& prefix) +{ + std::string defaultPrefix = name; + if (defaultPrefix == "") + defaultPrefix = prefix; + + ZResource::DeclareReferences(defaultPrefix); + + if (limbsArrayAddress != SEGMENTED_NULL && GETSEGNUM(limbsArrayAddress) == parent->segment) + { + offset_t ptr = Seg2Filespace(limbsArrayAddress, parent->baseAddress); + + if (!parent->HasDeclaration(ptr)) + { + limbsTable = new ZLimbTable(parent); + limbsTable->ExtractFromBinary(ptr, limbType, limbCount); + limbsTable->SetName(StringHelper::Sprintf("%sLimbs", defaultPrefix.c_str())); + parent->AddResource(limbsTable); + } + else + { + limbsTable = static_cast(parent->FindResource(ptr)); + } + + if (limbsTable->enumName == "") + { + limbsTable->enumName = enumName; + } + if (limbsTable->limbNoneName == "") + { + limbsTable->limbNoneName = limbNoneName; + } + if (limbsTable->limbMaxName == "") + { + limbsTable->limbMaxName = limbMaxName; + } + } +} + +std::string ZSkeleton::GetBodySourceCode() const +{ + std::string limbArrayName; + Globals::Instance->GetSegmentedPtrName(limbsArrayAddress, parent, "", limbArrayName, + parent->workerID); + + std::string countStr; + assert(limbsTable != nullptr); + // There are some Skeletons with the wrong limb count on them, so this check is necessary. + if (limbsTable->count == limbCount) + { + countStr = StringHelper::Sprintf("ARRAY_COUNT(%s)", limbArrayName.c_str()); + } + else + { + countStr = StringHelper::Sprintf("%i", limbCount); + } + + switch (type) + { + case ZSkeletonType::Normal: + case ZSkeletonType::Curve: + return StringHelper::Sprintf("\n\t%s, %s\n", limbArrayName.c_str(), countStr.c_str()); + + case ZSkeletonType::Flex: + return StringHelper::Sprintf("\n\t{ %s, %s }, %i\n", limbArrayName.c_str(), + countStr.c_str(), dListCount); + } + + // TODO: Throw exception? + return "ERROR"; +} + +size_t ZSkeleton::GetRawDataSize() const +{ + switch (type) + { + case ZSkeletonType::Flex: + return 0xC; + case ZSkeletonType::Normal: + case ZSkeletonType::Curve: + default: + return 0x8; + } +} + +std::string ZSkeleton::GetSourceTypeName() const +{ + switch (type) + { + case ZSkeletonType::Normal: + return "SkeletonHeader"; + case ZSkeletonType::Flex: + return "FlexSkeletonHeader"; + case ZSkeletonType::Curve: + return "CurveSkeletonHeader"; + } + + return "SkeletonHeader"; +} + +ZResourceType ZSkeleton::GetResourceType() const +{ + return ZResourceType::Skeleton; +} + +DeclarationAlignment ZSkeleton::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align4; +} + +uint8_t ZSkeleton::GetLimbCount() +{ + return limbCount; +} + +/* ZLimbTable */ + +ZLimbTable::ZLimbTable(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("LimbType"); + RegisterRequiredAttribute("Count"); + RegisterOptionalAttribute("EnumName"); + RegisterOptionalAttribute("LimbNone"); + RegisterOptionalAttribute("LimbMax"); +} + +void ZLimbTable::ExtractFromBinary(uint32_t nRawDataIndex, ZLimbType nLimbType, size_t nCount) +{ + rawDataIndex = nRawDataIndex; + limbType = nLimbType; + count = nCount; + + ParseRawData(); +} + +void ZLimbTable::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string limbTypeXml = registeredAttributes.at("LimbType").value; + limbType = ZLimb::GetTypeByAttributeName(limbTypeXml); + if (limbType == ZLimbType::Invalid) + { + HANDLE_WARNING_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + "invalid value found for 'LimbType' attribute.", + "Defaulting to 'Standard'."); + limbType = ZLimbType::Standard; + } + + count = StringHelper::StrToL(registeredAttributes.at("Count").value); + + enumName = registeredAttributes.at("EnumName").value; + limbNoneName = registeredAttributes.at("LimbNone").value; + limbMaxName = registeredAttributes.at("LimbMax").value; + + if (enumName != "") + { + if (limbNoneName == "" || limbMaxName == "") + { + HANDLE_ERROR_RESOURCE( + WarningType::MissingAttribute, parent, this, rawDataIndex, + "'EnumName' attribute was used but 'LimbNone'/'LimbMax' attributes is missing", ""); + } + } + + if (limbNoneName != "") + { + if (limbMaxName == "") + { + HANDLE_ERROR_RESOURCE( + WarningType::MissingAttribute, parent, this, rawDataIndex, + "'LimbNone' attribute was used but 'LimbMax' attribute is missing", ""); + } + } + + if (limbMaxName != "") + { + if (limbNoneName == "") + { + HANDLE_ERROR_RESOURCE( + WarningType::MissingAttribute, parent, this, rawDataIndex, + "'LimbMax' attribute was used but 'LimbNone' attribute is missing", ""); + } + } +} + +void ZLimbTable::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + uint32_t ptr = rawDataIndex; + + limbsAddresses.reserve(count); + for (size_t i = 0; i < count; i++) + { + limbsAddresses.push_back(BitConverter::ToUInt32BE(rawData, ptr)); + ptr += 4; + } +} + +void ZLimbTable::DeclareReferences(const std::string& prefix) +{ + std::string varPrefix = name; + if (varPrefix == "") + varPrefix = prefix; + + ZResource::DeclareReferences(varPrefix); + limbsReferences.reserve(count); + for (size_t i = 0; i < count; i++) + { + segptr_t limbAddress = limbsAddresses[i]; + + if (limbAddress != 0 && GETSEGNUM(limbAddress) == parent->segment) + { + uint32_t limbOffset = Seg2Filespace(limbAddress, parent->baseAddress); + ZLimb* limb; + + if (!parent->HasDeclaration(limbOffset)) + { + limb = new ZLimb(parent); + limb->ExtractFromBinary(limbOffset, limbType); + limb->SetName(limb->GetDefaultName(varPrefix)); + limb->DeclareVar(varPrefix, ""); + limb->DeclareReferences(varPrefix); + parent->AddResource(limb); + } + else + { + limb = static_cast(parent->FindResource(limbOffset)); + assert(limb != nullptr); + assert(limb->GetResourceType() == ZResourceType::Limb); + } + + limb->limbsTable = this; + limb->SetLimbIndex(i + 1); + + limbsReferences.push_back(limb); + } + } +} + +Declaration* ZLimbTable::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), auxName, limbsAddresses.size(), bodyStr); + decl->staticConf = staticConf; + return decl; +} + +std::string ZLimbTable::GetBodySourceCode() const +{ + std::string body; + + for (size_t i = 0; i < count; i++) + { + std::string limbName; + Globals::Instance->GetSegmentedPtrName(limbsAddresses[i], parent, "", limbName, + parent->workerID); + body += StringHelper::Sprintf("\t%s,", limbName.c_str()); + + auto& limb = limbsReferences.at(i); + std::string limbEnumName = limb->enumName; + if (limbEnumName != "") + { + body += StringHelper::Sprintf(" /* %s */", limbEnumName.c_str()); + } + + if (i + 1 < count) + body += "\n"; + } + + return body; +} + +std::string ZLimbTable::GetSourceOutputHeader([[maybe_unused]] const std::string& prefix, std::set* nameSet) +{ + if (limbNoneName == "" || limbMaxName == "" || enumName == "") + { + // Don't produce a enum of any of those attributes is missing + return ""; + } + + std::string limbEnum = StringHelper::Sprintf("typedef enum %s {\n", enumName.c_str()); + + // This assumes there isn't any skeleton with more than 0x100 limbs + + limbEnum += StringHelper::Sprintf(" /* 0x00 */ %s,\n", limbNoneName.c_str()); + + size_t i = 0; + for (; i < count; i++) + { + auto& limb = limbsReferences.at(i); + std::string limbEnumName = limb->enumName; + + if (limbEnumName == "") + { + HANDLE_ERROR_RESOURCE( + WarningType::MissingAttribute, parent, this, rawDataIndex, + "Skeleton's enum attributes were used but at least one limb is missing its " + "'LimbName' attribute", + StringHelper::Sprintf("When processing limb %02i, named '%s' at offset '0x%X'", + i + 1, limb->GetName().c_str(), limb->GetRawDataIndex())); + } + + limbEnum += StringHelper::Sprintf(" /* 0x%02X */ %s,\n", i + 1, limbEnumName.c_str()); + } + + limbEnum += StringHelper::Sprintf(" /* 0x%02X */ %s\n", i + 1, limbMaxName.c_str()); + + limbEnum += StringHelper::Sprintf("} %s;\n", enumName.c_str()); + + return limbEnum; +} + +std::string ZLimbTable::GetSourceTypeName() const +{ + switch (limbType) + { + case ZLimbType::Standard: + case ZLimbType::LOD: + case ZLimbType::Skin: + return "void*"; + + case ZLimbType::Curve: + case ZLimbType::Legacy: + return StringHelper::Sprintf("%s*", ZLimb::GetSourceTypeName(limbType)); + + case ZLimbType::Invalid: + // TODO: Proper error message or something. + assert("Invalid limb type.\n"); + } + + return "ERROR"; +} + +ZResourceType ZLimbTable::GetResourceType() const +{ + return ZResourceType::LimbTable; +} + +size_t ZLimbTable::GetRawDataSize() const +{ + return 4 * limbsAddresses.size(); +} + +std::string ZLimbTable::GetLimbEnumName(uint8_t limbIndex) const +{ + if (limbIndex == 0xFF) + { + return "LIMB_DONE"; + } + + if (limbIndex < count) + { + std::string limbEnumName = limbsReferences.at(limbIndex)->enumName; + if (limbEnumName != "") + { + return StringHelper::Sprintf("%s - 1", limbEnumName.c_str()); + } + } + else + { + HANDLE_WARNING_RESOURCE(WarningType::InvalidExtractedData, parent, this, rawDataIndex, + StringHelper::Sprintf("Limb index '%02i' out of range", limbIndex), + ""); + } + + return StringHelper::Sprintf("0x%02X", limbIndex); +} diff --git a/ZAPDTR/ZAPD/ZSkeleton.h b/ZAPDTR/ZAPD/ZSkeleton.h new file mode 100644 index 000000000..eee70445e --- /dev/null +++ b/ZAPDTR/ZAPD/ZSkeleton.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include + +#include "ZDisplayList.h" +#include "ZFile.h" +#include "ZLimb.h" + +enum class ZSkeletonType +{ + Normal, + Flex, + Curve, +}; + +class ZLimbTable : public ZResource +{ +public: + ZLimbType limbType = ZLimbType::Standard; + size_t count = 0; + + std::vector limbsAddresses; + std::vector limbsReferences; // borrowed pointers, do not delete! + + // XML attributes + std::string enumName; + std::string limbNoneName; + std::string limbMaxName; + + ZLimbTable(ZFile* nParent); + + void ExtractFromBinary(uint32_t nRawDataIndex, ZLimbType nLimbType, size_t nCount); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceOutputHeader([[maybe_unused]] const std::string& prefix, std::set* nameSet) override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + + std::string GetLimbEnumName(uint8_t limbIndex) const; +}; + +class ZSkeleton : public ZResource +{ +public: + ZSkeletonType type = ZSkeletonType::Normal; + ZLimbType limbType = ZLimbType::Standard; + std::string enumName; + std::string limbNoneName; + std::string limbMaxName; + + segptr_t limbsArrayAddress; + uint8_t limbCount = 0; + uint8_t dListCount = 0; // FLEX SKELETON ONLY + + ZSkeleton(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; + + uint8_t GetLimbCount(); + + ZLimbTable* limbsTable = nullptr; // borrowed pointer, do not delete! + +}; diff --git a/ZAPDTR/ZAPD/ZString.cpp b/ZAPDTR/ZAPD/ZString.cpp new file mode 100644 index 000000000..4a9069496 --- /dev/null +++ b/ZAPDTR/ZAPD/ZString.cpp @@ -0,0 +1,69 @@ +#include "ZString.h" + +#include +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(String, ZString); + +ZString::ZString(ZFile* nParent) : ZResource(nParent) +{ +} + +void ZString::ParseRawData() +{ + size_t size = 0; + const auto& rawData = parent->GetRawData(); + const auto& rawDataArr = rawData.data(); + size_t rawDataSize = rawData.size(); + for (size_t i = rawDataIndex; i < rawDataSize; ++i) + { + ++size; + if (rawDataArr[i] == '\0') + { + break; + } + } + + auto dataStart = rawData.begin() + rawDataIndex; + strData.assign(dataStart, dataStart + size); +} + +Declaration* ZString::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), auxName, 0, bodyStr); + decl->staticConf = staticConf; + return decl; +} + +std::string ZString::GetBodySourceCode() const +{ + return StringHelper::Sprintf("\t\"%s\"", strData.data()); +} + +std::string ZString::GetSourceOutputHeader([[maybe_unused]] const std::string& prefix, std::set *nameSet) +{ + return StringHelper::Sprintf("#define %s_macro \"%s\"", name.c_str(), strData.data()); +} + +std::string ZString::GetSourceTypeName() const +{ + return "char"; +} + +ZResourceType ZString::GetResourceType() const +{ + return ZResourceType::String; +} + +size_t ZString::GetRawDataSize() const +{ + return strData.size(); +} diff --git a/ZAPDTR/ZAPD/ZString.h b/ZAPDTR/ZAPD/ZString.h new file mode 100644 index 000000000..19f615a90 --- /dev/null +++ b/ZAPDTR/ZAPD/ZString.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ZResource.h" +#include "tinyxml2.h" + +class ZString : public ZResource +{ +public: + ZString(ZFile* nParent); + + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + std::string GetSourceOutputHeader(const std::string& prefix, std::set *nameSet) override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; + +protected: + std::vector strData; +}; diff --git a/ZAPDTR/ZAPD/ZSurfaceType.cpp b/ZAPDTR/ZAPD/ZSurfaceType.cpp new file mode 100644 index 000000000..feb29c3d3 --- /dev/null +++ b/ZAPDTR/ZAPD/ZSurfaceType.cpp @@ -0,0 +1,65 @@ +#include "ZSurfaceType.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +REGISTER_ZFILENODE(SurfaceType, ZSurfaceType); + +ZSurfaceType::ZSurfaceType(ZFile* nParent) : ZResource(nParent) +{ +} + +ZSurfaceType::~ZSurfaceType() +{ +} + +void ZSurfaceType::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + data[0] = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0); + data[1] = BitConverter::ToUInt32BE(rawData, rawDataIndex + 4); +} + +void ZSurfaceType::DeclareReferences(const std::string& prefix) +{ + std::string declaration; + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + parent->AddDeclaration(rawDataIndex, DeclarationAlignment::Align4, GetRawDataSize(), + GetSourceTypeName(), name.c_str(), GetBodySourceCode()); +} + +std::string ZSurfaceType::GetBodySourceCode() const +{ + return StringHelper::Sprintf("{0x%08X, 0x%08X}", data[0], data[1]); +} + +std::string ZSurfaceType::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sSurfaceType_%06X", prefix.c_str(), rawDataIndex); +} + +ZResourceType ZSurfaceType::GetResourceType() const +{ + return ZResourceType::SurfaceType; +} + +size_t ZSurfaceType::GetRawDataSize() const +{ + return 8; +} + +std::string ZSurfaceType::GetSourceTypeName() const +{ + return "SurfaceType"; +} + +bool ZSurfaceType::DoesSupportArray() const +{ + return true; +} diff --git a/ZAPDTR/ZAPD/ZSurfaceType.h b/ZAPDTR/ZAPD/ZSurfaceType.h new file mode 100644 index 000000000..283daf683 --- /dev/null +++ b/ZAPDTR/ZAPD/ZSurfaceType.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "ZFile.h" +#include "ZResource.h" + +class ZSurfaceType : public ZResource +{ +public: + std::array data; + + ZSurfaceType(ZFile* nParent); + ~ZSurfaceType(); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + std::string GetDefaultName(const std::string& prefix) const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + bool DoesSupportArray() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZSymbol.cpp b/ZAPDTR/ZAPD/ZSymbol.cpp new file mode 100644 index 000000000..b5df68669 --- /dev/null +++ b/ZAPDTR/ZAPD/ZSymbol.cpp @@ -0,0 +1,98 @@ +#include "ZSymbol.h" + +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Symbol, ZSymbol); + +ZSymbol::ZSymbol(ZFile* nParent) : ZResource(nParent) +{ + RegisterOptionalAttribute("Type"); + RegisterOptionalAttribute("TypeSize"); + RegisterOptionalAttribute("Count"); +} + +void ZSymbol::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string typeXml = registeredAttributes.at("Type").value; + + if (typeXml == "") + { + HANDLE_WARNING_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex, + "missing 'Type' attribute in ", "Defaulting to 'void*'."); + type = "void*"; + } + else + { + type = typeXml; + } + + std::string typeSizeXml = registeredAttributes.at("TypeSize").value; + if (typeSizeXml == "") + { + HANDLE_WARNING_RESOURCE(WarningType::MissingAttribute, parent, this, rawDataIndex, + "missing 'TypeSize' attribute in ", "Defaulting to '4'."); + typeSize = 4; // Size of a word. + } + else + { + typeSize = StringHelper::StrToL(typeSizeXml, 0); + } + + if (registeredAttributes.at("Count").wasSet) + { + isArray = true; + + std::string countXml = registeredAttributes.at("Count").value; + if (countXml != "") + count = StringHelper::StrToL(countXml, 0); + } + + if (registeredAttributes.at("Static").value == "On") + { + HANDLE_WARNING_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + "a cannot be marked as static", + "Disabling static for this resource."); + } + staticConf = StaticConfig::Off; +} + +Declaration* ZSymbol::DeclareVar([[maybe_unused]] const std::string& prefix, + [[maybe_unused]] const std::string& bodyStr) +{ + return nullptr; +} + +size_t ZSymbol::GetRawDataSize() const +{ + if (isArray) + return count * typeSize; + + return typeSize; +} + +std::string ZSymbol::GetSourceOutputHeader([[maybe_unused]] const std::string& prefix, std::set *nameSet) +{ + if (isArray) + { + if (count == 0) + return StringHelper::Sprintf("extern %s %s[];\n", type.c_str(), name.c_str()); + else + return StringHelper::Sprintf("extern %s %s[%i];\n", type.c_str(), name.c_str(), count); + } + + return StringHelper::Sprintf("extern %s %s;\n", type.c_str(), name.c_str()); +} + +std::string ZSymbol::GetSourceTypeName() const +{ + return type; +} + +ZResourceType ZSymbol::GetResourceType() const +{ + return ZResourceType::Symbol; +} diff --git a/ZAPDTR/ZAPD/ZSymbol.h b/ZAPDTR/ZAPD/ZSymbol.h new file mode 100644 index 000000000..e00f14c6b --- /dev/null +++ b/ZAPDTR/ZAPD/ZSymbol.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ZResource.h" +#include "tinyxml2.h" + +class ZSymbol : public ZResource +{ +protected: + std::string type; + size_t typeSize; + bool isArray = false; + uint32_t count = 0; + +public: + ZSymbol(ZFile* nParent); + + void ParseXML(tinyxml2::XMLElement* reader) override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + + std::string GetSourceOutputHeader(const std::string& prefix, std::set *nameSet) override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZText.cpp b/ZAPDTR/ZAPD/ZText.cpp new file mode 100644 index 000000000..16704a4aa --- /dev/null +++ b/ZAPDTR/ZAPD/ZText.cpp @@ -0,0 +1,193 @@ +#include "ZText.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Text, ZText); + +ZText::ZText(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("CodeOffset"); + RegisterOptionalAttribute("LangOffset", "0"); + RegisterOptionalAttribute("Language", "English"); +} + +void ZText::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + uint32_t currentPtr = StringHelper::StrToL(registeredAttributes.at("CodeOffset").value, 16); + uint32_t langPtr = currentPtr; + bool isPalLang = false; + bool isJpnLang = false; + + if (registeredAttributes.at("Language").value == "Japanese") + { + isJpnLang = true; + } + + if (StringHelper::StrToL(registeredAttributes.at("LangOffset").value, 16) != 0) + { + langPtr = StringHelper::StrToL(registeredAttributes.at("LangOffset").value, 16); + + if (langPtr != currentPtr) + isPalLang = true; + } + + std::vector codeData; + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + codeData = Globals::Instance->GetBaseromFile("code"); + else + codeData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "code"); + + while (true) + { + MessageEntry msgEntry; + msgEntry.id = BitConverter::ToInt16BE(codeData, currentPtr + 0); + + if (isPalLang) + { + msgEntry.segmentId = (codeData[langPtr + 0]); + msgEntry.msgOffset = BitConverter::ToInt32BE(codeData, langPtr + 0) & 0x00FFFFFF; + } + else + { + msgEntry.segmentId = (codeData[langPtr + 4]); + msgEntry.msgOffset = BitConverter::ToInt32BE(codeData, langPtr + 4) & 0x00FFFFFF; + } + + uint32_t msgPtr = msgEntry.msgOffset; + + if (isJpnLang) + { + msgEntry.textboxType = (codeData[currentPtr + 2] & 0xF0) >> 4; + msgEntry.textboxYPos = (codeData[currentPtr + 2] & 0x0F); + + uint16_t c = BitConverter::ToUInt16BE(rawData, msgPtr); + unsigned int extra = 0; + bool stop = false; + + while (!stop || extra > 0) { + msgEntry.msg += rawData[msgPtr + 1]; + msgEntry.msg += rawData[msgPtr]; + msgPtr+=2; + + // Some control codes require reading extra bytes + if (extra == 0) + { + // End marker, so stop this message and do not read anything else + if (c == 0x8170) + { + stop = true; + } + else if (c == 0x000B || c == 0x819A || c == 0x819E || c == 0x81A3 || c == 0x869F || + c == 0x86C7 || c == 0x86C9 || c == 0x81F3) + { + extra = 1; + } + // "Continue to new text ID", so stop this message and read one more short for the text ID + else if (c == 0x81CB) + { + extra = 1; + stop = true; + } + else if (c == 0x86B3) + { + extra = 2; + } + } + else + { + extra--; + } + + c = BitConverter::ToUInt16BE(rawData, msgPtr); + } + } + else + { + unsigned char c = rawData[msgPtr]; + unsigned int extra = 0; + bool stop = false; + + msgEntry.textboxType = (codeData[currentPtr + 2] & 0xF0) >> 4; + msgEntry.textboxYPos = (codeData[currentPtr + 2] & 0x0F); + // Continue parsing until we are told to stop and all extra bytes are read + while ((c != '\0' && !stop) || extra > 0) + { + msgEntry.msg += c; + msgPtr++; + + // Some control codes require reading extra bytes + if (extra == 0) + { + // End marker, so stop this message and do not read anything else + if (c == 0x02) + { + stop = true; + } + else if (c == 0x05 || c == 0x13 || c == 0x0E || c == 0x0C || c == 0x1E || c == 0x06 || + c == 0x14) + { + extra = 1; + } + // "Continue to new text ID", so stop this message and read two more bytes for the text ID + else if (c == 0x07) + { + extra = 2; + stop = true; + } + else if (c == 0x12 || c == 0x11) + { + extra = 2; + } + else if (c == 0x15) + { + extra = 3; + } + } + else + { + extra--; + } + + c = rawData[msgPtr]; + } + } + + messages.push_back(msgEntry); + + if (msgEntry.id == 0xFFFC || msgEntry.id == 0xFFFF) + break; + + currentPtr += 8; + + if (isPalLang) + langPtr += 4; + else + langPtr += 8; + } + + int bp2 = 0; +} + +std::string ZText::GetSourceTypeName() const +{ + return "u8"; +} + +size_t ZText::GetRawDataSize() const +{ + return 1; +} + +ZResourceType ZText::GetResourceType() const +{ + return ZResourceType::Text; +} diff --git a/ZAPDTR/ZAPD/ZText.h b/ZAPDTR/ZAPD/ZText.h new file mode 100644 index 000000000..ed85c8122 --- /dev/null +++ b/ZAPDTR/ZAPD/ZText.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ZResource.h" +#include "tinyxml2.h" + +class MessageEntry +{ +public: + uint16_t id; + uint8_t textboxType; + uint8_t textboxYPos; + uint32_t segmentId; + uint32_t msgOffset; + std::string msg; +}; + +class ZText : public ZResource +{ +public: + std::vector messages; + + ZText(ZFile* nParent); + + void ParseRawData() override; + + void ParseOoT(); + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZTextMM.cpp b/ZAPDTR/ZAPD/ZTextMM.cpp new file mode 100644 index 000000000..5c6367662 --- /dev/null +++ b/ZAPDTR/ZAPD/ZTextMM.cpp @@ -0,0 +1,222 @@ +#include "ZTextMM.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(TextMM, ZTextMM); + +ZTextMM::ZTextMM(ZFile* nParent) : ZResource(nParent) +{ + RegisterRequiredAttribute("CodeOffset"); + RegisterOptionalAttribute("LangOffset", "0"); +} + +void ZTextMM::ParseRawData() +{ + ZResource::ParseRawData(); + + ParseMM(); +} + +void ZTextMM::ParseMM() +{ + const auto& rawData = parent->GetRawData(); + uint32_t currentPtr = StringHelper::StrToL(registeredAttributes.at("CodeOffset").value, 16); + uint32_t langPtr = currentPtr; + bool isPalLang = false; + + if (StringHelper::StrToL(registeredAttributes.at("LangOffset").value, 16) != 0) + { + langPtr = StringHelper::StrToL(registeredAttributes.at("LangOffset").value, 16); + + if (langPtr != currentPtr) + isPalLang = true; + } + + std::vector codeData; + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + codeData = Globals::Instance->GetBaseromFile("code"); + else + codeData = + Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "code"); + + while (true) + { + MessageEntryMM msgEntry; + msgEntry.id = BitConverter::ToInt16BE(codeData, currentPtr + 0); + msgEntry.msgOffset = BitConverter::ToInt32BE(codeData, langPtr + 4) & 0x00FFFFFF; + + uint32_t msgPtr = msgEntry.msgOffset; + + msgEntry.segmentId = (codeData[langPtr + 4]); + + // NES and STAFF has different control codes and arg sizes, so we parse them separately + if (parent->GetName() == "staff_message_data_static") { // STAFF + // Credits has no header + // The textbox Type and Pos are in the code table just after the text ID as a single byte + uint8_t typePos = (codeData[currentPtr + 2]); + msgEntry.textboxType = (typePos & 0xF0) >> 4; + msgEntry.textboxYPos = typePos & 0x0F; + + // The rest of these are unused and not provided, so we set them to zero; + msgEntry.textboxType = 0; + msgEntry.icon = 0; + msgEntry.nextMessageID = 0; + msgEntry.firstItemCost = 0; + msgEntry.secondItemCost = 0; + + unsigned char c = rawData[msgPtr]; + + // Read until end marker (0x02) + while (true) { + msgEntry.msg += c; + + if (c == 0x02) { // End marker + break; + } + + switch (c) { + case 0x05: // Color: Change text color + case 0x06: // Shift: Print xx Spaces + case 0x0E: // Fade: Keep Text on Screen for xxxx Before Closing + case 0x13: // Item Icon: + case 0x14: // Text Speed: + case 0x1E: // Highscores: Prints xx highscore value + msgEntry.msg += rawData[msgPtr + 1]; + msgPtr++; + break; + case 0x07: // TextID: Open xxxx textID + case 0x0C: // Box Break Delay: Delay for xxxx Before Printing Remaining Text + case 0x11: // Fade2: Alternate delay + case 0x12: // SFX: Play Sound Effect xxxx + msgEntry.msg += rawData[msgPtr + 1]; + msgEntry.msg += rawData[msgPtr + 2]; + msgPtr += 2; + break; + case 0x15: // Background + msgEntry.msg += rawData[msgPtr + 1]; + msgEntry.msg += rawData[msgPtr + 2]; + msgEntry.msg += rawData[msgPtr + 3]; + msgPtr += 3; + break; + } + + msgPtr++; + c = rawData[msgPtr]; + } + } else if (parent->GetName() == "message_data_static_jp") { + msgEntry.textboxType = (rawData[msgPtr + 0]); + msgEntry.textboxYPos = (rawData[msgPtr + 1]); + msgEntry.icon = BitConverter::ToUInt16BE(rawData, msgPtr + 2); + msgEntry.nextMessageID = BitConverter::ToInt16BE(rawData, msgPtr + 4); + msgEntry.firstItemCost = BitConverter::ToInt16BE(rawData, msgPtr + 6); + msgEntry.secondItemCost = BitConverter::ToInt16BE(rawData, msgPtr + 8); + + msgPtr += 12; + + uint16_t c = BitConverter::ToUInt16BE(rawData, msgPtr); + + // Read until end marker (0x0500) + while (true) { + msgEntry.msg += rawData[msgPtr + 1]; + msgEntry.msg += rawData[msgPtr]; + + if (c == 0x0500) { // End marker + break; + } + + switch (c) { + case 0x001F: // Shift: Print 00xx Spaces + msgEntry.msg += rawData[msgPtr + 3]; + msgEntry.msg += rawData[msgPtr + 2]; + msgPtr += 2; + break; + case 0x0110: // Box Break Delay: Delay for xxxx Before Printing Remaining Text + case 0x0111: // Fade: Keep Text on Screen for xxxx Before Closing + case 0x0112: // Fade Skippable: Delay for xxxx Before Ending Conversation + case 0x0120: // SFX: Play Sound Effect xxxx + case 0x0128: // Delay: Delay for xxxx Before Resuming Text Flow + msgEntry.msg += rawData[msgPtr + 3]; + msgEntry.msg += rawData[msgPtr + 2]; + msgPtr += 2; + break; + } + + msgPtr += 2; + c = BitConverter::ToUInt16BE(rawData, msgPtr); + } + } else { // NES + // NES has a header with extra information, parse that and move the ptr forward + msgEntry.textboxType = (rawData[msgPtr + 0]); + msgEntry.textboxYPos = (rawData[msgPtr + 1]); + msgEntry.icon = (rawData[msgPtr + 2]); + msgEntry.nextMessageID = BitConverter::ToInt16BE(rawData, msgPtr + 3); + msgEntry.firstItemCost = BitConverter::ToInt16BE(rawData, msgPtr + 5); + msgEntry.secondItemCost = BitConverter::ToInt16BE(rawData, msgPtr + 7); + + msgPtr += 11; + + unsigned char c = rawData[msgPtr]; + + // Read until end marker (0xBF) + while (true) { + msgEntry.msg += c; + + if (c == 0xBF) { // End marker + break; + } + + switch (c) { + case 0x14: // Shift: Print xx Spaces + msgEntry.msg += rawData[msgPtr + 1]; + msgPtr++; + break; + case 0x1B: // Box Break Delay: Delay for xxxx Before Printing Remaining Text + case 0x1C: // Fade: Keep Text on Screen for xxxx Before Closing + case 0x1D: // Fade Skippable: Delay for xxxx Before Ending Conversation + case 0x1E: // SFX: Play Sound Effect xxxx + case 0x1F: // Delay: Delay for xxxx Before Resuming Text Flow + msgEntry.msg += rawData[msgPtr + 1]; + msgEntry.msg += rawData[msgPtr + 2]; + msgPtr += 2; + break; + } + + msgPtr++; + c = rawData[msgPtr]; + } + } + + messages.push_back(msgEntry); + + if (msgEntry.id == 0xFFFC || msgEntry.id == 0xFFFF) + break; + + currentPtr += 8; + + if (isPalLang) + langPtr += 4; + else + langPtr += 8; + } +} + +std::string ZTextMM::GetSourceTypeName() const +{ + return "u8"; +} + +size_t ZTextMM::GetRawDataSize() const +{ + return 1; +} + +ZResourceType ZTextMM::GetResourceType() const +{ + return ZResourceType::TextMM; +} diff --git a/ZAPDTR/ZAPD/ZTextMM.h b/ZAPDTR/ZAPD/ZTextMM.h new file mode 100644 index 000000000..48c7d7844 --- /dev/null +++ b/ZAPDTR/ZAPD/ZTextMM.h @@ -0,0 +1,36 @@ +#pragma once + +#include "ZResource.h" +#include "tinyxml2.h" + +class MessageEntryMM +{ +public: + uint16_t id; + uint8_t textboxType; + uint8_t textboxYPos; + uint16_t icon; + uint16_t nextMessageID; + uint16_t firstItemCost; + uint16_t secondItemCost; + uint32_t segmentId; + uint32_t msgOffset; + std::string msg; +}; + +class ZTextMM : public ZResource +{ +public: + std::vector messages; + + ZTextMM(ZFile* nParent); + + void ParseRawData() override; + + void ParseMM(); + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZTexture.cpp b/ZAPDTR/ZAPD/ZTexture.cpp new file mode 100644 index 000000000..7d939f75c --- /dev/null +++ b/ZAPDTR/ZAPD/ZTexture.cpp @@ -0,0 +1,1013 @@ +#include "ZTexture.h" + +#include + +#include "CRC32.h" +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/Directory.h" +#include +#include "Utils/Path.h" +#include "WarningHandler.h" + +// OTRTODO put in LUS + +#define ALIGN8(val) (((val) + 7) & ~7) +#define ALIGN16(val) (((val) + 0xF) & ~0xF) +#define ALIGN64(val) (((val) + 0x3F) & ~0x3F) + +REGISTER_ZFILENODE(Texture, ZTexture); + +ZTexture::ZTexture(ZFile* nParent) : ZResource(nParent) +{ + width = 0; + height = 0; + dWordAligned = true; + genOTRDef = true; + splitTlut = false; + + RegisterRequiredAttribute("Width"); + RegisterRequiredAttribute("Height"); + RegisterRequiredAttribute("Format"); + RegisterOptionalAttribute("TlutOffset"); + RegisterOptionalAttribute("ExternalTlut"); + RegisterOptionalAttribute("ExternalTlutOffset"); + RegisterOptionalAttribute("SplitTlut"); + + // Dummy property added by https://github.com/HarbourMasters/Shipwright/pull/3161 + // Used to indicate if a resource definition was added through a script + // and to enable easy removal/re-add of the definitions when introducing new rom support + // Can be removed once we feel it is no longer useful + // This is not used in ZAPD itself, the registration is to prevent missing attribute errors + RegisterOptionalAttribute("AddedByScript"); +} + +void ZTexture::ExtractFromBinary(uint32_t nRawDataIndex, int32_t nWidth, int32_t nHeight, + TextureType nType, bool nIsPalette) +{ + width = nWidth; + height = nHeight; + format = nType; + rawDataIndex = nRawDataIndex; + isPalette = nIsPalette; + name = GetDefaultName(parent->GetName()); + outName = name; + + // Don't parse raw data of external files + if (parent->GetMode() == ZFileMode::ExternalFile) + return; + + ParseRawData(); + CalcHash(); +} + +void ZTexture::FromPNG(const fs::path& pngFilePath, TextureType texType) +{ + format = texType; + name = StringHelper::Split(Path::GetFileNameWithoutExtension(pngFilePath.string()), ".")[0]; + PrepareRawDataFromFile(pngFilePath); +} + +void ZTexture::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + std::string widthXml = registeredAttributes.at("Width").value; + std::string heightXml = registeredAttributes.at("Height").value; + std::string SplitTlutXml = registeredAttributes.at("SplitTlut").value; + + if (!StringHelper::HasOnlyDigits(widthXml)) + { + std::string errorHeader = StringHelper::Sprintf( + "value of 'Width' attribute has non-decimal digits: '%s'", widthXml.c_str()); + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + errorHeader, ""); + } + if (!StringHelper::HasOnlyDigits(heightXml)) + { + std::string errorHeader = StringHelper::Sprintf( + "value of 'Height' attribute has non-decimal digits: '%s'", heightXml.c_str()); + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + errorHeader, ""); + } + + if (!registeredAttributes.at("ExternalTlut").wasSet && + registeredAttributes.at("SplitTlut").wasSet) + { + std::string errorHeader = + StringHelper::Sprintf("SplitTlut set without using an external tlut"); + HANDLE_WARNING_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + errorHeader, ""); + } + + if (!SplitTlutXml.empty()) + { + if (!tinyxml2::XMLUtil::ToBool(SplitTlutXml.c_str(), &splitTlut)) + { + std::string errorHeader = StringHelper::Sprintf( + "Invalid value passed to SplitTlut: '%s'. Valid values are true, false, 1, 0", + SplitTlutXml.c_str()); + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + errorHeader, ""); + } + } + + width = StringHelper::StrToL(widthXml); + height = StringHelper::StrToL(heightXml); + + std::string formatStr = registeredAttributes.at("Format").value; + format = GetTextureTypeFromString(formatStr); + + if (format == TextureType::Error) + { + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + "invalid value found for 'Format' attribute", ""); + } + + const auto& tlutOffsetAttr = registeredAttributes.at("TlutOffset"); + if (tlutOffsetAttr.wasSet) + { + switch (format) + { + case TextureType::Palette4bpp: + case TextureType::Palette8bpp: + tlutOffset = StringHelper::StrToL(tlutOffsetAttr.value, 16); + break; + + default: + HANDLE_ERROR_RESOURCE(WarningType::InvalidXML, parent, this, rawDataIndex, + "'TlutOffset' declared in non color-indexed (ci4 or ci8) texture", + ""); + break; + } + } +} + +void ZTexture::ParseRawData() +{ + if (rawDataIndex % 8 != 0) + dWordAligned = false; + + switch (format) + { + case TextureType::RGBA16bpp: + ConvertN64ToBitmap_RGBA16(); + break; + case TextureType::RGBA32bpp: + ConvertN64ToBitmap_RGBA32(); + break; + case TextureType::Grayscale4bpp: + ConvertN64ToBitmap_Grayscale4(); + break; + case TextureType::Grayscale8bpp: + ConvertN64ToBitmap_Grayscale8(); + break; + case TextureType::GrayscaleAlpha4bpp: + ConvertN64ToBitmap_GrayscaleAlpha4(); + break; + case TextureType::GrayscaleAlpha8bpp: + ConvertN64ToBitmap_GrayscaleAlpha8(); + break; + case TextureType::GrayscaleAlpha16bpp: + ConvertN64ToBitmap_GrayscaleAlpha16(); + break; + case TextureType::Palette4bpp: + ConvertN64ToBitmap_Palette4(); + break; + case TextureType::Palette8bpp: + ConvertN64ToBitmap_Palette8(); + break; + case TextureType::Error: + HANDLE_ERROR_RESOURCE(WarningType::InvalidAttributeValue, parent, this, rawDataIndex, + StringHelper::Sprintf("Invalid texture format", format), ""); + assert(!"TODO"); + break; + } +} + +void ZTexture::ParseRawDataLate() +{ + if (registeredAttributes["ExternalTlut"].wasSet) + { + const std::string externPalette = registeredAttributes["ExternalTlut"].value; + for (const auto& file : Globals::Instance->files) + { + if (file->GetName() == externPalette) + { + offset_t palOffset = 0; + if (registeredAttributes["ExternalTlutOffset"].wasSet) + { + palOffset = + StringHelper::StrToL(registeredAttributes["ExternalTlutOffset"].value, 16); + } + else + { + HANDLE_WARNING_RESOURCE( + WarningType::MissingOffsets, parent, this, rawDataIndex, + StringHelper::Sprintf( + "No ExternalTlutOffset Given. Assuming offset of 0x0"), + ""); + } + for (const auto& res : file->resources) + { + if (res->GetRawDataIndex() == palOffset) + { + ZTexture* palette = (ZTexture*)res; + ZTexture tlutTemp(file); + + tlut = &tlutTemp; + tlut->ExtractFromBinary(palOffset, palette->width, palette->height, + TextureType::RGBA16bpp, true); + SetTlut(tlut); + } + } + } + } + } +} + +void ZTexture::ConvertN64ToBitmap_RGBA16() +{ + textureData.InitEmptyRGBImage(width, height, true); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + int32_t pos = rawDataIndex + ((y * width) + x) * 2; + uint16_t data = parentRawData.at(pos + 1) | (parentRawData.at(pos) << 8); + uint8_t r = (data & 0xF800) >> 11; + uint8_t g = (data & 0x07C0) >> 6; + uint8_t b = (data & 0x003E) >> 1; + uint8_t alpha = data & 0x01; + + textureData.SetRGBPixel(y, x, (r << 3) | (r >> 2), (g << 3) | (g >> 2), + (b << 3) | (b >> 2), alpha * 255); + } + } +} + +void ZTexture::ConvertN64ToBitmap_RGBA32() +{ + textureData.InitEmptyRGBImage(width, height, true); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + size_t pos = rawDataIndex + ((y * width) + x) * 4; + uint8_t r = parentRawData.at(pos + 0); + uint8_t g = parentRawData.at(pos + 1); + uint8_t b = parentRawData.at(pos + 2); + uint8_t alpha = parentRawData.at(pos + 3); + + textureData.SetRGBPixel(y, x, r, g, b, alpha); + } + } +} + +void ZTexture::ConvertN64ToBitmap_Grayscale4() +{ + textureData.InitEmptyRGBImage(width, height, false); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x += 2) + { + for (uint8_t i = 0; i < 2; i++) + { + size_t pos = rawDataIndex + ((y * width) + x) / 2; + uint8_t grayscale = 0; + + if (i == 0) + grayscale = parentRawData.at(pos) & 0xF0; + else + grayscale = (parentRawData.at(pos) & 0x0F) << 4; + + textureData.SetGrayscalePixel(y, x + i, (grayscale << 4) | grayscale); + } + } + } +} + +void ZTexture::ConvertN64ToBitmap_Grayscale8() +{ + textureData.InitEmptyRGBImage(width, height, false); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + size_t pos = rawDataIndex + ((y * width) + x) * 1; + + textureData.SetGrayscalePixel(y, x, parentRawData.at(pos)); + } + } +} + +void ZTexture::ConvertN64ToBitmap_GrayscaleAlpha4() +{ + textureData.InitEmptyRGBImage(width, height, true); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x += 2) + { + for (uint16_t i = 0; i < 2; i++) + { + size_t pos = rawDataIndex + ((y * width) + x) / 2; + uint8_t data = 0; + + if (i == 0) + data = (parentRawData.at(pos) & 0xF0) >> 4; + else + data = parentRawData.at(pos) & 0x0F; + + uint8_t grayscale = data & 0b1110; + grayscale = (grayscale << 4) | (grayscale << 1) | (grayscale >> 2); + uint8_t alpha = (data & 0x01) ? 255 : 0; + + textureData.SetGrayscalePixel(y, x + i, grayscale, alpha); + } + } + } +} + +void ZTexture::ConvertN64ToBitmap_GrayscaleAlpha8() +{ + textureData.InitEmptyRGBImage(width, height, true); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + size_t pos = rawDataIndex + ((y * width) + x) * 1; + uint8_t pixel = parentRawData.at(pos); + uint8_t data = (pixel >> 4) & 0xF; + + data = (data << 4) | data; + uint8_t grayscale = data; + uint8_t alpha = (pixel & 0xF); + alpha = (alpha << 4) | alpha; + + textureData.SetGrayscalePixel(y, x, grayscale, alpha); + } + } +} + +void ZTexture::ConvertN64ToBitmap_GrayscaleAlpha16() +{ + textureData.InitEmptyRGBImage(width, height, true); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + size_t pos = rawDataIndex + ((y * width) + x) * 2; + uint8_t grayscale = parentRawData.at(pos + 0); + uint8_t alpha = parentRawData.at(pos + 1); + + textureData.SetGrayscalePixel(y, x, grayscale, alpha); + } + } +} + +void ZTexture::ConvertN64ToBitmap_Palette4() +{ + textureData.InitEmptyPaletteImage(width, height); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x += 2) + { + for (uint16_t i = 0; i < 2; i++) + { + size_t pos = rawDataIndex + ((y * width) + x) / 2; + uint8_t paletteIndex = 0; + + if (i == 0) + paletteIndex = (parentRawData.at(pos) & 0xF0) >> 4; + else + paletteIndex = (parentRawData.at(pos) & 0x0F); + + textureData.SetIndexedPixel(y, x + i, paletteIndex, paletteIndex * 16); + } + } + } +} + +void ZTexture::ConvertN64ToBitmap_Palette8() +{ + textureData.InitEmptyPaletteImage(width, height); + const auto& parentRawData = parent->GetRawData(); + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + size_t pos = rawDataIndex + ((y * width) + x) * 1; + uint8_t grayscale = parentRawData.at(pos); + + textureData.SetIndexedPixel(y, x, grayscale, grayscale); + } + } +} + +void ZTexture::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ + if (tlutOffset != static_cast(-1)) + { + tlut = parent->GetTextureResource(tlutOffset); + if (tlut == nullptr) + { + int32_t tlutDim = 16; + if (format == TextureType::Palette4bpp) + tlutDim = 4; + + tlut = new ZTexture(parent); + tlut->ExtractFromBinary(tlutOffset, tlutDim, tlutDim, TextureType::RGBA16bpp, true); + parent->AddTextureResource(tlutOffset, tlut); + tlut->DeclareVar(prefix, ""); + } + else + { + tlut->isPalette = true; + } + SetTlut(tlut); + } +} + +void ZTexture::PrepareRawDataFromFile(const fs::path& pngFilePath) +{ + textureData.ReadPng(pngFilePath); + + width = textureData.GetWidth(); + height = textureData.GetHeight(); + + textureDataRaw.clear(); + textureDataRaw.resize(ALIGN8(GetRawDataSize())); + + switch (format) + { + case TextureType::RGBA16bpp: + ConvertBitmapToN64_RGBA16(); + break; + case TextureType::RGBA32bpp: + ConvertBitmapToN64_RGBA32(); + break; + case TextureType::Grayscale4bpp: + ConvertBitmapToN64_Grayscale4(); + break; + case TextureType::Grayscale8bpp: + ConvertBitmapToN64_Grayscale8(); + break; + case TextureType::GrayscaleAlpha4bpp: + ConvertBitmapToN64_GrayscaleAlpha4(); + break; + case TextureType::GrayscaleAlpha8bpp: + ConvertBitmapToN64_GrayscaleAlpha8(); + break; + case TextureType::GrayscaleAlpha16bpp: + ConvertBitmapToN64_GrayscaleAlpha16(); + break; + case TextureType::Palette4bpp: + ConvertBitmapToN64_Palette4(); + break; + case TextureType::Palette8bpp: + ConvertBitmapToN64_Palette8(); + break; + case TextureType::Error: + HANDLE_ERROR_PROCESS(WarningType::InvalidPNG, "Input PNG file has invalid format type", ""); + break; + } +} + +void ZTexture::ConvertBitmapToN64_RGBA16() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x++) + { + size_t pos = ((y * width) + x) * 2; + RGBAPixel pixel = textureData.GetPixel(y, x); + + uint8_t r = pixel.r >> 3; + uint8_t g = pixel.g >> 3; + uint8_t b = pixel.b >> 3; + + uint8_t alphaBit = pixel.a != 0; + + uint16_t data = (r << 11) | (g << 6) | (b << 1) | alphaBit; + + textureDataRaw[pos + 0] = (data & 0xFF00) >> 8; + textureDataRaw[pos + 1] = (data & 0x00FF); + } + } +} + +void ZTexture::ConvertBitmapToN64_RGBA32() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x++) + { + size_t pos = ((y * width) + x) * 4; + RGBAPixel pixel = textureData.GetPixel(y, x); + + textureDataRaw[pos + 0] = pixel.r; + textureDataRaw[pos + 1] = pixel.g; + textureDataRaw[pos + 2] = pixel.b; + textureDataRaw[pos + 3] = pixel.a; + } + } +} + +void ZTexture::ConvertBitmapToN64_Grayscale4() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x += 2) + { + size_t pos = ((y * width) + x) / 2; + uint8_t r1 = textureData.GetPixel(y, x).r; + uint8_t r2 = textureData.GetPixel(y, x + 1).r; + + textureDataRaw[pos] = (uint8_t)(((r1 / 16) << 4) + (r2 / 16)); + } + } +} + +void ZTexture::ConvertBitmapToN64_Grayscale8() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x++) + { + size_t pos = (y * width) + x; + RGBAPixel pixel = textureData.GetPixel(y, x); + textureDataRaw[pos] = pixel.r; + } + } +} + +void ZTexture::ConvertBitmapToN64_GrayscaleAlpha4() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x += 2) + { + size_t pos = ((y * width) + x) / 2; + uint8_t data = 0; + + for (uint16_t i = 0; i < 2; i++) + { + RGBAPixel pixel = textureData.GetPixel(y, x + i); + uint8_t cR = pixel.r; + uint8_t alphaBit = pixel.a != 0; + + if (i == 0) + data = (((cR >> 5) << 1) | alphaBit) << 4; + else + data |= ((cR >> 5) << 1) | alphaBit; + } + + textureDataRaw[pos] = data; + } + } +} + +void ZTexture::ConvertBitmapToN64_GrayscaleAlpha8() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x++) + { + size_t pos = ((y * width) + x) * 1; + RGBAPixel pixel = textureData.GetPixel(y, x); + + uint8_t r = (pixel.r >> 4) & 0xF; + uint8_t a = (pixel.a >> 4) & 0xF; + + textureDataRaw[pos] = (r << 4) | a; + } + } +} + +void ZTexture::ConvertBitmapToN64_GrayscaleAlpha16() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x++) + { + size_t pos = ((y * width) + x) * 2; + RGBAPixel pixel = textureData.GetPixel(y, x); + + uint8_t cR = pixel.r; + uint8_t aR = pixel.a; + + textureDataRaw[pos + 0] = cR; + textureDataRaw[pos + 1] = aR; + } + } +} + +void ZTexture::ConvertBitmapToN64_Palette4() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x += 2) + { + size_t pos = ((y * width) + x) / 2; + + uint8_t cR1 = textureData.GetIndexedPixel(y, x); + uint8_t cR2 = textureData.GetIndexedPixel(y, x + 1); + + textureDataRaw[pos] = (cR1 << 4) | (cR2); + } + } +} + +void ZTexture::ConvertBitmapToN64_Palette8() +{ + for (uint16_t y = 0; y < height; y++) + { + for (uint16_t x = 0; x < width; x++) + { + size_t pos = ((y * width) + x); + uint8_t cR = textureData.GetIndexedPixel(y, x); + + textureDataRaw[pos] = cR; + } + } +} + +float ZTexture::GetPixelMultiplyer() const +{ + switch (format) + { + case TextureType::Grayscale4bpp: + case TextureType::GrayscaleAlpha4bpp: + case TextureType::Palette4bpp: + return 0.5f; + case TextureType::Grayscale8bpp: + case TextureType::GrayscaleAlpha8bpp: + case TextureType::Palette8bpp: + return 1; + case TextureType::GrayscaleAlpha16bpp: + case TextureType::RGBA16bpp: + return 2; + case TextureType::RGBA32bpp: + return 4; + default: + return -1; + } +} + +size_t ZTexture::GetRawDataSize() const +{ + return (width * height * GetPixelMultiplyer()); +} + +std::string ZTexture::GetIMFmtFromType() +{ + switch (format) + { + case TextureType::RGBA32bpp: + case TextureType::RGBA16bpp: + return "G_IM_FMT_RGBA"; + case TextureType::Grayscale4bpp: + case TextureType::Grayscale8bpp: + return "G_IM_FMT_I"; + case TextureType::Palette4bpp: + case TextureType::Palette8bpp: + return "G_IM_FMT_CI"; + case TextureType::GrayscaleAlpha4bpp: + case TextureType::GrayscaleAlpha8bpp: + case TextureType::GrayscaleAlpha16bpp: + return "G_IM_FMT_IA"; + default: + return "ERROR"; + } +} + +std::string ZTexture::GetIMSizFromType() +{ + switch (format) + { + case TextureType::Grayscale4bpp: + case TextureType::Palette4bpp: + case TextureType::GrayscaleAlpha4bpp: + return "G_IM_SIZ_4b"; + case TextureType::Palette8bpp: + case TextureType::Grayscale8bpp: + return "G_IM_SIZ_8b"; + case TextureType::GrayscaleAlpha16bpp: + case TextureType::RGBA16bpp: + return "G_IM_SIZ_16b"; + case TextureType::RGBA32bpp: + return "G_IM_SIZ_32b"; + default: + return "ERROR"; + } +} + +std::string ZTexture::GetDefaultName(const std::string& prefix) const +{ + const char* suffix = "Tex"; + if (isPalette) + suffix = "TLUT"; + return StringHelper::Sprintf("%s%s_%06X", prefix.c_str(), suffix, rawDataIndex); +} + +uint32_t ZTexture::GetWidth() const +{ + return width; +} + +uint32_t ZTexture::GetHeight() const +{ + return height; +} + +void ZTexture::SetDimensions(uint32_t nWidth, uint32_t nHeight) +{ + width = nWidth; + height = nHeight; + ParseRawData(); +} + +TextureType ZTexture::GetTextureType() const +{ + return format; +} + +void ZTexture::Save(const fs::path& outFolder) +{ + // Optionally generate text file containing CRC information. This is going to be a one time + // process for generating the Texture Pool XML. + if (Globals::Instance->outputCrc) + { + DiskFile::WriteAllText((Globals::Instance->outputPath / (outName + ".txt")).string(), + StringHelper::Sprintf("%08lX", hash)); + } + + // Do not save png files if we're making an OTR file. They're not needed... + if (Globals::Instance->otrMode) + return; + + auto outPath = GetPoolOutPath(outFolder); + + if (!Directory::Exists(outPath.string())) + Directory::CreateDirectory(outPath.string()); + + fs::path outFileName; + + if (!dWordAligned) + outFileName = outPath / (outName + ".u32" + "." + GetExternalExtension() + ".png"); + else + outFileName = outPath / (outName + +"." + GetExternalExtension() + ".png"); + +#ifdef TEXTURE_DEBUG + printf("Saving PNG: %s\n", outFileName.c_str()); + printf("\t Var name: %s\n", name.c_str()); + if (tlut != nullptr) + printf("\t TLUT name: %s\n", tlut->name.c_str()); +#endif + + textureData.WritePng(outFileName); + +#ifdef TEXTURE_DEBUG + printf("\n"); +#endif +} + +Declaration* ZTexture::DeclareVar(const std::string& prefix, + [[maybe_unused]] const std::string& bodyStr) +{ + std::string auxName = name; + std::string auxOutName = outName; + std::string incStr; + + //if (Globals::Instance->otrMode) + //return nullptr; + + if (auxName == "") + auxName = GetDefaultName(prefix); + + if (auxOutName == "") + auxOutName = GetDefaultName(prefix); + + auto filepath = Globals::Instance->outputPath / fs::path(auxOutName).stem(); + + if (dWordAligned) + incStr = StringHelper::Sprintf("%s.%s.inc.c", filepath.string().c_str(), + GetExternalExtension().c_str()); + else + incStr = StringHelper::Sprintf("%s.u32.%s.inc.c", filepath.string().c_str(), + GetExternalExtension().c_str()); + + if (!Globals::Instance->cfg.texturePool.empty()) + { + CalcHash(); + + // TEXTURE POOL CHECK + const auto& poolEntry = Globals::Instance->cfg.texturePool.find(hash); + if (poolEntry != Globals::Instance->cfg.texturePool.end()) + { + if (dWordAligned) + incStr = + StringHelper::Sprintf("%s.%s.inc.c", poolEntry->second.path.string().c_str(), + GetExternalExtension().c_str()); + else + incStr = StringHelper::Sprintf("%s.u32.%s.inc.c", + poolEntry->second.path.string().c_str(), + GetExternalExtension().c_str()); + } + } + size_t texSizeDivisor = (dWordAligned) ? 8 : 4; + + Declaration* decl; + + if (parent->makeDefines) + { + decl = parent->AddDeclarationIncludeArray(rawDataIndex, incStr, GetRawDataSize(), + GetSourceTypeName(), auxName, GetHeaderDefines(), + GetRawDataSize() / texSizeDivisor); + } + else + { + decl = parent->AddDeclarationIncludeArray(rawDataIndex, incStr, GetRawDataSize(), + GetSourceTypeName(), auxName, + GetRawDataSize() / texSizeDivisor); + } + decl->staticConf = staticConf; + return decl; +} + +std::string ZTexture::GetBodySourceCode() const +{ + std::string sourceOutput; + + if (!Globals::Instance->otrMode) + { + size_t texSizeInc = (dWordAligned) ? 8 : 4; + for (size_t i = 0; i < textureDataRaw.size(); i += texSizeInc) + { + if (i % 32 == 0) + sourceOutput += " "; + if (dWordAligned) + sourceOutput += + StringHelper::Sprintf("0x%016llX, ", BitConverter::ToUInt64BE(textureDataRaw, i)); + else + sourceOutput += + StringHelper::Sprintf("0x%08llX, ", BitConverter::ToUInt32BE(textureDataRaw, i)); + if (i % 32 == 24) + sourceOutput += StringHelper::Sprintf(" // 0x%06X \n", rawDataIndex + ((i / 32) * 32)); + } + + // Ensure there's always a trailing line feed to prevent dumb warnings. + // Please don't remove this line, unless you somehow made a way to prevent + // that warning when building the OoT repo. + sourceOutput += "\n"; + } else if (Globals::Instance->buildRawTexture) { + sourceOutput += std::string(textureDataRaw.begin(), textureDataRaw.end()); + } + + return sourceOutput; +} + +std::string ZTexture::GetHeaderDefines() const +{ + // OTRTODO add to LUS + #if 0 + std::string definePrefix = StringHelper::camelCaseTo_SCREAMING_SNAKE_CASE(name.c_str(), true); + std::string ret = StringHelper::Sprintf("#define %s_WIDTH %d\n", definePrefix.c_str(), width); + + ret += StringHelper::Sprintf("#define %s_HEIGHT %d\n", definePrefix.c_str(), height); + ret += StringHelper::Sprintf("#define %s_SIZE 0x%zX\n", definePrefix.c_str(), GetRawDataSize()); + + return ret; + #endif + return ""; +} + +bool ZTexture::IsExternalResource() const +{ + return true; +} + +ZResourceType ZTexture::GetResourceType() const +{ + return ZResourceType::Texture; +} + +std::string ZTexture::GetSourceTypeName() const +{ + return dWordAligned ? "u64" : "u32"; +} + +void ZTexture::CalcHash() +{ + //if (hash == 0) + { + const auto& parentRawData = parent->GetRawData(); + hash = CRC32B(parentRawData.data() + rawDataIndex, GetRawDataSize()); + } +} + +std::string ZTexture::GetExternalExtension() const +{ + switch (format) + { + case TextureType::RGBA32bpp: + return "rgba32"; + case TextureType::RGBA16bpp: + return "rgba16"; + case TextureType::Grayscale4bpp: + return "i4"; + case TextureType::Grayscale8bpp: + return "i8"; + case TextureType::GrayscaleAlpha4bpp: + return "ia4"; + case TextureType::GrayscaleAlpha8bpp: + return "ia8"; + case TextureType::GrayscaleAlpha16bpp: + return "ia16"; + case TextureType::Palette4bpp: + return "ci4"; + case TextureType::Palette8bpp: + return "ci8"; + default: + return "ERROR"; + } +} + +fs::path ZTexture::GetPoolOutPath(const fs::path& defaultValue) +{ + if (Globals::Instance->cfg.texturePool.find(hash) != Globals::Instance->cfg.texturePool.end()) + return Path::GetDirectoryName(Globals::Instance->cfg.texturePool[hash].path.string()); + + return defaultValue; +} + +TextureType ZTexture::GetTextureTypeFromString(const std::string& str) +{ + TextureType texType = TextureType::Error; + + if (str == "rgba32") + texType = TextureType::RGBA32bpp; + else if (str == "rgba16") + texType = TextureType::RGBA16bpp; + else if (str == "rgb5a1") + { + texType = TextureType::RGBA16bpp; + HANDLE_WARNING(WarningType::Deprecated, + "the texture format 'rgb5a1' is currently deprecated", + "It will be removed in a future version. Use the format 'rgba16' instead."); + } + else if (str == "i4") + texType = TextureType::Grayscale4bpp; + else if (str == "i8") + texType = TextureType::Grayscale8bpp; + else if (str == "ia4") + texType = TextureType::GrayscaleAlpha4bpp; + else if (str == "ia8") + texType = TextureType::GrayscaleAlpha8bpp; + else if (str == "ia16") + texType = TextureType::GrayscaleAlpha16bpp; + else if (str == "ci4") + texType = TextureType::Palette4bpp; + else if (str == "ci8") + texType = TextureType::Palette8bpp; + else + // TODO: handle this case in a more coherent way + HANDLE_WARNING(WarningType::InvalidAttributeValue, + "invalid value found for 'Type' attribute", "Defaulting to ''."); + return texType; +} + +bool ZTexture::IsColorIndexed() const +{ + switch (format) + { + case TextureType::Palette4bpp: + case TextureType::Palette8bpp: + return true; + + default: + return false; + } +} + +void ZTexture::SetTlut(ZTexture* nTlut) +{ + assert(IsColorIndexed()); + assert(nTlut->isPalette); + tlut = nTlut; + + textureData.SetPalette(tlut->textureData, splitTlut ? 128 : 0); +} + +bool ZTexture::HasTlut() const +{ + return tlut != nullptr; +} diff --git a/ZAPDTR/ZAPD/ZTexture.h b/ZAPDTR/ZAPD/ZTexture.h new file mode 100644 index 000000000..1461ff95a --- /dev/null +++ b/ZAPDTR/ZAPD/ZTexture.h @@ -0,0 +1,119 @@ +#pragma once + +#include "ImageBackend.h" +#include "ZResource.h" +#include "tinyxml2.h" + +enum class TextureType +{ + Error, + RGBA32bpp, + RGBA16bpp, + Palette4bpp, + Palette8bpp, + Grayscale4bpp, + Grayscale8bpp, + GrayscaleAlpha4bpp, + GrayscaleAlpha8bpp, + GrayscaleAlpha16bpp, +}; + +class ZTexture : public ZResource +{ +protected: + TextureType format = TextureType::Error; + uint32_t width, height; + + ImageBackend textureData; + std::vector textureDataRaw; // When reading from a PNG file. + uint32_t tlutOffset = static_cast(-1); + ZTexture* tlut = nullptr; + bool splitTlut; + + // The following functions convert from N64 binary data to a bitmap to be saved to a PNG. + void ConvertN64ToBitmap_RGBA16(); + void ConvertN64ToBitmap_RGBA32(); + void ConvertN64ToBitmap_Grayscale8(); + void ConvertN64ToBitmap_GrayscaleAlpha8(); + void ConvertN64ToBitmap_Grayscale4(); + void ConvertN64ToBitmap_GrayscaleAlpha4(); + void ConvertN64ToBitmap_GrayscaleAlpha16(); + void ConvertN64ToBitmap_Palette4(); + void ConvertN64ToBitmap_Palette8(); + + // The following functions convert from a bitmap to N64 binary data. + void PrepareRawDataFromFile(const fs::path& inFolder); + void ConvertBitmapToN64_RGBA16(); + void ConvertBitmapToN64_RGBA32(); + void ConvertBitmapToN64_Grayscale4(); + void ConvertBitmapToN64_Grayscale8(); + void ConvertBitmapToN64_GrayscaleAlpha4(); + void ConvertBitmapToN64_GrayscaleAlpha8(); + void ConvertBitmapToN64_GrayscaleAlpha16(); + void ConvertBitmapToN64_Palette4(); + void ConvertBitmapToN64_Palette8(); + +public: + ZTexture(ZFile* nParent); + + bool isPalette = false; + bool dWordAligned = true; + + void ExtractFromBinary(uint32_t nRawDataIndex, int32_t nWidth, int32_t nHeight, + TextureType nType, bool nIsPalette); + void FromPNG(const fs::path& pngFilePath, TextureType texType); + static TextureType GetTextureTypeFromString(const std::string& str); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + /// + /// Calculates the hash of this texture, for use with the texture pool. + /// + void CalcHash() override; + + void Save(const fs::path& outFolder) override; + + std::string GetHeaderDefines() const; + bool IsExternalResource() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + std::string GetExternalExtension() const override; + + size_t GetRawDataSize() const override; + std::string GetIMFmtFromType(); + std::string GetIMSizFromType(); + std::string GetDefaultName(const std::string& prefix) const override; + uint32_t GetWidth() const; + uint32_t GetHeight() const; + void SetDimensions(uint32_t nWidth, uint32_t nHeight); + + /// + /// Returns how many bytes each pixel takes up. + /// + /// + float GetPixelMultiplyer() const; + + TextureType GetTextureType() const; + + /// + /// Returns the path to the texture pool, taken from the config file. + /// + /// + /// + fs::path GetPoolOutPath(const fs::path& defaultValue); + + /// + /// Returns if this texture uses a palette. + /// + /// + bool IsColorIndexed() const; + + void SetTlut(ZTexture* nTlut); + bool HasTlut() const; + void ParseRawDataLate() override; +}; diff --git a/ZAPDTR/ZAPD/ZTextureAnimation.cpp b/ZAPDTR/ZAPD/ZTextureAnimation.cpp new file mode 100644 index 000000000..07ae223f3 --- /dev/null +++ b/ZAPDTR/ZAPD/ZTextureAnimation.cpp @@ -0,0 +1,679 @@ +/** + * File: ZTextureAnimation.cpp + * ZResources defined: ZTextureAnimation, ZTextureAnimationParams (XML declaration not supported for + * the latter) + * Purpose: extracting texture animating structures from asset files + * Note: data type is exclusive to Majora's Mask + * + * Structure of data: + * A texture animation consists of a main array of data of the form + * `SS 00 00 0T PPPPPPPP`, + * where `S` is the segment that the functionality is sent to (and read by DLists), `T` is the + * "type" (see below), and `P` is a pointer to the type's subsidiary information, which is + * invariably just above this main array. Details of this subsidiary data are given below. + * + * Segment + * === + * The segment starts at 1 and is incremented in each entry; the final one is negated, which is used + * as the indication to stop reading. The actual segment the game will use to draw is S + 7. + * + * Types + * === + * There are 7 values that `T` can take: + * `0`: A single texture scroll (Implemented by Gfx_TexScroll): subsidiary params are given as + * an array with one entry, a struct `XX YY WW HH` containing xStep, yStep, width, height + * (all u8). + * + * `1`: A dual texture scroll (Implementated by Gfx_TwoTexScroll): same as type `0`, but with + * two entries in the params array. + * + * `2`: Color changing: Changes the primColor (with a LOD factor) and envColor + * `KKKK LLLL PPPPPPPP QQQQQQQQ RRRRRRRR` + * - `K` (u16) is the total length of the animation (and in this case, the total number of + * colors) + * - `L` (u16) is seemingly always 0 for this type, and not used in the code + * - `P` segmented pointer to array of augmented primColors + * - `Q` segmented pointer to array of envColors, and can be NULL + * - `R` segmented pointer to array of frameData (u8), which is also seemingly always NULL + * for this type. + * + * envColors take the form `RR GG BB AA` as usual, while primColors have an extra LODFrac + * element: RR GG BB AA LL + * + * `3`: Color changing (LERP): similar to type `2`, but uses linear interpolation. The structure + * is similar to `2`, but + * - `K` is now just the animation length, while + * - `L` is the total number of colors, and + * - the frameData is used to determine which colors to interpolate. + * + * `4`: Color changing (Lagrange interpolation): For extraction purposes identical to type 3. + * Uses a nonlinear interpolation formula to change the colours more smoothly. + * + * `5`: Texture cycle: functions like a gif. Subsidiary params: + * `FFFF 0000 PPPPPPPP QQQQQQQQ` + * where + * - `F` (u16) time between changes in frames + * - `P` pointer to array of segmented pointers to textures to cycle through + * - `Q` array of indices, indicating which texture to use next when a change frame is + * reached. The maximum indicates the number of textures in the texture array. + * + * `6`: This is used to indicate an empty/unimplemented params set. It is ignored by the game's + * function provided that the segment is 0. A generic empty one takes the form + * `00 00 00 06 00000000`, + * and has no extra data. + * + * Implementation + * === + * - ZTextureAnimation requires a declaration in the XML to extract anything. It handles the main + * array. It uses a POD struct for each entry, and declares references to the params structs. + * + * - ZTextureAnimationParams is not (currently) declarable in an XML. It is a parent class for the + * three classes that handle the various cases: + * - TextureScrollingParams for types `0` and `1` + * - TextureColorChangingParams for types `2`,`3`,`4` + * - TextureCyclingParams for type `5` + * Each of these will declare all its subsidiary arrays, using POD structs. + */ +#include "ZTextureAnimation.h" + +#include +#include +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "WarningHandler.h" +#include "ZFile.h" +#include "ZResource.h" +#include "tinyxml2.h" + +REGISTER_ZFILENODE(TextureAnimation, ZTextureAnimation); + +/* Constructors */ +ZTextureAnimationParams::ZTextureAnimationParams(ZFile* parent) : ZResource::ZResource(parent) +{ +} +TextureScrollingParams::TextureScrollingParams(ZFile* parent) + : ZTextureAnimationParams::ZTextureAnimationParams(parent) +{ +} +TextureColorChangingParams::TextureColorChangingParams(ZFile* parent) + : ZTextureAnimationParams::ZTextureAnimationParams(parent) +{ +} +TextureCyclingParams::TextureCyclingParams(ZFile* parent) + : ZTextureAnimationParams::ZTextureAnimationParams(parent) +{ +} + +/* TextureAnimationParams */ +/* This class only implements the functions common to all or most its inheritors */ + +void ZTextureAnimationParams::ExtractFromBinary(uint32_t nRawDataIndex) +{ + rawDataIndex = nRawDataIndex; + + ParseRawData(); +} + +// Implemented by TextureScrollingParams only +void ZTextureAnimationParams::ExtractFromBinary([[maybe_unused]] uint32_t nRawDataIndex, + [[maybe_unused]] int count) +{ +} + +std::string +ZTextureAnimationParams::GetDefaultName([[maybe_unused]] const std::string& prefix) const +{ + return "ShouldNotBeVIsible"; +} + +ZResourceType ZTextureAnimationParams::GetResourceType() const +{ + return ZResourceType::TextureAnimationParams; +} + +/* TextureScrollingParams */ + +void TextureScrollingParams::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + for (int i = 0; i < count; i++) + { + rows[i].xStep = BitConverter::ToInt8BE(rawData, rawDataIndex + 4 * i); + rows[i].yStep = BitConverter::ToInt8BE(rawData, rawDataIndex + 4 * i + 1); + rows[i].width = BitConverter::ToUInt8BE(rawData, rawDataIndex + 4 * i + 2); + rows[i].height = BitConverter::ToUInt8BE(rawData, rawDataIndex + 4 * i + 3); + } +} + +void TextureScrollingParams::ExtractFromBinary(uint32_t nRawDataIndex, int nCount) +{ + rawDataIndex = nRawDataIndex; + count = nCount; + + ParseRawData(); +} + +std::string TextureScrollingParams::GetSourceTypeName() const +{ + return "AnimatedMatTexScrollParams"; // TODO: Better name +} + +std::string TextureScrollingParams::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sTexScrollParams_%06X", prefix.c_str(), rawDataIndex); +} + +size_t TextureScrollingParams::GetRawDataSize() const +{ + return 4 * count; +} + +/** + * Overrides the parent version to declare an array of the params rather than just one entry. + */ +Declaration* TextureScrollingParams::DeclareVar(const std::string& prefix, + const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + return parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), auxName, count, bodyStr); +} + +std::string TextureScrollingParams::GetBodySourceCode() const +{ + std::string bodyStr; + + for (int i = 0; i < count; i++) + { + bodyStr += StringHelper::Sprintf("\t{ %d, %d, 0x%02X, 0x%02X },\n", rows[i].xStep, + rows[i].yStep, rows[i].width, rows[i].height); + } + + bodyStr.pop_back(); + + return bodyStr; +} + +/* TextureColorChangingParams */ + +/** + * Also parses the color and frameData arrays + */ +void TextureColorChangingParams::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + animLength = BitConverter::ToUInt16BE(rawData, rawDataIndex); + colorListCount = BitConverter::ToUInt16BE(rawData, rawDataIndex + 2); + + // Handle type 2 separately + uint16_t listLength = + ((type == TextureAnimationParamsType::ColorChange) ? animLength : colorListCount); + + if (listLength == 0) + HANDLE_ERROR_RESOURCE(WarningType::Always, parent, this, rawDataIndex, + "color list length cannot be 0", ""); + + primColorListAddress = BitConverter::ToUInt32BE(rawData, rawDataIndex + 4); + envColorListAddress = BitConverter::ToUInt32BE(rawData, rawDataIndex + 8); + frameDataListAddress = BitConverter::ToUInt32BE(rawData, rawDataIndex + 0xC); + + uint32_t primColorListOffset = Seg2Filespace(primColorListAddress, parent->baseAddress); + uint32_t envColorListOffset = Seg2Filespace(envColorListAddress, parent->baseAddress); + uint32_t frameDataListOffset = Seg2Filespace(frameDataListAddress, parent->baseAddress); + + uint32_t currentPtr; + + F3DPrimColor currentPrimColor; + + for (currentPtr = primColorListOffset; currentPtr < primColorListOffset + 5 * listLength; + currentPtr += 5) + { + currentPrimColor = {BitConverter::ToUInt8BE(rawData, currentPtr), + BitConverter::ToUInt8BE(rawData, currentPtr + 1), + BitConverter::ToUInt8BE(rawData, currentPtr + 2), + BitConverter::ToUInt8BE(rawData, currentPtr + 3), + BitConverter::ToUInt8BE(rawData, currentPtr + 4)}; + primColorList.push_back(currentPrimColor); + } + + F3DEnvColor currentEnvColor; + + for (currentPtr = envColorListOffset; currentPtr < envColorListOffset + 4 * listLength; + currentPtr += 4) + { + currentEnvColor = {BitConverter::ToUInt8BE(rawData, currentPtr), + BitConverter::ToUInt8BE(rawData, currentPtr + 1), + BitConverter::ToUInt8BE(rawData, currentPtr + 2), + BitConverter::ToUInt8BE(rawData, currentPtr + 3)}; + envColorList.push_back(currentEnvColor); + } + + uint16_t currentFrameData; + + for (currentPtr = frameDataListOffset; currentPtr < frameDataListOffset + 2 * listLength; + currentPtr += 2) + { + currentFrameData = BitConverter::ToUInt16BE(rawData, currentPtr); + frameDataList.push_back(currentFrameData); + } +} + +std::string TextureColorChangingParams::GetSourceTypeName() const +{ + return "AnimatedMatColorParams"; // TODO: Better name +} + +std::string TextureColorChangingParams::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sColorParams_%06X", prefix.c_str(), rawDataIndex); +} + +size_t TextureColorChangingParams::GetRawDataSize() const +{ + return 0x10; +} + +void TextureColorChangingParams::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ + if (primColorListAddress != 0) // NULL + { + std::string primColorBodyStr; + + for (const auto& color : primColorList) + { + primColorBodyStr += StringHelper::Sprintf("\t{ %d, %d, %d, %d, %d },\n", color.r, + color.g, color.b, color.a, color.lodFrac); + } + + primColorBodyStr.pop_back(); + + parent->AddDeclarationArray( + Seg2Filespace(primColorListAddress, parent->baseAddress), DeclarationAlignment::Align4, + primColorList.size() * 5, "F3DPrimColor", + StringHelper::Sprintf("%sTexColorChangingPrimColors_%06X", parent->GetName().c_str(), + Seg2Filespace(primColorListAddress, parent->baseAddress)), + primColorList.size(), primColorBodyStr); + } + + if (envColorListAddress != 0) // NULL + { + std::string envColorBodyStr; + + for (const auto& color : envColorList) + { + envColorBodyStr += StringHelper::Sprintf("\t{ %d, %d, %d, %d },\n", color.r, color.g, + color.b, color.a); + } + + envColorBodyStr.pop_back(); + + parent->AddDeclarationArray( + Seg2Filespace(envColorListAddress, parent->baseAddress), DeclarationAlignment::Align4, + envColorList.size() * 4, "F3DEnvColor", + StringHelper::Sprintf("%sTexColorChangingEnvColors_%06X", parent->GetName().c_str(), + Seg2Filespace(envColorListAddress, parent->baseAddress)), + envColorList.size(), envColorBodyStr); + } + + if (frameDataListAddress != 0) // NULL + { + std::string frameDataBodyStr = "\t"; + + for (const auto& frame : frameDataList) + { + frameDataBodyStr += StringHelper::Sprintf("%d, ", frame); + } + + frameDataBodyStr.pop_back(); + + parent->AddDeclarationArray( + Seg2Filespace(frameDataListAddress, parent->baseAddress), DeclarationAlignment::Align4, + frameDataList.size() * 2, "u16", + StringHelper::Sprintf("%sTexColorChangingFrameData_%06X", parent->GetName().c_str(), + Seg2Filespace(frameDataListAddress, parent->baseAddress)), + frameDataList.size(), frameDataBodyStr); + } +} + +std::string TextureColorChangingParams::GetBodySourceCode() const +{ + std::string primColorListName; + std::string envColorListName; + std::string frameDataListName; + + Globals::Instance->GetSegmentedPtrName(primColorListAddress, parent, "", primColorListName, + parent->workerID); + Globals::Instance->GetSegmentedPtrName(envColorListAddress, parent, "", envColorListName, + parent->workerID); + Globals::Instance->GetSegmentedPtrName(frameDataListAddress, parent, "", frameDataListName, + parent->workerID); + + std::string bodyStr = StringHelper::Sprintf( + "\n %d, %d, %s, %s, %s,\n", animLength, colorListCount, primColorListName.c_str(), + envColorListName.c_str(), frameDataListName.c_str()); + + return bodyStr; +} + +/* TextureCyclingParams */ + +void TextureCyclingParams::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + cycleLength = BitConverter::ToUInt16BE(rawData, rawDataIndex); + if (cycleLength == 0) + HANDLE_ERROR_RESOURCE(WarningType::Always, parent, this, rawDataIndex, + "cycle length cannot be 0", ""); + + textureListAddress = BitConverter::ToUInt32BE(rawData, rawDataIndex + 4); + textureIndexListAddress = BitConverter::ToUInt32BE(rawData, rawDataIndex + 8); + + uint32_t textureListOffset = Seg2Filespace(textureListAddress, parent->baseAddress); + uint32_t textureIndexListOffset = Seg2Filespace(textureIndexListAddress, parent->baseAddress); + + uint32_t currentPtr; + + uint8_t currentIndex; + uint8_t maxIndex = 0; // To find the length of the texture list + + for (currentPtr = textureIndexListOffset; currentPtr < textureIndexListOffset + cycleLength; + currentPtr++) + { + currentIndex = BitConverter::ToUInt8BE(rawData, currentPtr); + textureIndexList.push_back(currentIndex); + if (currentIndex > maxIndex) + maxIndex = currentIndex; + } + + for (currentPtr = textureListOffset; currentPtr <= textureListOffset + 4 * maxIndex; + currentPtr += 4) + { + textureList.push_back(BitConverter::ToUInt32BE(rawData, currentPtr)); + } +} + +std::string TextureCyclingParams::GetSourceTypeName() const +{ + return "AnimatedMatTexCycleParams"; // TODO: Better name +} + +std::string TextureCyclingParams::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sTexCycleParams_%06X", prefix.c_str(), rawDataIndex); +} + +size_t TextureCyclingParams::GetRawDataSize() const +{ + return 0xC; +} + +void TextureCyclingParams::DeclareReferences([[maybe_unused]] const std::string& prefix) +{ + if (textureListAddress != 0) // NULL + { + std::string texturesBodyStr; + std::string texName; + std::string comment; + + for (const auto& tex : textureList) + { + bool texFound = + Globals::Instance->GetSegmentedPtrName(tex, parent, "", texName, parent->workerID); + + // texName is a raw segmented pointer. This occurs if the texture is not declared + // separately since we cannot read the format. In theory we could scan DLists for the + // format on the appropriate segments. + if (!texFound) + { + comment = " // Raw pointer, declare texture in XML to use proper symbol"; + + auto msgHeader = StringHelper::Sprintf( + "TexCycle texture array declared here points to unknown texture at address %s", + texName.c_str()); + HANDLE_WARNING_RESOURCE( + WarningType::HardcodedPointer, parent, this, rawDataIndex, msgHeader, + "Please declare the texture in the XML to use the proper symbol."); + } + texturesBodyStr += StringHelper::Sprintf("\t%s,%s\n", texName.c_str(), comment.c_str()); + } + + texturesBodyStr.pop_back(); + + parent->AddDeclarationArray( + Seg2Filespace(textureListAddress, parent->baseAddress), DeclarationAlignment::Align4, + textureList.size() * 4, "TexturePtr", + StringHelper::Sprintf("%sTexCycleTexPtrs_%06X", parent->GetName().c_str(), + Seg2Filespace(textureListAddress, parent->baseAddress)), + textureList.size(), texturesBodyStr); + } + + if (textureIndexListAddress != 0) // NULL + { + std::string indicesBodyStr = "\t"; + + for (uint8_t index : textureIndexList) + { + indicesBodyStr += StringHelper::Sprintf("%d, ", index); + } + + indicesBodyStr.pop_back(); + + parent->AddDeclarationArray( + Seg2Filespace(textureIndexListAddress, parent->baseAddress), + DeclarationAlignment::Align4, textureIndexList.size(), "u8", + StringHelper::Sprintf("%sTexCycleTexIndices_%06X", parent->GetName().c_str(), + Seg2Filespace(textureIndexListAddress, parent->baseAddress)), + textureIndexList.size(), indicesBodyStr); + } +} + +std::string TextureCyclingParams::GetBodySourceCode() const +{ + std::string textureListName; + std::string textureIndexListName; + + Globals::Instance->GetSegmentedPtrName(textureListAddress, parent, "", textureListName, + parent->workerID); + Globals::Instance->GetSegmentedPtrName(textureIndexListAddress, parent, "", + textureIndexListName, parent->workerID); + + std::string bodyStr = StringHelper::Sprintf( + "\n %d, %s, %s,\n", cycleLength, textureListName.c_str(), textureIndexListName.c_str()); + + return bodyStr; +} + +/* ZTextureAnimation */ + +ZTextureAnimation::ZTextureAnimation(ZFile* nParent) : ZResource(nParent) +{ + genOTRDef = true; +} + +/** + * Builds the array of params + */ +void ZTextureAnimation::ParseRawData() +{ + ZResource::ParseRawData(); + + TextureAnimationEntry currentEntry; + auto rawData = parent->GetRawData(); + int16_t type; + + for (uint32_t currentPtr = rawDataIndex;; currentPtr += 8) + { + type = BitConverter::ToInt16BE(rawData, currentPtr + 2); + + currentEntry.segment = BitConverter::ToInt8BE(rawData, currentPtr); + currentEntry.type = (TextureAnimationParamsType)type; + currentEntry.paramsPtr = BitConverter::ToUInt32BE(rawData, currentPtr + 4); + entries.push_back(currentEntry); + + if ((type < 0) || (type > 6)) + { + HANDLE_ERROR_RESOURCE( + WarningType::Always, parent, this, rawDataIndex, + StringHelper::Sprintf( + "unknown TextureAnimationParams type 0x%02X in TextureAnimation", type), + StringHelper::Sprintf( + "Entry reads { 0x%02X, 0x%02X, 0x%08X } , but type should be " + "between 0x00 and 0x06 inclusive.", + currentEntry.segment, type, currentEntry.paramsPtr)); + } + + if (currentEntry.segment <= 0) + break; + } +} + +#include +/** + * For each params entry, + */ +void ZTextureAnimation::DeclareReferences(const std::string& prefix) +{ + std::string varPrefix = name; + if (varPrefix == "") + varPrefix = prefix; + + ZResource::DeclareReferences(varPrefix); + + for (const auto& entry : entries) + { + if (entry.paramsPtr != 0 && GETSEGNUM(entry.paramsPtr) == parent->segment) + { + uint32_t paramsOffset = Seg2Filespace(entry.paramsPtr, parent->baseAddress); + if (!parent->HasDeclaration(paramsOffset)) + { + ZTextureAnimationParams* params; + int count; + switch (entry.type) + { + case TextureAnimationParamsType::SingleScroll: + if (true) + { + count = 1; + // The else now allows SingleScroll to fall through to params = ... without + // touching the code in the else block + } + else + { + // The contents of this block can only be run by jumping into it with the + // case label + [[fallthrough]]; + case TextureAnimationParamsType::DualScroll: + count = 2; + } + params = new TextureScrollingParams(parent); + params->type = entry.type; + params->ExtractFromBinary(paramsOffset, count); + break; + + case TextureAnimationParamsType::ColorChange: + case TextureAnimationParamsType::ColorChangeLERP: + case TextureAnimationParamsType::ColorChangeLagrange: + params = new TextureColorChangingParams(parent); + params->type = entry.type; + params->ExtractFromBinary(paramsOffset); + break; + + case TextureAnimationParamsType::TextureCycle: + params = new TextureCyclingParams(parent); + params->type = entry.type; + params->ExtractFromBinary(paramsOffset); + break; + + case TextureAnimationParamsType::Empty: + HANDLE_WARNING_RESOURCE( + WarningType::InvalidExtractedData, parent, this, rawDataIndex, + "TextureAnimationParams entry has empty type (6), but params pointer is " + "not NULL", + StringHelper::Sprintf("Params read { 0x%02X, 0x%02X, 0x%08X } .", + entry.segment, (int)entry.type, entry.paramsPtr)); + return; + default: + // Because GCC is worried this could happen + assert(entry.type < TextureAnimationParamsType::SingleScroll || + entry.type > TextureAnimationParamsType::Empty); + return; + } + + params->SetName(params->GetDefaultName(varPrefix)); + params->DeclareVar(varPrefix, ""); + parent->AddResource(params); + } + } + } +} + +std::string ZTextureAnimation::GetSourceTypeName() const +{ + return "AnimatedMaterial"; // TODO: Better name +} + +ZResourceType ZTextureAnimation::GetResourceType() const +{ + return ZResourceType::TextureAnimation; +} + +/** + * The size of the main array + */ +size_t ZTextureAnimation::GetRawDataSize() const +{ + return entries.size() * 8; +} + +std::string ZTextureAnimation::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sTexAnim_%06X", prefix.c_str(), rawDataIndex); +} + +//std::string ZTextureAnimation::GetSourceOutputHeader(const std::string& prefix, +// std::set* nameSet) +//{ +// return std::string(); +//} + +Declaration* ZTextureAnimation::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + Declaration* decl = + parent->AddDeclarationArray(rawDataIndex, GetDeclarationAlignment(), GetRawDataSize(), + GetSourceTypeName(), auxName, entries.size(), bodyStr); + decl->staticConf = staticConf; + return decl; +} + +std::string ZTextureAnimation::GetBodySourceCode() const +{ + std::string bodyStr; + + for (const auto& entry : entries) + { + std::string paramName; + Globals::Instance->GetSegmentedPtrName(entry.paramsPtr, parent, "", paramName, + parent->workerID); + + bodyStr += StringHelper::Sprintf("\t{ %d, %d, %s },\n", entry.segment, entry.type, + paramName.c_str()); + } + + bodyStr.pop_back(); + + return bodyStr; +} diff --git a/ZAPDTR/ZAPD/ZTextureAnimation.h b/ZAPDTR/ZAPD/ZTextureAnimation.h new file mode 100644 index 000000000..f49836f39 --- /dev/null +++ b/ZAPDTR/ZAPD/ZTextureAnimation.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include +#include +#include + +#include "ZResource.h" + +enum class TextureAnimationParamsType +{ + /* 0 */ SingleScroll, + /* 1 */ DualScroll, + /* 2 */ ColorChange, + /* 3 */ ColorChangeLERP, + /* 4 */ ColorChangeLagrange, + /* 5 */ TextureCycle, + /* 6 */ Empty // An empty TextureAnimation has the form 00 00 00 06 00000000 +}; + +class ZTextureAnimationParams : public ZResource +{ +public: + ZTextureAnimationParams(ZFile* parent); + + void ExtractFromBinary(uint32_t nRawDataIndex); + virtual void ExtractFromBinary(uint32_t nRawDataIndex, int count); + + virtual std::string GetDefaultName(const std::string& prefix) const; + ZResourceType GetResourceType() const; + + TextureAnimationParamsType type; +}; + +struct TextureScrollingParamsEntry +{ + int8_t xStep; + int8_t yStep; + uint8_t width; + uint8_t height; +}; + +class TextureScrollingParams : public ZTextureAnimationParams +{ +public: + TextureScrollingParams(ZFile* parent); + + void ParseRawData() override; + void ExtractFromBinary(uint32_t nRawDataIndex, int count) override; + + std::string GetSourceTypeName() const override; + std::string GetDefaultName(const std::string& prefix) const override; + size_t GetRawDataSize() const override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + int count; // 1 for Single, 2 for Dual + TextureScrollingParamsEntry rows[2]; // Too small to make a vector worth it +}; + +struct F3DPrimColor +{ + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + uint8_t lodFrac; +}; + +struct F3DEnvColor +{ + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; + +class TextureColorChangingParams : public ZTextureAnimationParams +{ +public: + TextureColorChangingParams(ZFile* parent); + + void ParseRawData() override; + + std::string GetSourceTypeName() const override; + std::string GetDefaultName(const std::string& prefix) const override; + size_t GetRawDataSize() const override; + + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + uint16_t animLength; // size of list for type 2 + uint16_t colorListCount; + segptr_t primColorListAddress; + segptr_t envColorListAddress; + segptr_t frameDataListAddress; + std::vector primColorList; + std::vector envColorList; + std::vector frameDataList; +}; + +class TextureCyclingParams : public ZTextureAnimationParams +{ +public: + TextureCyclingParams(ZFile* parent); + + void ParseRawData() override; + + std::string GetSourceTypeName() const override; + std::string GetDefaultName(const std::string& prefix) const override; + size_t GetRawDataSize() const override; + + void DeclareReferences(const std::string& prefix) override; + + std::string GetBodySourceCode() const override; + + uint16_t cycleLength; + segptr_t textureListAddress; + segptr_t textureIndexListAddress; + std::vector textureList; + std::vector textureIndexList; +}; + +struct TextureAnimationEntry +{ + int8_t segment; + TextureAnimationParamsType type; + segptr_t paramsPtr; +}; + +class ZTextureAnimation : public ZResource +{ +public: + ZTextureAnimation(ZFile* nParent); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + size_t GetRawDataSize() const override; + std::string GetDefaultName(const std::string& prefix) const override; + //std::string GetSourceOutputHeader(const std::string& prefix, + // std::set* nameSet) override; + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + std::vector entries; +}; diff --git a/ZAPDTR/ZAPD/ZVector.cpp b/ZAPDTR/ZAPD/ZVector.cpp new file mode 100644 index 000000000..092b876d2 --- /dev/null +++ b/ZAPDTR/ZAPD/ZVector.cpp @@ -0,0 +1,126 @@ +#include "ZVector.h" + +#include + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include +#include "Utils/StringHelper.h" +#include "WarningHandler.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Vector, ZVector); + +ZVector::ZVector(ZFile* nParent) : ZResource(nParent) +{ + scalarType = ZScalarType::ZSCALAR_NONE; + dimensions = 0; + + RegisterRequiredAttribute("Type"); + RegisterRequiredAttribute("Dimensions"); +} + +void ZVector::ExtractFromBinary(uint32_t nRawDataIndex, ZScalarType nScalarType, + uint32_t nDimensions) +{ + rawDataIndex = nRawDataIndex; + scalarType = nScalarType; + dimensions = nDimensions; + + // Don't parse raw data of external files + if (parent->GetMode() == ZFileMode::ExternalFile) + return; + + ParseRawData(); +} + +void ZVector::ParseXML(tinyxml2::XMLElement* reader) +{ + ZResource::ParseXML(reader); + + this->scalarType = ZScalar::MapOutputTypeToScalarType(registeredAttributes.at("Type").value); + + this->dimensions = StringHelper::StrToL(registeredAttributes.at("Dimensions").value, 16); +} + +void ZVector::ParseRawData() +{ + int32_t currentRawDataIndex = rawDataIndex; + // TODO: this shouldn't be necessary. + scalars.clear(); + scalars.reserve(dimensions); + for (uint32_t i = 0; i < dimensions; i++) + { + ZScalar scalar(parent); + scalar.ExtractFromBinary(currentRawDataIndex, scalarType); + currentRawDataIndex += scalar.GetRawDataSize(); + + scalars.push_back(scalar); + } + + // Ensure the scalars vector has the same number of elements as the vector dimension. + assert(scalars.size() == dimensions); +} + +size_t ZVector::GetRawDataSize() const +{ + size_t size = 0; + + for (size_t i = 0; i < this->scalars.size(); i++) + size += this->scalars[i].GetRawDataSize(); + + return size; +} + +bool ZVector::DoesSupportArray() const +{ + return true; +} + +std::string ZVector::GetSourceTypeName() const +{ + if (dimensions == 3 && scalarType == ZScalarType::ZSCALAR_F32) + return "Vec3f"; + else if (dimensions == 3 && scalarType == ZScalarType::ZSCALAR_S16) + return "Vec3s"; + else if (dimensions == 3 && scalarType == ZScalarType::ZSCALAR_S32) + return "Vec3i"; + else + { + std::string msgHeader = StringHelper::Sprintf( + "encountered unsupported vector type: %d dimensions, %s type", dimensions, + ZScalar::MapScalarTypeToOutputType(scalarType).c_str()); + + HANDLE_ERROR_RESOURCE(WarningType::NotImplemented, parent, this, rawDataIndex, msgHeader, + ""); + } +} + +std::string ZVector::GetBodySourceCode() const +{ + std::string body = ""; + + for (size_t i = 0; i < scalars.size(); i++) + { + body += StringHelper::Sprintf("%6s", scalars[i].GetBodySourceCode().c_str()); + + if (i + 1 < scalars.size()) + body += ", "; + } + + return body; +} + +ZResourceType ZVector::GetResourceType() const +{ + return ZResourceType::Vector; +} + +DeclarationAlignment ZVector::GetDeclarationAlignment() const +{ + if (scalars.size() == 0) + { + return DeclarationAlignment::Align4; + } + return scalars.at(0).GetDeclarationAlignment(); +} diff --git a/ZAPDTR/ZAPD/ZVector.h b/ZAPDTR/ZAPD/ZVector.h new file mode 100644 index 000000000..a50d3e808 --- /dev/null +++ b/ZAPDTR/ZAPD/ZVector.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include "ZResource.h" +#include "ZScalar.h" +#include "tinyxml2.h" + +class ZVector : public ZResource +{ +public: + std::vector scalars; + ZScalarType scalarType; + uint32_t dimensions; + + ZVector(ZFile* nParent); + + void ExtractFromBinary(uint32_t nRawDataIndex, ZScalarType nScalarType, uint32_t nDimensions); + + void ParseXML(tinyxml2::XMLElement* reader) override; + void ParseRawData() override; + + std::string GetBodySourceCode() const override; + + bool DoesSupportArray() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; +}; diff --git a/ZAPDTR/ZAPD/ZVtx.cpp b/ZAPDTR/ZAPD/ZVtx.cpp new file mode 100644 index 000000000..e4b3d9796 --- /dev/null +++ b/ZAPDTR/ZAPD/ZVtx.cpp @@ -0,0 +1,86 @@ +#include "ZVtx.h" + +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Vtx, ZVtx); + +ZVtx::ZVtx(ZFile* nParent) : ZResource(nParent) +{ + x = 0; + y = 0; + z = 0; + flag = 0; + s = 0; + t = 0; + r = 0; + g = 0; + b = 0; + a = 0; +} + +void ZVtx::ParseRawData() +{ + ZResource::ParseRawData(); + + const auto& rawData = parent->GetRawData(); + x = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + y = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + z = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + flag = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + s = BitConverter::ToInt16BE(rawData, rawDataIndex + 8); + t = BitConverter::ToInt16BE(rawData, rawDataIndex + 10); + r = rawData[rawDataIndex + 12]; + g = rawData[rawDataIndex + 13]; + b = rawData[rawDataIndex + 14]; + a = rawData[rawDataIndex + 15]; +} + +Declaration* ZVtx::DeclareVar(const std::string& prefix, const std::string& bodyStr) +{ + Declaration* decl = ZResource::DeclareVar(prefix, bodyStr); + decl->isExternal = true; + return decl; +} + +std::string ZVtx::GetBodySourceCode() const +{ + return StringHelper::Sprintf("VTX(%i, %i, %i, %i, %i, %i, %i, %i, %i)", x, y, z, s, t, r, g, b, + a); +} + +size_t ZVtx::GetRawDataSize() const +{ + return 16; +} + +bool ZVtx::DoesSupportArray() const +{ + return true; +} + +ZResourceType ZVtx::GetResourceType() const +{ + return ZResourceType::Vertex; +} + +bool ZVtx::IsExternalResource() const +{ + return true; +} + +std::string ZVtx::GetSourceTypeName() const +{ + return "Vtx"; +} + +std::string ZVtx::GetExternalExtension() const +{ + return "vtx"; +} + +DeclarationAlignment ZVtx::GetDeclarationAlignment() const +{ + return DeclarationAlignment::Align8; +} diff --git a/ZAPDTR/ZAPD/ZVtx.h b/ZAPDTR/ZAPD/ZVtx.h new file mode 100644 index 000000000..511048791 --- /dev/null +++ b/ZAPDTR/ZAPD/ZVtx.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include "ZResource.h" +#include "ZScalar.h" +#include "tinyxml2.h" + +class ZVtx : public ZResource +{ +public: + int16_t x, y, z; + uint16_t flag; + int16_t s, t; + uint8_t r, g, b, a; + + ZVtx(ZFile* nParent); + + void ParseRawData() override; + + Declaration* DeclareVar(const std::string& prefix, const std::string& bodyStr) override; + std::string GetBodySourceCode() const override; + + bool IsExternalResource() const override; + bool DoesSupportArray() const override; + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + std::string GetExternalExtension() const override; + + size_t GetRawDataSize() const override; + DeclarationAlignment GetDeclarationAlignment() const override; +}; diff --git a/ZAPDTR/ZAPD/ZWaterbox.cpp b/ZAPDTR/ZAPD/ZWaterbox.cpp new file mode 100644 index 000000000..9a289db52 --- /dev/null +++ b/ZAPDTR/ZAPD/ZWaterbox.cpp @@ -0,0 +1,74 @@ +#include "ZWaterbox.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/StringHelper.h" + +REGISTER_ZFILENODE(Waterbox, ZWaterbox); + +ZWaterbox::ZWaterbox(ZFile* nParent) : ZResource(nParent) +{ +} + +ZWaterbox::~ZWaterbox() +{ +} + +void ZWaterbox::ParseRawData() +{ + const auto& rawData = parent->GetRawData(); + + xMin = BitConverter::ToInt16BE(rawData, rawDataIndex + 0); + ySurface = BitConverter::ToInt16BE(rawData, rawDataIndex + 2); + zMin = BitConverter::ToInt16BE(rawData, rawDataIndex + 4); + xLength = BitConverter::ToInt16BE(rawData, rawDataIndex + 6); + zLength = BitConverter::ToInt16BE(rawData, rawDataIndex + 8); + + if (Globals::Instance->game == ZGame::OOT_SW97) + properties = BitConverter::ToInt16BE(rawData, rawDataIndex + 10); + else + properties = BitConverter::ToInt32BE(rawData, rawDataIndex + 12); +} + +void ZWaterbox::DeclareReferences(const std::string& prefix) +{ + std::string declaration; + std::string auxName = name; + + if (name == "") + auxName = GetDefaultName(prefix); + + parent->AddDeclaration(rawDataIndex, DeclarationAlignment::Align4, GetRawDataSize(), + GetSourceTypeName(), name.c_str(), GetBodySourceCode()); +} + +std::string ZWaterbox::GetBodySourceCode() const +{ + return StringHelper::Sprintf("%i, %i, %i, %i, %i, 0x%08X", xMin, ySurface, zMin, xLength, + zLength, properties); +} + +std::string ZWaterbox::GetDefaultName(const std::string& prefix) const +{ + return StringHelper::Sprintf("%sWaterBoxes_%06X", prefix.c_str(), rawDataIndex); +} + +ZResourceType ZWaterbox::GetResourceType() const +{ + return ZResourceType::Waterbox; +} + +size_t ZWaterbox::GetRawDataSize() const +{ + return 16; +} + +std::string ZWaterbox::GetSourceTypeName() const +{ + return "WaterBox"; +} + +bool ZWaterbox::DoesSupportArray() const +{ + return true; +} diff --git a/ZAPDTR/ZAPD/ZWaterbox.h b/ZAPDTR/ZAPD/ZWaterbox.h new file mode 100644 index 000000000..e190b26a0 --- /dev/null +++ b/ZAPDTR/ZAPD/ZWaterbox.h @@ -0,0 +1,30 @@ +#pragma once + +#include "ZFile.h" +#include "ZResource.h" + +class ZWaterbox : public ZResource +{ +public: + int16_t xMin; + int16_t ySurface; + int16_t zMin; + int16_t xLength; + int16_t zLength; + int32_t properties; + + ZWaterbox(ZFile* nParent); + ~ZWaterbox(); + + void ParseRawData() override; + void DeclareReferences(const std::string& prefix) override; + std::string GetBodySourceCode() const override; + std::string GetDefaultName(const std::string& prefix) const override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + bool DoesSupportArray() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/any/any/zlib.static.txt b/ZAPDTR/ZAPD/any/any/zlib.static.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ZAPDTR/ZAPD/ctpl_stl.h b/ZAPDTR/ZAPD/ctpl_stl.h new file mode 100644 index 000000000..8e2d9c908 --- /dev/null +++ b/ZAPDTR/ZAPD/ctpl_stl.h @@ -0,0 +1,251 @@ +/********************************************************* +* +* Copyright (C) 2014 by Vitaliy Vitsentiy +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*********************************************************/ + + +#ifndef __ctpl_stl_thread_pool_H__ +#define __ctpl_stl_thread_pool_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// thread pool to run user's functors with signature +// ret func(int id, other_params) +// where id is the index of the thread that runs the functor +// ret is some return type + + +namespace ctpl { + + namespace detail { + template + class Queue { + public: + bool push(T const & value) { + std::unique_lock lock(this->mutex); + this->q.push(value); + return true; + } + // deletes the retrieved element, do not use for non integral types + bool pop(T & v) { + std::unique_lock lock(this->mutex); + if (this->q.empty()) + return false; + v = this->q.front(); + this->q.pop(); + return true; + } + bool empty() { + std::unique_lock lock(this->mutex); + return this->q.empty(); + } + private: + std::queue q; + std::mutex mutex; + }; + } + + class thread_pool { + + public: + + thread_pool() { this->init(); } + thread_pool(int nThreads) { this->init(); this->resize(nThreads); } + + // the destructor waits for all the functions in the queue to be finished + ~thread_pool() { + this->stop(true); + } + + // get the number of running threads in the pool + int size() { return static_cast(this->threads.size()); } + + // number of idle threads + int n_idle() { return this->nWaiting; } + std::thread & get_thread(int i) { return *this->threads[i]; } + + // change the number of threads in the pool + // should be called from one thread, otherwise be careful to not interleave, also with this->stop() + // nThreads must be >= 0 + void resize(int nThreads) { + if (!this->isStop && !this->isDone) { + int oldNThreads = static_cast(this->threads.size()); + if (oldNThreads <= nThreads) { // if the number of threads is increased + this->threads.resize(nThreads); + this->flags.resize(nThreads); + + for (int i = oldNThreads; i < nThreads; ++i) { + this->flags[i] = std::make_shared>(false); + this->set_thread(i); + } + } + else { // the number of threads is decreased + for (int i = oldNThreads - 1; i >= nThreads; --i) { + *this->flags[i] = true; // this thread will finish + this->threads[i]->detach(); + } + { + // stop the detached threads that were waiting + std::unique_lock lock(this->mutex); + this->cv.notify_all(); + } + this->threads.resize(nThreads); // safe to delete because the threads are detached + this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals + } + } + } + + // empty the queue + void clear_queue() { + std::function * _f; + while (this->q.pop(_f)) + delete _f; // empty the queue + } + + // pops a functional wrapper to the original function + std::function pop() { + std::function * _f = nullptr; + this->q.pop(_f); + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + std::function f; + if (_f) + f = *_f; + return f; + } + + // wait for all computing threads to finish and stop all threads + // may be called asynchronously to not pause the calling thread while waiting + // if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions + void stop(bool isWait = false) { + if (!isWait) { + if (this->isStop) + return; + this->isStop = true; + for (int i = 0, n = this->size(); i < n; ++i) { + *this->flags[i] = true; // command the threads to stop + } + this->clear_queue(); // empty the queue + } + else { + if (this->isDone || this->isStop) + return; + this->isDone = true; // give the waiting threads a command to finish + } + { + std::unique_lock lock(this->mutex); + this->cv.notify_all(); // stop all waiting threads + } + for (int i = 0; i < static_cast(this->threads.size()); ++i) { // wait for the computing threads to finish + if (this->threads[i]->joinable()) + this->threads[i]->join(); + } + // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads + // therefore delete them here + this->clear_queue(); + this->threads.clear(); + this->flags.clear(); + } + + template + auto push(F && f, Rest&&... rest) ->std::future { + auto pck = std::make_shared>( + std::bind(std::forward(f), std::placeholders::_1, std::forward(rest)...) + ); + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + return pck->get_future(); + } + + // run the user's function that excepts argument int - id of the running thread. returned value is templatized + // operator returns std::future, where the user can get the result and rethrow the catched exceptins + template + auto push(F && f) ->std::future { + auto pck = std::make_shared>(std::forward(f)); + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + return pck->get_future(); + } + + + private: + + // deleted + thread_pool(const thread_pool &);// = delete; + thread_pool(thread_pool &&);// = delete; + thread_pool & operator=(const thread_pool &);// = delete; + thread_pool & operator=(thread_pool &&);// = delete; + + void set_thread(int i) { + std::shared_ptr> flag(this->flags[i]); // a copy of the shared ptr to the flag + auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() { + std::atomic & _flag = *flag; + std::function * _f; + bool isPop = this->q.pop(_f); + while (true) { + while (isPop) { // if there is anything in the queue + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + (*_f)(i); + if (_flag) + return; // the thread is wanted to stop, return even if the queue is not empty yet + else + isPop = this->q.pop(_f); + } + // the queue is empty here, wait for the next command + std::unique_lock lock(this->mutex); + ++this->nWaiting; + this->cv.wait(lock, [this, &_f, &isPop, &_flag](){ isPop = this->q.pop(_f); return isPop || this->isDone || _flag; }); + --this->nWaiting; + if (!isPop) + return; // if the queue is empty and this->isDone == true or *flag then return + } + }; + this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique() + } + + void init() { this->nWaiting = 0; this->isStop = false; this->isDone = false; } + + std::vector> threads; + std::vector>> flags; + detail::Queue *> q; + std::atomic isDone; + std::atomic isStop; + std::atomic nWaiting; // how many threads are waiting + + std::mutex mutex; + std::condition_variable cv; + }; + +} + +#endif // __ctpl_stl_thread_pool_H__ \ No newline at end of file diff --git a/ZAPDTR/ZAPD/genbuildinfo.py b/ZAPDTR/ZAPD/genbuildinfo.py new file mode 100644 index 000000000..3317c4276 --- /dev/null +++ b/ZAPDTR/ZAPD/genbuildinfo.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +import argparse +from datetime import datetime +import getpass +import subprocess + +parser = argparse.ArgumentParser() +parser.add_argument("--devel", action="store_true") +args = parser.parse_args() + +with open("build/ZAPD/BuildInfo.cpp", "w+") as buildFile: + # Get commit hash from git + # If git fails due to a missing .git directory, a default label will be used instead. + try: + label = subprocess.check_output(["git", "describe", "--always"]).strip().decode("utf-8") + except: + label = "GIT_NOT_FOUND" + + now = datetime.now() + if args.devel: + label += " ~ Development version" + buildFile.write("extern const char gBuildHash[] = \"" + label + "\";\n") + #buildFile.write("extern const char gBuildDate[] = \"" + now.strftime("%Y-%m-%d %H:%M:%S") + "\";\n") diff --git a/ZAPDTR/ZAPD/packages.config b/ZAPDTR/ZAPD/packages.config new file mode 100644 index 000000000..c387aaed7 --- /dev/null +++ b/ZAPDTR/ZAPD/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/ZAPDTR/ZAPD/pathconf.c b/ZAPDTR/ZAPD/pathconf.c new file mode 100644 index 000000000..8ef448999 --- /dev/null +++ b/ZAPDTR/ZAPD/pathconf.c @@ -0,0 +1,6 @@ +#include + +long pathconf(const char *path, int name) { + return -1; +} + diff --git a/ZAPDTR/ZAPD/yaz0/readwrite.h b/ZAPDTR/ZAPD/yaz0/readwrite.h new file mode 100644 index 000000000..af3471590 --- /dev/null +++ b/ZAPDTR/ZAPD/yaz0/readwrite.h @@ -0,0 +1,29 @@ +#ifndef __READWRITE_H__ +#define __READWRITE_H__ + +#include + +/* variables */ +union { + uint32_t u; + float f; +} __u32_f32_union__; + +#define U32(x) \ + ((uint32_t)((((uint8_t*)(x))[0] << 24) | (((uint8_t*)(x))[1] << 16) | \ + (((uint8_t*)(x))[2] << 8) | ((uint8_t*)(x))[3])) +#define U16(x) ((uint16_t)(((*((uint8_t*)(x))) << 8) | ((uint8_t*)(x))[1])) +#define U8(x) ((uint8_t)((uint8_t*)(x))[0]) +#define S32(x) ((int32_t)(U32(x))) +#define S16(x) ((int16_t)(U16(x))) +#define F32(x) (((__u32_f32_union__.u = U32(x)) & 0) + __u32_f32_union__.f) + +#define W32(x, v) \ + { \ + *((uint8_t*)x + 3) = ((v)&0xFF); \ + *((uint8_t*)x + 2) = (((v) >> 8) & 0xFF); \ + *((uint8_t*)x + 1) = (((v) >> 16) & 0xFF); \ + *((uint8_t*)x + 0) = (((v) >> 24) & 0xFF); \ + } + +#endif \ No newline at end of file diff --git a/ZAPDTR/ZAPD/yaz0/yaz0.cpp b/ZAPDTR/ZAPD/yaz0/yaz0.cpp new file mode 100644 index 000000000..60d3e9980 --- /dev/null +++ b/ZAPDTR/ZAPD/yaz0/yaz0.cpp @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include "readwrite.h" + +#include "yaz0.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; + +/* internal declarations */ +int yaz0_encode_internal(const u8* src, int srcSize, u8* Data); + +int yaz0_get_size(u8* src) { return U32(src + 0x4); } + +u32 toDWORD(u32 d) { + u8 w1 = d & 0xFF; + u8 w2 = (d >> 8) & 0xFF; + u8 w3 = (d >> 16) & 0xFF; + u8 w4 = d >> 24; + return (w1 << 24) | (w2 << 16) | (w3 << 8) | w4; +} + +// simple and straight encoding scheme for Yaz0 +u32 longest_match_brute(const u8* src, int size, int pos, u32* pMatchPos) { + int startPos = pos - 0x1000; + int max_match_size = size - pos; + u32 best_match_size = 0; + u32 best_match_pos = 0; + + if (max_match_size < 3) return 0; + + if (startPos < 0) startPos = 0; + + if (max_match_size > 0x111) max_match_size = 0x111; + + for (int i = startPos; i < pos; i++) { + int current_size; + for (current_size = 0; current_size < max_match_size; current_size++) { + if (src[i + current_size] != src[pos + current_size]) { + break; + } + } + if (current_size > best_match_size) { + best_match_size = current_size; + best_match_pos = i; + if (best_match_size == 0x111) break; + } + } + *pMatchPos = best_match_pos; + return best_match_size; +} + +u32 longest_match_rabinkarp(const u8* src, int size, int pos, u32* match_pos) { + int startPos = pos - 0x1000; + int max_match_size = size - pos; + u32 best_match_size = 0; + u32 best_match_pos = 0; + + if (max_match_size < 3) return 0; + + if (startPos < 0) startPos = 0; + + if (max_match_size > 0x111) max_match_size = 0x111; + + int find_hash = src[pos] << 16 | src[pos + 1] << 8 | src[pos + 2]; + int current_hash = src[startPos] << 16 | src[startPos + 1] << 8 | src[startPos + 2]; + + for (int i = startPos; i < pos; i++) { + if(current_hash == find_hash) { + int current_size; + for (current_size = 3; current_size < max_match_size; current_size++) { + if (src[i + current_size] != src[pos + current_size]) { + break; + } + } + if (current_size > best_match_size) { + best_match_size = current_size; + best_match_pos = i; + if (best_match_size == 0x111) break; + } + } + current_hash = (current_hash << 8 | src[i + 3]) & 0xFFFFFF; + } + *match_pos = best_match_pos; + + return best_match_size; +} + +int yaz0_encode_internal(const u8* src, int srcSize, u8* Data) { + int srcPos = 0; + + int bitmask = 0x80; + u8 currCodeByte = 0; + int currCodeBytePos = 0; + int pos = currCodeBytePos + 1; + + while (srcPos < srcSize) { + u32 numBytes; + u32 matchPos; + + numBytes = longest_match_rabinkarp(src, srcSize, srcPos, &matchPos); + //fprintf(stderr, "pos %x len %x pos %x\n", srcPos, (int)numBytes, (int)matchPos); + if (numBytes < 3) { + //fprintf(stderr, "single byte %02x\n", src[srcPos]); + Data[pos++] = src[srcPos++]; + currCodeByte |= bitmask; + } else { + // RLE part + u32 dist = srcPos - matchPos - 1; + + if (numBytes >= 0x12) // 3 byte encoding + { + Data[pos++] = dist >> 8; // 0R + Data[pos++] = dist & 0xFF; // FF + if (numBytes > 0xFF + 0x12) numBytes = 0xFF + 0x12; + Data[pos++] = numBytes - 0x12; + } else // 2 byte encoding + { + Data[pos++] = ((numBytes - 2) << 4) | (dist >> 8); + Data[pos++] = dist & 0xFF; + } + srcPos += numBytes; + } + bitmask >>= 1; + // write eight codes + if (!bitmask) { + Data[currCodeBytePos] = currCodeByte; + currCodeBytePos = pos++; + + currCodeByte = 0; + bitmask = 0x80; + } + } + if (bitmask) { + Data[currCodeBytePos] = currCodeByte; + } + + return pos; +} + +std::vector yaz0_encode_fast(const u8* src, int src_size) { + std::vector buffer; + std::vector> lut; + lut.resize(0x1000000); + + for (int i = 0; i < src_size - 3; ++i) { + lut[src[i + 0] << 16 | src[i + 1] << 8 | src[i + 2]].push_back(i); + } + + return buffer; +} + +std::vector yaz0_encode(const u8* src, int src_size) { + std::vector buffer(src_size * 10 / 8 + 16); + u8* dst = buffer.data(); + + // write 4 bytes yaz0 header + memcpy(dst, "Yaz0", 4); + + // write 4 bytes uncompressed size + W32(dst + 4, src_size); + + // encode + int dst_size = yaz0_encode_internal(src, src_size, dst + 16); + int aligned_size = (dst_size + 31) & -16; + buffer.resize(aligned_size); + +#if 0 + std::vector decompressed(src_size); + yaz0_decode(buffer.data(), decompressed.data(), src_size); + if(memcmp(src, decompressed.data(), src_size)) { + fprintf(stderr, "Decompressed buffer is different from original\n"); + } +#endif + + return buffer; +} + +uint32_t yaz0_decode(const uint8_t* source, uint8_t* decomp, int32_t decompSize) { + uint32_t srcPlace = 0, dstPlace = 0; + uint32_t i, dist, copyPlace, numBytes; + uint8_t codeByte, byte1, byte2; + uint8_t bitCount = 0; + + if (source[0] != 'Y' || source[1] != 'a' || source[2] != 'z' || source[3] != '0') + { + return 0; + } + + // Get size from the YAZ header + if (decompSize == -1) + { + decompSize = toDWORD(((uint32_t*)source)[1]); + } + + source += 0x10; + while (dstPlace < decompSize) { + /* If there are no more bits to test, get a new byte */ + if (!bitCount) { + codeByte = source[srcPlace++]; + bitCount = 8; + } + + /* If bit 7 is a 1, just copy 1 byte from source to destination */ + /* Else do some decoding */ + if (codeByte & 0x80) { + decomp[dstPlace++] = source[srcPlace++]; + } else { + /* Get 2 bytes from source */ + byte1 = source[srcPlace++]; + byte2 = source[srcPlace++]; + + /* Calculate distance to move in destination */ + /* And the number of bytes to copy */ + dist = ((byte1 & 0xF) << 8) | byte2; + copyPlace = dstPlace - (dist + 1); + numBytes = byte1 >> 4; + + /* Do more calculations on the number of bytes to copy */ + if (!numBytes) + numBytes = source[srcPlace++] + 0x12; + else + numBytes += 2; + + /* Copy data from a previous point in destination */ + /* to current point in destination */ + for (i = 0; i < numBytes; i++) decomp[dstPlace++] = decomp[copyPlace++]; + } + + /* Set up for the next read cycle */ + codeByte = codeByte << 1; + bitCount--; + } + return decompSize; +} + +void yaz0_decodeYarArchive(const uint8_t* src, uint8_t* dest, size_t decompSize) +{ + unsigned int firstEntryPos = toDWORD(((uint32_t*)src)[0]); + size_t totalSize = 0; + totalSize += yaz0_decode(src + firstEntryPos, dest + totalSize, -1); + + for (unsigned int i = 1; i < (firstEntryPos / 4); i++) + { + if (i == 58) + { + int bp = 0; + } + unsigned int entry = toDWORD(((uint32_t*)src)[i]); + totalSize += yaz0_decode(src + firstEntryPos + entry, dest + totalSize, -1); + } +} \ No newline at end of file diff --git a/ZAPDTR/ZAPD/yaz0/yaz0.h b/ZAPDTR/ZAPD/yaz0/yaz0.h new file mode 100644 index 000000000..25546e2d2 --- /dev/null +++ b/ZAPDTR/ZAPD/yaz0/yaz0.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +uint32_t yaz0_decode(const uint8_t* src, uint8_t* dest, int32_t destsize); +std::vector yaz0_encode(const uint8_t* src, int src_size); +void yaz0_decodeYarArchive(const uint8_t* src, uint8_t* dest, size_t decompSize); diff --git a/ZAPDTR/copycheck.py b/ZAPDTR/copycheck.py new file mode 100755 index 000000000..36288f685 --- /dev/null +++ b/ZAPDTR/copycheck.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +import os +from shutil import copyfile + +if (os.environ.get('ZAPD_COPYDIR') != None): + print("Copying ZAPD.out to repo...") + #print(os.environ.get('ZAPD_COPYDIR')) + copyfile("ZAPD.out", os.environ.get('ZAPD_COPYDIR') + "/ZAPD.out") diff --git a/ZAPDTR/docs/zapd_extraction_xml_reference.md b/ZAPDTR/docs/zapd_extraction_xml_reference.md new file mode 100644 index 000000000..78817dc6e --- /dev/null +++ b/ZAPDTR/docs/zapd_extraction_xml_reference.md @@ -0,0 +1,680 @@ +# ZAPD extraction XML reference + +This document aims to be a small reference of how to create a compatible xml file for ZAPD. + +## Table of contents + +- [ZAPD extraction XML reference](#zapd-extraction-xml-reference) + - [Table of contents](#table-of-contents) + - [Basic XML](#basic-xml) + - [Resources types](#resources-types) + - [File](#file) + - [ExternalFile](#externalfile) + - [Texture](#texture) + - [Background](#background) + - [Blob](#blob) + - [DList](#dlist) + - [TextureAnimation](#textureanimation) + - [Scene and Room](#scene-and-room) + - [AltHeader](#altheader) + - [Animation](#animation) + - [PlayerAnimation](#playeranimation) + - [CurveAnimation](#curveanimation) + - [LegacyAnimation](#legacyanimation) + - [Skeleton](#skeleton) + - [LimbTable](#limbtable) + - [Limb](#limb) + - [Symbol](#symbol) + - [Collision](#collision) + - [Scalar](#scalar) + - [Vector](#vector) + - [Vtx](#vtx) + - [Mtx](#mtx) + - [Cutscene](#cutscene) + - [Array](#array) + - [Path](#path) + - [PlayerAnimationData](#playeranimationdata) + - [Pointer](#pointer) + +## Basic XML + +An example of an object xml: + +```xml + + + + + + + + + + + + + + + + + + + +``` + +Every xml must have a `` tag. It must have at least one `` child. + +## Resources types + +The following is a list of the resources/tags supported by ZAPD, and the attributes needed by each one. + +For most resources inside a `` tag **you should also set an `Offset` attribute**. This is the offset (within the file) of the resource you are exporting. The `Offset` attribute is expected to be in hexadecimal, for example `Offset="0x41F0"`. + +It's worth noting that every tag expects a `Name="gNameOfTheAsset"`. This is will be the name of the extracted variable in the output C code. Every asset must be prefixed with `g` and the suffix should represent the type of the variable. + +Every tag can accept a `Static` attribute to specify if the asset should be marked as `static` or not. +There are 3 valid values (defaults to `Global`): + +- `Global`: Mark static if the flag `--static` was used. +- `On`: Override the global config and **always mark** as `static`. +- `Off`: Override the global config and **don't mark** as `static`. + +This table summarizes if the asset will be marked `static` (✅) or not (❌) +| `Static=""` attribute in XML | Without `--static` flag | With `--static` flag | +| ---------------------------- | ----------------------- | -------------------- | +| `On` | ✅ | ✅ | +| `Global` (default) | ❌ | ✅ | +| `Off` | ❌ | ❌ | + +------------------------- + +### File + +- Example of this tag: + +```xml + +``` + +- Attributes: + + - `Name`: Required. The name of the file in `baserom/` which will be extracted. + - `OutName`: Optional. The output name of the generated C source file. Defaults to the value passed to `Name`. + - `Segment`: Optional. This is the segment number of the current file. Expects a decimal number between 0 and 15 inclusive, usually 6 if it is an object. If not specified, the file will use VRAM instead of segmented addresses. + - `BaseAddress`: Optional. RAM address of the file. Expects a hex number (with `0x` prefix). Default value: `0`. + - `RangeStart`: Optional. File offset where the extraction will begin. Hex. Default value: `0x000000000`. + - `RangeEnd`: Optional. File offset where the extraction will end. Hex. Default value: `0xFFFFFFFF`. + - `Game`: Optional. Valid values: `OOT`, `MM`, `SW97` and `OOTSW97`. Default value: `OOT`. + +------------------------- + +### ExternalFile + +Allows ZAPD to map segmented addresses to variables declared in other files by using its XML. + +It is useful for objects that use variables from `gameplay_keep`, `gameplay_dangeon_keep`, `gameplay_field_keep`, etc. + +This tag can be used in the global `config.xml` file. + +- Example of this tag: + +```xml + +``` + +- Attributes: + + - `XmlPath`: Required. The path of the XML, relative to the value set by `ExternalXMLFolder` in the configuration file. + - `OutPath`: Required. The path were the header for the corresponding external file is. It is used to `#include` it in the generated `.c` file. + +------------------------- + +### Texture + +Textures are extracted as `.png` files. + +- Example: + +```xml + +``` + +Will be defined as: + +```c +u64 gCraterSmokeConeTex[] = { +#include "assets/objects/object_spot17_obj/crater_smoke_cone.ia8.inc.c" +}; +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Tex`, unless it is a palette, in that case it is suffixed by `TLUT`. + - `OutName`: Required. The filename of the extracted `.png` file. + - `Format`: Required. The format of the image. Valid values: `rgba32`, `rgba16`, `i4`, `i8`, `ia4`, `ia8`, `ia16`, `ci4` and `ci8`. + - `Width`: Required. Width in pixels of the image. + - `Height`: Required. Height in pixels of the image. + - `TlutOffset`: Optional. Specifies the tlut's offset used by this texture. This attribute is only valid if `Format` is either `ci4` or `ci8`, otherwise an exception would be thrown. + - `ExternalTlut`: Optional. Specifies that the texture's tlut is in a different file. Takes the filename of the file that contains the tlut. + - `ExternalTlutOffset`: Optional. Specifies the offset in the `ExternalTlut` of the tlut for the given texture. + - `SplitTlut`: Optional. Specifies that the given texture should take from the upper half of the tlut. Takes a bool, i.e. one of `true`, `false`, `1`, `0`. + +The following is a list of the texture formats the Nintendo 64 supports, with their gfxdis names and ZAPD format names. + +| Format name | Typing in `gsDPLoadTextureBlock` | "Format" in xml | +| ----------------------------------------------- | -------------------------------- | --------------- | +| 4-bit intensity (I) | `G_IM_FMT_I, G_IM_SIZ_4b` | `i4` | +| 4-bit intensity with alpha (I/A) (3/1) | `G_IM_FMT_IA, G_IM_SIZ_4b` | `ia4` | +| 4-bit color index (CI) | `G_IM_FMT_CI, G_IM_SIZ_4b` | `ci4` | +| 8-bit I | `G_IM_FMT_I, G_IM_SIZ_8b` | `i8` | +| 8-bit IA (4/4) | `G_IM_FMT_IA, G_IM_SIZ_8b` | `ia8` | +| 8-bit CI | `G_IM_FMT_CI, G_IM_SIZ_8b` | `ci8` | +| 16-bit red, green, blue, alpha (RGBA) (5/5/5/1) | `G_IM_FMT_RGBA, G_IM_SIZ_16b` | `rgba16` | +| 16-bit IA (8/8) | `G_IM_FMT_IA, G_IM_SIZ_16b` | `ia16` | +| 16-bit YUV (Luminance, Blue-Y, Red-Y) | `G_IM_FMT_YUV, G_IM_SIZ_16b` | (not used) | +| 32-bit RGBA (8/8/8/8) | `G_IM_FMT_RGBA, G_IM_SIZ_32b` | `rgba32` | + +If you want to know more about this formats, you can check [`gsDPLoadTextureBlock`](http://n64devkit.square7.ch/n64man/gdp/gDPLoadTextureBlock.htm) for most formats, or [`gDPLoadTextureBlock_4b`](http://n64devkit.square7.ch/n64man/gdp/gDPLoadTextureBlock_4b.htm) for the 4-bit formats. + +------------------------- + +### Background + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Background`. + - `OutName`: Required. The filename of the extracted `.jpg` file. + +※ Explicit use of this tag isn't often necesary because it would probably be extracted automatically by another extracted element. You can use this to name them if you don't like the autogenerated name. + +------------------------- + +### Blob + +Blob are binary data that will be extracted as `.bin` files. + +- Example: + +```xml + +``` + +Will be defined as: + +```c + +u8 gFireTempleBlob_00CCC0[] = { +#include "assets/objects/object_hidan_objects/gFireTempleBlob_00CCC0.bin.inc.c" +}; +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Blob`. + - `Size`: Required. Amount of bytes to extract. Hex. + +※ We usually use blobs when we can't figure out the content's type of chunk of data. + +------------------------- + +### DList + +A.k.a. Display list, or Gfx. + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `DL`. + +------------------------- + +### TextureAnimation + +A data type exclusive to Majora's Mask, that has scrolling, color changing, and texture changing capabilities. Declaring the main array will generate everything else; textures for the TextureCycle type must be declared manually in the XML to use symbols. (If it does reference any undeclared textures, ZAPD will warn and give the their offsets.) + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `TexAnim`. + +------------------------- + +### Scene and Room + +`Scene`s and `Room`s are a bit special, because `Room`s usually needs assets declared in their respective `Scene` (which is in a different file), so they need to be extracted together. + +To accomplish this, the scene and each of their rooms must be declared in the same XML. + +- Example: + +```xml + + + + + + + + + + + + + + +``` + +- Attributes: + + - `HackMode`: Optional. This is a simple non-hardcoded way to handle some edge cases. Valid values: `syotes_room`. + +------------------------- + +### AltHeader + +Like `Scene`s and `Room`s, `AltHeader`s is special too. It should always be declared in the same `File` as a `Scene` or a `Room`. + +- Example: + +```xml + + + + + + + + + + + + + + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `AltHeader`. + +------------------------- + +### Animation + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Anim`. + +------------------------- + +### PlayerAnimation + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Anim`. + +------------------------- + +### CurveAnimation + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Anim`. + - `SkelOffset`: Required. Offset of the `CurveSkeleton` (I.e. a [`Skeleton`](#skeleton) resource with `Type="Curve"`) related to this animation. + +------------------------- + +### LegacyAnimation + +Useful only for the unused `object_human`'s animation data. + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Anim`. + +------------------------- + +### Skeleton + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Skel`. + - `Type`: Required. Valid values: `Normal`, `Flex` and `Curve`. + - `LimbType`: Required. Valid values: `Standard`, `LOD`, `Skin`, `Curve` and `Legacy`. + - `EnumName`: Optional. The name of `typedef`'d limb enum. + - `LimbNone`: Optional. The name of the limb with index zero in the limb enum. + - `LimbMax`: Optional. The name of the max limb index in the limb enum. + +ZAPD is able to generate a limb enum by itself only if all the required data is provided. Providing some but not all the required data would trigger an error and the execution will halt. + +The required data is providing the `EnumName`, `LimbNone` and `LimbMax` attributes in the `Skeleton` or `LimbTable` node and the `EnumName` attribute in every `Limb` of this skeleton. + +※ There are no restrictions in the `Type` and `LimbType` attributes besides the valid values, so any skeleton type can be combined with any limb type. + +------------------------- + +### LimbTable + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Skel`. + - `LimbType`: Required. Valid values: `Standard`, `LOD`, `Skin`, `Curve` and `Legacy`. + - `Count`: Required. Amount of limbs. Integer. + - `EnumName`: Optional. The name of `typedef`'d limb enum. + - `LimbNone`: Optional. The name of the limb with index zero in the limb enum. + - `LimbMax`: Optional. The name of the max limb index in the limb enum. + +See [Skeleton](#skeleton) for info on the limb enum generation. + +------------------------- + +### Limb + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Limb`. + - `LimbType`: Required. Valid values: `Standard`, `LOD`, `Skin`, `Curve` and `Legacy`. + - `EnumName`: Optional. The name used for this limb in the limbs enum. It must be either present in every limb or in none. + +See [Skeleton](#skeleton) for info on the limb enum generation. + +------------------------- + +### Symbol + +A special element that allows declaring a variable without actually extracting it from the current file. Useful when a resource references an element from another file. The symbol will be declared as `extern`. + +- Example: + +```xml + +``` + +Will be declared as: + +```c +extern u8 gJsjutanShadowTex[2048]; +``` + +- Attributes: + + - `Type`: The type of the declared variable. If missing, it will default to `void*`. + - `TypeSize`: The size in bytes of the type. If missing, it will default to `4` (the size of a word and a pointer). Integer or hex value. + - `Count`: Optional. If it is present, the variable will be declared as an array instead of a plain variable. The value of this attribute specifies the length of the array. If `Count` is present but it has no value (`Count=""`), then the length of the array will not be specified either in the declared variable. Integer or hex value. + - `Static`: This attribute can't be enabled on a Symbol node. A warning will be showed in this case. + +------------------------- + +### Collision + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Col`. + +------------------------- + +### Scalar + +Allows the extraction of a single number. + +- Example: + +```xml + +``` + +Will be extracted as: + +```c +u64 pad34F8 = { 0 }; +``` + +- Attributes: + + - `Name`: Required. Suffixed by ~~`TBD`~~. + - `Type`: Required. Valid values: `s8`, `u8`, `x8`, `s16`, `u16`, `x16`, `s32`, `u32`, `x32`, `s64`, `u64`, `x64`, `f32` and `f64`. + +※ Can be wrapped in an [`Array`](#array) tag. + +------------------------- + +### Vector + +Extracts a vector. + +Current supported types are `Vec3s`, `Vec3i` or `Vec3f`. + +- Example: + +```xml + + + +``` + +Will be extracted as: + +```c +Vec3s gLinkPauseChildDekuShieldJointTable[24] = { + { -37, 2346, 93 }, + { 0, 11995, 0 }, + { -16385, -305, -16333 }, + { 0, 51, 12 }, + { 3761, 2263, -384 }, + { 0, 0, 3786 }, + { 1594, 1384, -18344 }, + { -2288, -2428, -1562 }, + { 0, 0, 3219 }, + { -2148, -5, -16840 }, + { 15365, -1708, 15611 }, + { 1761, 8365, 17711 }, + { 0, 0, 18859 }, + { 0, 0, 0 }, + { -9392, -9579, 28686 }, + { 0, 0, -7093 }, + { -2748, 685, -14092 }, + { 213, 6553, -32212 }, + { 0, 0, -1877 }, + { 3267, 3309, -16090 }, + { -18101, 25946, -2670 }, + { -104, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } +}; +``` + +- Attributes: + + - `Name`: Required. Suffixed by ~~`TBD`~~. + - `Type`: Required. Specifies the vector's type (`Vec3s`, `Vec3i` and `Vec3f`). Valid values: `s16`, `s32` and `f32`. + - `Dimensions`: Required. The amount of dimensions of the vector. Valid values: `3`. + +※ Can be wrapped in an [`Array`](#array) tag. + +------------------------- + +### Vtx + +- Example: + +```xml + + + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Vtx`. + +※ Can be wrapped in an [`Array`](#array) tag. + +------------------------- + +### Mtx + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Mtx`. + +※ Explicit use of this tag isn't often necesary because it would probably be extracted automatically by another extracted element. + +------------------------- + +### Cutscene + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Cs`. + +※ Explicit use of this tag isn't often necesary because it would probably be extracted automatically by another extracted element. + +------------------------- + +### Array + +The `Array` element is special, because it needs an inner element to work. It will declare an array of its inner element. + +Currently, only [`Pointer`](#pointer), [`Scalar`](#scalar), [`Vector`](#vector) and [`Vtx`](#vtx) support being wrapped in an array. + +- Example: + +```xml + + + +``` + +- Attributes: + + - `Name`: Required. How the variable will be named. By our convention it should be prefixed by `g`. The sufix is mandated by the element contained. + - `Count`: Required. Amount of elements. Integer. + +------------------------- + +### Path + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `Path`. + - `NumPaths`: Optional. The amount of paths contained in the array. It must be a positive integer. + +------------------------- + +### PlayerAnimationData + +Allows the extraction of the specific data of the player animations which are found in the `link_animetion` file. + +- Example: + +```xml + +``` + +- Attributes: + + - `Name`: Required. Suffixed by `AnimData`. + - `FrameCount`: Required. The length of the animation in frames. It must be a positive integer. + +------------------------- + +### Pointer + +Allows the extraction of a variable that contains a pointer + +- Example: + +```xml + + + +``` + +- Attributes: + + - `Name`: Required. + - `Type`: Required. The type of the extracted pointer. + +※ Can be wrapped in an [`Array`](#array) tag. + +------------------------- diff --git a/ZAPDTR/docs/zapd_xml_spec.md b/ZAPDTR/docs/zapd_xml_spec.md new file mode 100644 index 000000000..9fcc0d4aa --- /dev/null +++ b/ZAPDTR/docs/zapd_xml_spec.md @@ -0,0 +1,55 @@ +# ZAPD XML specification + +ZAPD XMLs use a restrictive subset of the XML standard: any ZAPD XML must be a valid XML (All elements starting with `` ending appropriately with ``, single "empty-element" tags with `/` at the end, etc.). + +Reminder that in + +```xml + + + + + + + + + +``` + +``, ``, `` are *children* of ``, but `` is not. `` is a *descendent* of `` and a child of ``. + +- Every XML's outermost element start/end tag is a single ``. +- The children of a `` must be ``s. +- A `` has *resources* as children. A resource is almost always single empty-element tag, and has one of the types + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + - `` + +- A `` cannot descend from a ``. +- All resources must be children of a ``. +- `` is the only paired resource tag enclosing an element; the element must be a single resource tag, one of + - `` + - `` + - `` diff --git a/ZAPDTR/lib/libgfxd/.gitrepo b/ZAPDTR/lib/libgfxd/.gitrepo new file mode 100644 index 000000000..4c8b7fe76 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = git@github.com:glankk/libgfxd.git + branch = master + commit = 008f73dca8ebc9151b205959b17773a19c5bd0da + parent = c92cfda733aa740a53129a21f69c8abb08a465c7 + method = merge + cmdver = 0.4.3 diff --git a/ZAPDTR/lib/libgfxd/LICENSE b/ZAPDTR/lib/libgfxd/LICENSE new file mode 100644 index 000000000..a7655f829 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016-2021 glank (glankk@github.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ZAPDTR/lib/libgfxd/README.md b/ZAPDTR/lib/libgfxd/README.md new file mode 100644 index 000000000..2e8258943 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/README.md @@ -0,0 +1,478 @@ +## Installing +Run `make` for a single-threaded build, or `make MT=y` for a multi-threaded +build. Copy `libgfxd.a` to your lib directory, and `gfxd.h` to your include +directory. + +## Example usage +Example source code: +``` +#include +#include + +static int macro_fn(void) +{ + /* Print a tab before each macro, and a comma and newline after each + macro */ + gfxd_puts("\t"); + gfxd_macro_dflt(); /* Execute the default macro handler */ + gfxd_puts(",\n"); + + return 0; +} + +int main() +{ + /* Read from stdin and write to stdout */ + gfxd_input_fd(fileno(stdin)); + gfxd_output_fd(fileno(stdout)); + + /* Override the default macro handler to make the output prettier */ + gfxd_macro_fn(macro_fn); + + /* Select F3DEX as the target microcode */ + gfxd_target(gfxd_f3dex); + + /* Set the input endianness to big endian, and the word size to 4 */ + gfxd_endian(gfxd_endian_big, 4); + + /* Print an opening brace */ + gfxd_puts("{\n"); + + /* Execute until the end of input, or until encountering an invalid + command */ + gfxd_execute(); + + /* Print a closing brace */ + gfxd_puts("}\n"); +} +``` + +Example input (binary): +``` +0xe7000000, 0x00000000, +0xfc127e03, 0xfffffdf8, +0xb900031d, 0xc8112078, +0xb6000000, 0x000e0000, +0xb7000000, 0x00012000, +0xfa000000, 0xffffffff, +0x040030bf, 0x000002e0, +0xb1000204, 0x00020604, +0xb1080a0c, 0x000a0e0c, +0xb10a1012, 0x000a120e, +0xb1140200, 0x00140016, +0xb8000000, 0x00000000, +``` + +Example output: +``` +{ + gsDPPipeSync(), + gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, 0, 0, 0, 1, COMBINED, 0, PRIMITIVE, 0, 0, 0, 0, COMBINED), + gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_OPA_SURF2), + gsSPClearGeometryMode(G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR), + gsSPSetGeometryMode(G_CULL_BACK | G_FOG), + gsDPSetPrimColor(0, 0, 0xFF, 0xFF, 0xFF, 0xFF), + gsSPVertex(0x000002E0, 12, 0), + gsSP2Triangles(0, 1, 2, 0, 1, 3, 2, 0), + gsSP2Triangles(4, 5, 6, 0, 5, 7, 6, 0), + gsSP1Quadrangle(5, 8, 9, 7, 0), + gsSP1Quadrangle(10, 1, 0, 11, 0), + gsSPEndDisplayList(), +} +``` + +## Input/output methods +The input consists of any number of `Gfx` packets, and the output is the +decompiled macros in plain-text. The endianness and microcode type of the input +can be set using `gfxd_endian` and `gfxd_target`. + +Several methods of doing I/O are available. No method is selected by default, +meaning there will be no input, and any output will be discarded. + +--- + +##### `void gfxd_input_buffer(const void *buf, int size)` +##### `void gfxd_output_buffer(char *buf, int size)` +Use the buffer pointed to by `buf`, of `size` bytes. + +--- + +##### `void gfxd_input_fd(int fd)` +##### `void gfxd_output_fd(int fd)` +Use `read()` / `write()` with the provided file descriptor, `fd`. + +--- + +##### `typedef int gfxd_input_fn_t(void *buf, int count)` +##### `typedef int gfxd_output_fn_t(const char *buf, int count)` +##### `void gfxd_input_callback(gfxd_input_fn_t *fn)` +##### `void gfxd_output_callback(gfxd_output_fn_t *fn)` +Use the provided callback function, `fn`. `fn` should copy at most `count` +bytes to/from `buf`, and return the number of bytes actually copied. The input +callback should return 0 to signal end of input. + +## Handlers +The macro handler function is responsible for writing the output of each +decompiled macro. The default macro handler is `gfxd_macro_dflt`, but this can +be changed with `gfxd_macro_fn`. The new handler can extend the default +function by calling `gfxd_macro_dflt` within it, or it can override it +completely. + +--- + +##### `int gfxd_macro_dflt()` +The default macro handler. Outputs the macro name, dynamic display list pointer +if one has been specified, and then each argument in order using the function +registered using `gfxd_arg_fn` (`gfxd_arg_dflt` by default), and returns zero. +Because it is designed to be extended, it only outputs the macro text, without +any whitespace or punctuation before or after. When this function is used as +the sole macro handler, it will output the entire display list on one line +without any separation between macros, which is probably not what you want. + +--- + +##### `void gfxd_macro_fn(gfxd_macro_fn_t *fn)` +Set `fn` to be the macro handler function. `fn` can be null, in which case the +handler is reset to the default. + +--- + +##### `void gfxd_arg_dflt(int arg_num)` +The default argument handler for `gfxd_macro_dflt`. For the argument with index +`arg_num`, calls `gfxd_arg_callbacks`, and prints the argument value if the +callback returns zero, or if there is no callback for the given argument. + +--- + +##### `void gfxd_arg_fn(gfxd_arg_fn_t *fn)` +Set `fn` to be the argument handler function, called by `gfxd_macro_dflt`, +for each argument in the current macro, not counting the dynamic display list +pointer if one has been specified. `fn` can be null, in which case the handler +is reset to the default. This only affects the output of `gfxd_macro_dflt`, and +has no observable effect if `gfxd_macro_dflt` is overridden (not extended). + +## Argument callbacks +Callbacks can be registered that will be executed when an argument of a certain +type is encountered. The default argument handler `gfxd_arg_dflt` will execute +callbacks as needed using `gfxd_arg_callbacks`. If a callback returns non-zero, +`gfxd_arg_dflt` will not output anything. This is to allow callbacks to +override the default argument output. Otherwise, `gfxd_arg_dflt` will output +the argument value after the callback function's output. + +--- + +##### `int gfxd_arg_callbacks(int arg_num)` +Examines the argument with index `arg_num` and executes the callback function +for that argument type, if such a callback is supported and has been +registered. This function returns the value that was returned by the callback +function. If no callback function has been registered for the argument type, +zero is returned. + +Most argument callbacks have some extra parameters containing information that +might be relevant to the argument that triggered the callback. The extra +information is extracted only from the current macro, as gfxd does not retain +any context information from previous or subsequent macros. If any of the extra +parameter values is not available in the current macro, the value for that +parameter is substituted with `-1` for signed parameters, and zero for unsigned +parameters. + +--- + +##### `typedef int gfxd_tlut_fn_t(uint32_t tlut, int32_t idx, int32_t count)` +##### `void gfxd_tlut_callback(gfxd_tlut_fn_t *fn)` +Set the callback function for palette arguments. The argument type is +`gfxd_Tlut`. The palette index is in `idx` and the number of colors in `count`. + +--- + +##### `typedef int gfxd_timg_fn_t(uint32_t timg, int32_t fmt, int32_t siz, int32_t width, int32_t height, int32_t pal)` +##### `void gfxd_timg_callback(gfxd_timg_fn_t *fn)` +Set the callback function for texture arguments. The argument type is +`gfxd_Timg`. The image format is in `fmt` and `siz`, the dimensions in `width` +and `height`, and the palette index in `pal`. + +--- + +##### `typedef int gfxd_cimg_fn_t(uint32_t cimg, int32_t fmt, int32_t siz, int32_t width)` +##### `void gfxd_cimg_callback(gfxd_cimg_fn_t *fn)` +Set the callback function for frame buffer arguments. The argument type is +`gfxd_Cimg`. The image format is in `fmt` and `siz`, and the horizontal +resolution in `width`. + +--- + +##### `typedef int gfxd_zimg_fn_t(uint32_t zimg)` +##### `void gfxd_zimg_callback(gfxd_zimg_fn_t *fn)` +Set the callback function for depth buffer arguments. The argument type is +`gfxd_Zimg`. + +--- + +##### `typedef int gfxd_dl_fn_t(uint32_t dl)` +##### `void gfxd_dl_callback(gfxd_dl_fn_t *fn)` +Set the callback function for display list arguments. The argument type is +`gfxd_Dl`. + +--- + +##### `typedef int gfxd_mtx_fn_t(uint32_t mtx)` +##### `void gfxd_mtx_callback(gfxd_mtx_fn_t *fn)` +Set the callback function for matrix arguments. The argument type is +`gfxd_Mtxptr`. + +--- + +##### `typedef int gfxd_lookat_fn_t(uint32_t lookat, int32_t count)` +##### `void gfxd_lookat_callback(gfxd_lookat_fn_t *fn)` +Set the callback function for lookat array arguments. The argument type is +`gfxd_Lookatptr`. The number of lookat structures (1 or 2) is in `count`. + +--- + +##### `typedef int gfxd_light_fn_t(uint32_t light, int32_t count)` +##### `void gfxd_light_callback(gfxd_light_fn_t *fn)` +Set the callback function for light array arguments. The argument type is +`gfxd_Lightptr`. The number of light structures is in `count`. + +--- + +##### `typedef int gfxd_seg_fn_t(uint32_t seg, int32_t num)` +##### `void gfxd_seg_callback(gfxd_seg_fn_t *fn)` +Set the callback function for segment base arguments. The argument type is +`gfxd_Segptr`. The segment number is in `num`. + +--- + +##### `typedef int gfxd_vtx_fn_t(uint32_t vtx, int32_t num)` +##### `void gfxd_vtx_callback(gfxd_vtx_fn_t *fn)` +Set the callback function for vertex array arguments. The argument type is +`gfxd_Vtxptr`. The number of vertex structures is in `num`. + +--- + +##### `typedef int gfxd_vp_fn_t(uint32_t vp)` +##### `void gfxd_vp_callback(gfxd_vp_fn_t *fn)` +Set the callback function for viewport arguments. The argument type is +`gfxd_Vp`. + +--- + +##### `typedef int gfxd_uctext_fn_t(uint32_t text, uint32_t size)` +##### `void gfxd_uctext_callback(gfxd_uctext_fn_t *fn)` +Set the callback function for microcode text arguments. The argument type is +`gfxd_Uctext`. The size of the text segment is in `size`. + +--- + +##### `typedef int gfxd_ucdata_fn_t(uint32_t data, uint32_t size)` +##### `void gfxd_ucdata_callback(gfxd_ucdata_fn_t *fn)` +Set the callback function for microcode data arguments. The argument type is +`gfxd_Ucdata`. The size of the data segment is in `size`. + +--- + +##### `typedef int gfxd_dram_fn_t(uint32_t dram, uint32_t size)` +##### `void gfxd_dram_callback(gfxd_dram_fn_t *fn)` +Set the callback function for generic pointer arguments. The argument type is +`gfxd_Dram`. The size of the data is in `size`. + +## General settings +These functions control general input and output settings. + +--- + +##### `void gfxd_target(gfxd_ucode_t ucode)` +Select `ucode` as the target microcode. `ucode` can be `gfxd_f3d`, `gfxd_f3db`, +`gfxd_f3dex`, `gfxd_f3dexb`, or `gfxd_f3dex2`. The microcode must be selected +before `gfxd_execute`, as no microcode is selected by default. + +--- + +##### `void gfxd_endian(int endian, int wordsize)` +Select `endian` as the endianness of the input, and `wordsize` as the size of +each word in number of bytes. `endian` can be `gfxd_endian_big`, +`gfxd_endian_little`, or `gfxd_endian_host` (the endianness of the host +machine). `wordsize` can be 1, 2, 4, or 8. Big endian is selected by default, +with a word size of 4. + +--- + +##### `void gfxd_dynamic(const char *arg)` +Enable or disable the use of dynamic `g` macros instead of static `gs` macros, +and select the dynamic display list pointer argument to be used. `arg` will be +used by `gfxd_macro_dflt` as the first argument to dynamic macros. If `arg` is +null, dynamic macros are disabled, and `gs` macros are used. Also affects the +result of `gfxd_macro_name`, as it will return either the dynamic or static +version of the macro name as selected by this setting. + +--- + +##### `void gfxd_enable(int cap)` +##### `void gfxd_disable(int cap)` +Enable or disable the feature specified by `cap`. Can be one of the following; +- `gfxd_stop_on_invalid`: Stop execution when encountering an invalid macro. +Enabled by default. +- `gfxd_stop_on_end`: Stop execution when encountering a `SPBranchList` or +`SPEndDisplayList`. Enabled by default. +- `gfxd_emit_dec_color`: Print color components as decimal instead of +hexadecimal. Disabled by default. +- `gfxd_emit_q_macro`: Print fixed-point conversion `q` macros for fixed-point +values. Disabled by default. +- `gfxd_emit_ext_macro`: Emit non-standard macros. Some commands are valid +(though possibly meaningless), but have no macros associated with them, such as +a standalone `G_RDPHALF_1`. When this feature is enabled, such a command will +produce a non-standard `gsDPHalf1` macro instead of a raw hexadecimal command. +Also enables some non-standard multi-packet texture loading macros. Disabled by +default. + +--- + +##### `void gfxd_udata_set(void *ptr)` +##### `void *gfxd_udata_get(void)` +Set or get a generic pointer that can be used to pass user-defined data in and +out of callback functions. + +## Execution +Decompilation is started using the `gfxd_execute` function. When gfxd is +executing (i.e. after `gfxd_execute` has been entered, and before it returns), +the general settings and the I/O settings should not be changed. + +--- + +##### `int gfxd_execute()` +Start executing gfxd with the current settings. For each macro, the macro +handler registered with `gfxd_macro_fn` is called. Execution ends when the +input ends, the macro handler returns non-zero, when an invalid macro is +encountered and `gfxd_stop_on_invalid` is enabled, or when `SPBranchList` or +`SPEndDisplayList` is encountered and `gfxd_stop_on_end` is enabled. If +execution ends due to an invalid macro, `-1` is returned. If execution ends +because the macro handler returns non-zero, the return value from the macro +handler is returned. Otherwise zero is returned. + +## Macro information +The following functions can be used to obtain information about the current +macro and its arguments. They should only be used in custom handlers and +callbacks from within `gfxd_execute`. If used elsewhere, their behavior is +undefined. + +--- + +##### `int gfxd_macro_offset()` +Returns the offset in the input data of the current macro. The offset starts +at zero when `gfxd_execute` is called. + +--- + +##### `int gfxd_macro_packets()` +Returns the number of `Gfx` packets within the current macro. + +--- + +##### `const void *gfxd_macro_data()` +Returns a pointer to the input data for the current macro. The data is not +byte-swapped. The data has a length of `sizeof(Gfx) * gfxd_macro_packets()`. + +--- + +##### `int gfxd_macro_id()` +Returns a number that uniquely identifies the current macro. The number will +be one of the constants in `gfxd.h`. + +--- + +##### `const char *gfxd_macro_name()` +Returns the name of the current macro. If the macro does not have a name (i.e. +it's invalid), null is returned. If a dynamic display list pointer has been +specified, the dynamic `g` version is returned. Otherwise the static `gs` +version is returned. The returned pointer is invalidated by a subsequent call +to `gfxd_macro_name`. + +--- + +##### `int gfxd_arg_count()` +Returns the number of arguments to the current macro, not including a dynamic +display list pointer if one has been specified. + +--- + +##### `int gfxd_arg_type(int arg_num)` +Returns a number that identifies the type of the argument with index `arg_num`. +The number will be one of the constants in `gfxd.h`. + +--- + +##### `const char *gfxd_arg_name(int arg_num)` +Returns the name of the argument with index `arg_num`. Argument names are not +canonical, nor are they needed for macro disassembly, but they can be useful +for informational and diagnostic purposes. + +--- + +##### `int gfxd_arg_fmt(int arg_num)` +Returns the data format of the argument with index `arg_num`. The return value +will be `gfxd_argfmt_i` for `int32_t`, `gfxd_argfmt_u` for `uint32_t`, or +`gfxd_argfmt_f` for `float`. When accessing the value of the argument with +`gfxd_arg_value`, the member with the corresponding type should be used. + +--- + +##### `const gfxd_value_t *gfxd_arg_value(int arg_num)` +Returns a pointer to the value of the argument with index `arg_num`. The value +is a union of type `gfxd_value_t` with the following layout; +``` +typedef union +{ + int32_t i; + uint32_t u; + float f; +} gfxd_value_t +``` + +--- + +##### `const gfxd_value_t *gfxd_value_by_type(int type, int idx)` +Returns a pointer to the value of the argument that is of `type`, and has order +`idx` in all arguments of that type. An `idx` of zero returns the first +argument that has the specified type. If there is no argument with the given +type and order, null is returned. + +--- + +##### `int gfxd_arg_valid(int arg_num)` +Returns non-zero if the argument with index `arg_num` is "valid", for some +definition of valid. An invalid argument generally means that the disassembler +found inconsistencies in the input data, or that the data can not be reproduced +by the current macro type. The argument still has a value that can be printed, +though the value is not guaranteed to make any sense. + +## Custom output +When the default handlers are overridden or extended, the custom handler +functions will want to do some output of their own. The following methods are +available for inserting custom text into the gfxd output. + +--- + +##### `int gfxd_write(const void *buf, int count)` +Insert `count` bytes from the buffer at `buf` into the output. The number of +characters written is returned. + +--- + +##### `int gfxd_puts(const char *str)` +Insert the null-terminated string at `str` into the output. The number of +characters written is returned. + +--- + +##### `int gfxd_printf(const char *fmt, ...)` +Insert the printf-formatted string described by `fmt` and additional arguments +into the output. Limited to 255 characters. The number of characters written is +returned. + +--- + +##### `int gfxd_print_value(int type, const gfxd_value_t *value)` +Insert the type-formatted value into the output. The type should be one of the +constants in `gfxd.h`. The number of characters written is returned. The +macro argument with index `n` can be printed with +`gfxd_print_value(gfxd_arg_type(n), gfxd_arg_value(n))`. diff --git a/ZAPDTR/lib/libgfxd/gbi.h b/ZAPDTR/lib/libgfxd/gbi.h new file mode 100644 index 000000000..69fa7f12e --- /dev/null +++ b/ZAPDTR/lib/libgfxd/gbi.h @@ -0,0 +1,3838 @@ +/** + * gbi.h version 0.3.6 + * n64 graphics microcode interface library + * compatible with fast3d, f3dex, f3dex2, s2dex, and s2dex2 + * + * select a microcode with one of these preprocessor definitions; + * #define F3D_GBI + * for fast3d (selected automatically by default), or + * #define F3DEX_GBI + * for f3dex/s2dex, or + * #define F3DEX_GBI_2 + * for f3dex2/s2dex2 + * + * for early versions of fast3d and f3dex, also define the following; + * #define F3D_BETA + * + * ido incompatibilities; + * - use of c99 variadic macros + * - use of c99 fixed-width integer types + * - use of c99 designated initializers + * - use of c99 compound literals + * - use of c11 _Alignas + * - use of gnu c compound expressions + * - use of gnu c __typeof__ + * + * libultra incompatibilities; + * - many private, undocumented, or obsolete macros not commonly used by + * programmers are missing + * - many different implementation details that will produce matching gbi, + * but not matching code +**/ + +#ifndef N64_GBI_H +#define N64_GBI_H + +#include + +/* use fast3d by default */ +#if !defined(F3D_GBI) && !defined(F3DEX_GBI) && !defined(F3DEX_GBI_2) +# define F3D_GBI +#endif + +/* commands for fast3d and f3dex */ +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define G_SPNOOP 0x00 +# define G_MTX 0x01 +# define G_MOVEMEM 0x03 +# define G_VTX 0x04 +# define G_DL 0x06 +# if defined(F3D_BETA) +# define G_RDPHALF_2 0xB2 +# define G_RDPHALF_1 0xB3 +# define G_PERSPNORM 0xB4 +# else +# define G_RDPHALF_2 0xB3 +# define G_RDPHALF_1 0xB4 +# endif +# define G_LINE3D 0xB5 +# define G_CLEARGEOMETRYMODE 0xB6 +# define G_SETGEOMETRYMODE 0xB7 +# define G_ENDDL 0xB8 +# define G_SETOTHERMODE_L 0xB9 +# define G_SETOTHERMODE_H 0xBA +# define G_TEXTURE 0xBB +# define G_MOVEWORD 0xBC +# define G_POPMTX 0xBD +# define G_CULLDL 0xBE +# define G_TRI1 0xBF +# define G_NOOP 0xC0 +#endif + +/* commands for f3dex */ +#if defined(F3DEX_GBI) +# define G_LOAD_UCODE 0xAF +# define G_BRANCH_Z 0xB0 +# define G_TRI2 0xB1 +# if !defined(F3D_BETA) +# define G_MODIFYVTX 0xB2 +# endif +#endif + +/* commands for f3dex2 */ +#if defined(F3DEX_GBI_2) +# define G_NOOP 0x00 +# define G_VTX 0x01 +# define G_MODIFYVTX 0x02 +# define G_CULLDL 0x03 +# define G_BRANCH_Z 0x04 +# define G_TRI1 0x05 +# define G_TRI2 0x06 +# define G_QUAD 0x07 +# define G_LINE3D 0x08 +# define G_SPECIAL_3 0xD3 +# define G_SPECIAL_2 0xD4 +# define G_SPECIAL_1 0xD5 +# define G_DMA_IO 0xD6 +# define G_TEXTURE 0xD7 +# define G_POPMTX 0xD8 +# define G_GEOMETRYMODE 0xD9 +# define G_MTX 0xDA +# define G_MOVEWORD 0xDB +# define G_MOVEMEM 0xDC +# define G_LOAD_UCODE 0xDD +# define G_DL 0xDE +# define G_ENDDL 0xDF +# define G_SPNOOP 0xE0 +# define G_RDPHALF_1 0xE1 +# define G_SETOTHERMODE_L 0xE2 +# define G_SETOTHERMODE_H 0xE3 +# define G_RDPHALF_2 0xF1 +#endif + +/* rdp commands */ +#define G_TEXRECT 0xE4 +#define G_TEXRECTFLIP 0xE5 +#define G_RDPLOADSYNC 0xE6 +#define G_RDPPIPESYNC 0xE7 +#define G_RDPTILESYNC 0xE8 +#define G_RDPFULLSYNC 0xE9 +#define G_SETKEYGB 0xEA +#define G_SETKEYR 0xEB +#define G_SETCONVERT 0xEC +#define G_SETSCISSOR 0xED +#define G_SETPRIMDEPTH 0xEE +#define G_RDPSETOTHERMODE 0xEF +#define G_LOADTLUT 0xF0 +#define G_SETTILESIZE 0xF2 +#define G_LOADBLOCK 0xF3 +#define G_LOADTILE 0xF4 +#define G_SETTILE 0xF5 +#define G_FILLRECT 0xF6 +#define G_SETFILLCOLOR 0xF7 +#define G_SETFOGCOLOR 0xF8 +#define G_SETBLENDCOLOR 0xF9 +#define G_SETPRIMCOLOR 0xFA +#define G_SETENVCOLOR 0xFB +#define G_SETCOMBINE 0xFC +#define G_SETTIMG 0xFD +#define G_SETZIMG 0xFE +#define G_SETCIMG 0xFF + +/* commands for s2dex */ +#if defined(F3DEX_GBI) +# define G_BG_1CYC 0x01 +# define G_BG_COPY 0x02 +# define G_OBJ_RECTANGLE 0x03 +# define G_OBJ_SPRITE 0x04 +# define G_OBJ_MOVEMEM 0x05 +# define G_SELECT_DL 0xB0 +# define G_OBJ_RENDERMODE 0xB1 +# define G_OBJ_RECTANGLE_R 0xB2 +# define G_OBJ_LOADTXTR 0xC1 +# define G_OBJ_LDTX_SPRITE 0xC2 +# define G_OBJ_LDTX_RECT 0xC3 +# define G_OBJ_LDTX_RECT_R 0xC4 +#endif + +/* commands for s2dex2 */ +#if defined(F3DEX_GBI_2) +# define G_OBJ_RECTANGLE 0x01 +# define G_OBJ_SPRITE 0x02 +# define G_SELECT_DL 0x04 +# define G_OBJ_LOADTXTR 0x05 +# define G_OBJ_LDTX_SPRITE 0x06 +# define G_OBJ_LDTX_RECT 0x07 +# define G_OBJ_LDTX_RECT_R 0x08 +# define G_BG_1CYC 0x09 +# define G_BG_COPY 0x0A +# define G_OBJ_RENDERMODE 0x0B +# define G_OBJ_RECTANGLE_R 0xDA +# define G_OBJ_MOVEMEM 0xDC +#endif + +/* commands for s2dex and s2dex2 */ +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +# define G_RDPHALF_0 0xE4 +#endif + +/* image formats */ +#define G_IM_FMT_RGBA 0 +#define G_IM_FMT_YUV 1 +#define G_IM_FMT_CI 2 +#define G_IM_FMT_IA 3 +#define G_IM_FMT_I 4 +#define G_IM_SIZ_4b 0 +#define G_IM_SIZ_8b 1 +#define G_IM_SIZ_16b 2 +#define G_IM_SIZ_32b 3 + +/* texture settings */ +#define G_TX_NOMIRROR (gI_(0b0) << 0) +#define G_TX_MIRROR (gI_(0b1) << 0) +#define G_TX_WRAP (gI_(0b0) << 1) +#define G_TX_CLAMP (gI_(0b1) << 1) +#define G_TX_NOMASK gI_(0) +#define G_TX_NOLOD gI_(0) +#define G_OFF gI_(0) +#define G_ON gI_(1) + +/* tile indices */ +#define G_TX_LOADTILE 7 +#define G_TX_RENDERTILE 0 + +/* loadblock constants */ +#define G_TX_DXT_FRAC 11 +#define G_TX_LDBLK_MAX_TXL 2047 + +/* geometry mode */ +#define G_ZBUFFER (gI_(0b1) << 0) +#define G_SHADE (gI_(0b1) << 2) +#define G_CULL_BOTH (G_CULL_FRONT | G_CULL_BACK) +#define G_FOG (gI_(0b1) << 16) +#define G_LIGHTING (gI_(0b1) << 17) +#define G_TEXTURE_GEN (gI_(0b1) << 18) +#define G_TEXTURE_GEN_LINEAR (gI_(0b1) << 19) +#define G_LOD (gI_(0b1) << 20) + +/* geometry mode for fast3d */ +#if defined(F3D_GBI) +# define G_CLIPPING (gI_(0b0) << 0) +#endif + +/* geometry mode for fast3d and f3dex */ +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define G_TEXTURE_ENABLE (gI_(0b1) << 1) +# define G_SHADING_SMOOTH (gI_(0b1) << 9) +# define G_CULL_FRONT (gI_(0b1) << 12) +# define G_CULL_BACK (gI_(0b1) << 13) +#endif + +/* geometry mode for f3dex and f3dex2 */ +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +# define G_CLIPPING (gI_(0b1) << 23) +#endif + +/* geometry mode for f3dex2 */ +#if defined(F3DEX_GBI_2) +# define G_TEXTURE_ENABLE (gI_(0b0) << 0) +# define G_CULL_FRONT (gI_(0b1) << 9) +# define G_CULL_BACK (gI_(0b1) << 10) +# define G_SHADING_SMOOTH (gI_(0b1) << 21) +#endif + +/* othermode lo */ +#define G_MDSFT_ALPHACOMPARE 0 +#define G_MDSFT_ZSRCSEL 2 +#define G_MDSFT_RENDERMODE 3 +#define G_MDSFT_BLENDER 16 +#define G_MDSIZ_ALPHACOMPARE 2 +#define G_MDSIZ_ZSRCSEL 1 +#define G_MDSIZ_RENDERMODE 29 +#define G_MDSIZ_BLENDER 13 + +#define G_AC_NONE (gI_(0b00) << G_MDSFT_ALPHACOMPARE) +#define G_AC_THRESHOLD (gI_(0b01) << G_MDSFT_ALPHACOMPARE) +#define G_AC_DITHER (gI_(0b11) << G_MDSFT_ALPHACOMPARE) +#define G_ZS_PIXEL (gI_(0b0) << G_MDSFT_ZSRCSEL) +#define G_ZS_PRIM (gI_(0b1) << G_MDSFT_ZSRCSEL) +#define AA_EN (gI_(0b1) << (G_MDSFT_RENDERMODE + 0)) +#define Z_CMP (gI_(0b1) << (G_MDSFT_RENDERMODE + 1)) +#define Z_UPD (gI_(0b1) << (G_MDSFT_RENDERMODE + 2)) +#define IM_RD (gI_(0b1) << (G_MDSFT_RENDERMODE + 3)) +#define CLR_ON_CVG (gI_(0b1) << (G_MDSFT_RENDERMODE + 4)) +#define CVG_DST_CLAMP (gI_(0b00) << (G_MDSFT_RENDERMODE + 5)) +#define CVG_DST_WRAP (gI_(0b01) << (G_MDSFT_RENDERMODE + 5)) +#define CVG_DST_FULL (gI_(0b10) << (G_MDSFT_RENDERMODE + 5)) +#define CVG_DST_SAVE (gI_(0b11) << (G_MDSFT_RENDERMODE + 5)) +#define ZMODE_OPA (gI_(0b00) << (G_MDSFT_RENDERMODE + 7)) +#define ZMODE_INTER (gI_(0b01) << (G_MDSFT_RENDERMODE + 7)) +#define ZMODE_XLU (gI_(0b10) << (G_MDSFT_RENDERMODE + 7)) +#define ZMODE_DEC (gI_(0b11) << (G_MDSFT_RENDERMODE + 7)) +#define CVG_X_ALPHA (gI_(0b1) << (G_MDSFT_RENDERMODE + 9)) +#define ALPHA_CVG_SEL (gI_(0b1) << (G_MDSFT_RENDERMODE + 10)) +#define FORCE_BL (gI_(0b1) << (G_MDSFT_RENDERMODE + 11)) + +#define G_BL_1MA gI_(0b00) +#define G_BL_1 gI_(0b10) +#define G_BL_0 gI_(0b11) +#define G_BL_CLR_IN gI_(0b00) +#define G_BL_CLR_MEM gI_(0b01) +#define G_BL_CLR_BL gI_(0b10) +#define G_BL_CLR_FOG gI_(0b11) +#define G_BL_A_IN gI_(0b00) +#define G_BL_A_FOG gI_(0b01) +#define G_BL_A_MEM gI_(0b01) +#define G_BL_A_SHADE gI_(0b10) + +#define GBL_c1(p, a, m, b) \ + ( \ + gF_(p, 2, 30) | \ + gF_(a, 2, 26) | \ + gF_(m, 2, 22) | \ + gF_(b, 2, 18) \ + ) +#define GBL_c2(p, a, m, b) \ + ( \ + gF_(p, 2, 28) | \ + gF_(a, 2, 24) | \ + gF_(m, 2, 20) | \ + gF_(b, 2, 16) \ + ) + +/* render modes */ +#define G_RM_OPA_SURF \ + ( \ + CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_OPA_SURF2 \ + ( \ + CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_AA_OPA_SURF \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_OPA_SURF2 \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_OPA_SURF \ + ( \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_OPA_SURF2 \ + ( \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_ZB_OPA_SURF \ + ( \ + Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_ZB_OPA_SURF2 \ + ( \ + Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_OPA_SURF \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_OPA_SURF2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_ZB_OPA_SURF \ + ( \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_ZB_OPA_SURF2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) + +#define G_RM_XLU_SURF \ + ( \ + IM_RD | CVG_DST_FULL | ZMODE_OPA | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_XLU_SURF2 \ + ( \ + IM_RD | CVG_DST_FULL | ZMODE_OPA | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_XLU_SURF \ + ( \ + AA_EN | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | ZMODE_OPA | \ + FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_XLU_SURF2 \ + ( \ + AA_EN | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | ZMODE_OPA | \ + FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_ZB_XLU_SURF \ + ( \ + Z_CMP | IM_RD | CVG_DST_FULL | ZMODE_XLU | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_ZB_XLU_SURF2 \ + ( \ + Z_CMP | IM_RD | CVG_DST_FULL | ZMODE_XLU | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_XLU_SURF \ + ( \ + AA_EN | Z_CMP | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | \ + ZMODE_XLU | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_XLU_SURF2 \ + ( \ + AA_EN | Z_CMP | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | \ + ZMODE_XLU | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_ZB_OPA_DECAL \ + ( \ + Z_CMP | CVG_DST_FULL | ZMODE_DEC | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_ZB_OPA_DECAL2 \ + ( \ + Z_CMP | CVG_DST_FULL | ZMODE_DEC | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_OPA_DECAL \ + ( \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ZMODE_DEC | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_OPA_DECAL2 \ + ( \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ZMODE_DEC | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_ZB_OPA_DECAL \ + ( \ + AA_EN | Z_CMP | CVG_DST_WRAP | ZMODE_DEC | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_ZB_OPA_DECAL2 \ + ( \ + AA_EN | Z_CMP | CVG_DST_WRAP | ZMODE_DEC | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) + +#define G_RM_ZB_XLU_DECAL \ + ( \ + Z_CMP | IM_RD | CVG_DST_FULL | ZMODE_DEC | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_ZB_XLU_DECAL2 \ + ( \ + Z_CMP | IM_RD | CVG_DST_FULL | ZMODE_DEC | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_XLU_DECAL \ + ( \ + AA_EN | Z_CMP | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | \ + ZMODE_DEC | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_XLU_DECAL2 \ + ( \ + AA_EN | Z_CMP | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | \ + ZMODE_DEC | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_AA_ZB_OPA_INTER \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_INTER | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_OPA_INTER2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_INTER | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_ZB_OPA_INTER \ + ( \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ZMODE_INTER | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_RA_ZB_OPA_INTER2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ZMODE_INTER | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) + +#define G_RM_AA_ZB_XLU_INTER \ + ( \ + AA_EN | Z_CMP | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | \ + ZMODE_INTER | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_XLU_INTER2 \ + ( \ + AA_EN | Z_CMP | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | \ + ZMODE_INTER | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_AA_XLU_LINE \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_XLU_LINE2 \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_XLU_LINE \ + ( \ + AA_EN | Z_CMP | IM_RD | CVG_DST_CLAMP | ZMODE_XLU | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_XLU_LINE2 \ + ( \ + AA_EN | Z_CMP | IM_RD | CVG_DST_CLAMP | ZMODE_XLU | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_AA_DEC_LINE \ + ( \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_DEC_LINE2 \ + ( \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_DEC_LINE \ + ( \ + AA_EN | Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_DEC | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_DEC_LINE2 \ + ( \ + AA_EN | Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_DEC | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + + +#define G_RM_TEX_EDGE \ + ( \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_TEX_EDGE2 \ + ( \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_AA_TEX_EDGE \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_TEX_EDGE2 \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_TEX_EDGE \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_TEX_EDGE2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) + +#define G_RM_AA_ZB_TEX_INTER \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_INTER | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_TEX_INTER2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_INTER | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) + +#define G_RM_AA_SUB_SURF \ + ( \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_SUB_SURF2 \ + ( \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_SUB_SURF \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) +#define G_RM_AA_ZB_SUB_SURF2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) \ + ) + +#define G_RM_PCL_SURF \ + ( \ + G_AC_DITHER | CVG_DST_FULL | ZMODE_OPA | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_PCL_SURF2 \ + ( \ + G_AC_DITHER | CVG_DST_FULL | ZMODE_OPA | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_AA_PCL_SURF \ + ( \ + G_AC_DITHER | AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_PCL_SURF2 \ + ( \ + G_AC_DITHER | AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_ZB_PCL_SURF \ + ( \ + G_AC_DITHER | Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | \ + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_ZB_PCL_SURF2 \ + ( \ + G_AC_DITHER | Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | \ + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_AA_ZB_PCL_SURF \ + ( \ + G_AC_DITHER | AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_PCL_SURF2 \ + ( \ + G_AC_DITHER | AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_AA_OPA_TERR \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_OPA_TERR2 \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_OPA_TERR \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_OPA_TERR2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_AA_TEX_TERR \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_TEX_TERR2 \ + ( \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_TEX_TERR \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_TEX_TERR2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_AA_SUB_TERR \ + ( \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_SUB_TERR2 \ + ( \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_SUB_TERR \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_ZB_SUB_TERR2 \ + ( \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_CLD_SURF \ + ( \ + IM_RD | CVG_DST_SAVE | ZMODE_OPA | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_CLD_SURF2 \ + ( \ + IM_RD | CVG_DST_SAVE | ZMODE_OPA | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_ZB_CLD_SURF \ + ( \ + Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_XLU | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_ZB_CLD_SURF2 \ + ( \ + Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_XLU | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_ZB_OVL_SURF \ + ( \ + Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_DEC | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_ZB_OVL_SURF2 \ + ( \ + Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_DEC | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) + +#define G_RM_ADD \ + ( \ + IM_RD | CVG_DST_SAVE | ZMODE_OPA | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1) \ + ) +#define G_RM_ADD2 \ + ( \ + IM_RD | CVG_DST_SAVE | ZMODE_OPA | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1) \ + ) + +#define G_RM_FOG_SHADE_A \ + GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA) + +#define G_RM_FOG_PRIM_A \ + GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_IN, G_BL_1MA) + +#define G_RM_PASS \ + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define G_RM_VISCVG \ + ( \ + IM_RD | FORCE_BL | \ + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_BL, G_BL_A_MEM) \ + ) +#define G_RM_VISCVG2 \ + ( \ + IM_RD | FORCE_BL | \ + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_BL, G_BL_A_MEM) \ + ) + +#define G_RM_OPA_CI \ + ( \ + CVG_DST_CLAMP | ZMODE_OPA | \ + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) +#define G_RM_OPA_CI2 \ + ( \ + CVG_DST_CLAMP | ZMODE_OPA | \ + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) \ + ) + +#define G_RM_NOOP GBL_c1(0, 0, 0, 0) +#define G_RM_NOOP2 GBL_c2(0, 0, 0, 0) + +#define G_RM_SPRITE G_RM_OPA_SURF +#define G_RM_SPRITE2 G_RM_OPA_SURF2 +#define G_RM_RA_SPRITE \ + ( \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | \ + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_RA_SPRITE2 \ + ( \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | \ + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) \ + ) +#define G_RM_AA_SPRITE G_RM_AA_TEX_TERR +#define G_RM_AA_SPRITE2 G_RM_AA_TEX_TERR2 +#define G_RM_XLU_SPRITE G_RM_XLU_SURF +#define G_RM_XLU_SPRITE2 G_RM_XLU_SURF2 +#define G_RM_AA_XLU_SPRITE G_RM_AA_XLU_SURF +#define G_RM_AA_XLU_SPRITE2 G_RM_AA_XLU_SURF2 + +#define G_OBJRM_NOTXCLAMP (gI_(0b1) << 0) +#define G_OBJRM_XLU (gI_(0b1) << 1) +#define G_OBJRM_ANTIALIAS (gI_(0b1) << 2) +#define G_OBJRM_BILERP (gI_(0b1) << 3) +#define G_OBJRM_SHRINKSIZE_1 (gI_(0b1) << 4) +#define G_OBJRM_SHRINKSIZE_2 (gI_(0b1) << 5) +#define G_OBJRM_WIDEN (gI_(0b1) << 6) + +/* othermode hi */ +#define G_MDSFT_ALPHADITHER 4 +#define G_MDSFT_RGBDITHER 6 +#define G_MDSFT_COMBKEY 8 +#define G_MDSFT_TEXTCONV 9 +#define G_MDSFT_TEXTFILT 12 +#define G_MDSFT_TEXTLUT 14 +#define G_MDSFT_TEXTLOD 16 +#define G_MDSFT_TEXTDETAIL 17 +#define G_MDSFT_TEXTPERSP 19 +#define G_MDSFT_CYCLETYPE 20 +#define G_MDSFT_PIPELINE 23 +#define G_MDSIZ_ALPHADITHER 2 +#define G_MDSIZ_RGBDITHER 2 +#define G_MDSIZ_COMBKEY 1 +#define G_MDSIZ_TEXTCONV 3 +#define G_MDSIZ_TEXTFILT 2 +#define G_MDSIZ_TEXTLUT 2 +#define G_MDSIZ_TEXTLOD 1 +#define G_MDSIZ_TEXTDETAIL 2 +#define G_MDSIZ_TEXTPERSP 1 +#define G_MDSIZ_CYCLETYPE 2 +#define G_MDSIZ_PIPELINE 1 + +#define G_AD_PATTERN (gI_(0b00) << G_MDSFT_ALPHADITHER) +#define G_AD_NOTPATTERN (gI_(0b01) << G_MDSFT_ALPHADITHER) +#define G_AD_NOISE (gI_(0b10) << G_MDSFT_ALPHADITHER) +#define G_AD_DISABLE (gI_(0b11) << G_MDSFT_ALPHADITHER) +#define G_CD_MAGICSQ (gI_(0b00) << G_MDSFT_RGBDITHER) +#define G_CD_BAYER (gI_(0b01) << G_MDSFT_RGBDITHER) +#define G_CD_NOISE (gI_(0b10) << G_MDSFT_RGBDITHER) +#define G_CD_DISABLE (gI_(0b11) << G_MDSFT_RGBDITHER) +#define G_CD_ENABLE (gI_(0b10) << G_MDSFT_RGBDITHER) +#define G_CK_NONE (gI_(0b0) << G_MDSFT_COMBKEY) +#define G_CK_KEY (gI_(0b1) << G_MDSFT_COMBKEY) +#define G_TC_CONV (gI_(0b000) << G_MDSFT_TEXTCONV) +#define G_TC_FILTCONV (gI_(0b101) << G_MDSFT_TEXTCONV) +#define G_TC_FILT (gI_(0b110) << G_MDSFT_TEXTCONV) +#define G_TF_POINT (gI_(0b00) << G_MDSFT_TEXTFILT) +#define G_TF_BILERP (gI_(0b10) << G_MDSFT_TEXTFILT) +#define G_TF_AVERAGE (gI_(0b11) << G_MDSFT_TEXTFILT) +#define G_TT_NONE (gI_(0b00) << G_MDSFT_TEXTLUT) +#define G_TT_RGBA16 (gI_(0b10) << G_MDSFT_TEXTLUT) +#define G_TT_IA16 (gI_(0b11) << G_MDSFT_TEXTLUT) +#define G_TL_TILE (gI_(0b0) << G_MDSFT_TEXTLOD) +#define G_TL_LOD (gI_(0b1) << G_MDSFT_TEXTLOD) +#define G_TD_CLAMP (gI_(0b00) << G_MDSFT_TEXTDETAIL) +#define G_TD_SHARPEN (gI_(0b01) << G_MDSFT_TEXTDETAIL) +#define G_TD_DETAIL (gI_(0b10) << G_MDSFT_TEXTDETAIL) +#define G_TP_NONE (gI_(0b0) << G_MDSFT_TEXTPERSP) +#define G_TP_PERSP (gI_(0b1) << G_MDSFT_TEXTPERSP) +#define G_CYC_1CYCLE (gI_(0b00) << G_MDSFT_CYCLETYPE) +#define G_CYC_2CYCLE (gI_(0b01) << G_MDSFT_CYCLETYPE) +#define G_CYC_COPY (gI_(0b10) << G_MDSFT_CYCLETYPE) +#define G_CYC_FILL (gI_(0b11) << G_MDSFT_CYCLETYPE) +#define G_PM_NPRIMITIVE (gI_(0b0) << G_MDSFT_PIPELINE) +#define G_PM_1PRIMITIVE (gI_(0b1) << G_MDSFT_PIPELINE) + +/* color conversion constants */ +#define G_CV_K0 (175) +#define G_CV_K1 (-43) +#define G_CV_K2 (-89) +#define G_CV_K3 (222) +#define G_CV_K4 (114) +#define G_CV_K5 (42) + +/* color combiner */ +#define G_CCMUX_COMBINED 0 +#define G_CCMUX_TEXEL0 1 +#define G_CCMUX_TEXEL1 2 +#define G_CCMUX_PRIMITIVE 3 +#define G_CCMUX_SHADE 4 +#define G_CCMUX_ENVIRONMENT 5 +#define G_CCMUX_1 6 +#define G_CCMUX_NOISE 7 +#define G_CCMUX_0 31 +#define G_CCMUX_CENTER 6 +#define G_CCMUX_K4 7 +#define G_CCMUX_SCALE 6 +#define G_CCMUX_COMBINED_ALPHA 7 +#define G_CCMUX_TEXEL0_ALPHA 8 +#define G_CCMUX_TEXEL1_ALPHA 9 +#define G_CCMUX_PRIMITIVE_ALPHA 10 +#define G_CCMUX_SHADE_ALPHA 11 +#define G_CCMUX_ENV_ALPHA 12 +#define G_CCMUX_LOD_FRACTION 13 +#define G_CCMUX_PRIM_LOD_FRAC 14 +#define G_CCMUX_K5 15 +#define G_ACMUX_COMBINED 0 +#define G_ACMUX_TEXEL0 1 +#define G_ACMUX_TEXEL1 2 +#define G_ACMUX_PRIMITIVE 3 +#define G_ACMUX_SHADE 4 +#define G_ACMUX_ENVIRONMENT 5 +#define G_ACMUX_1 6 +#define G_ACMUX_0 7 +#define G_ACMUX_LOD_FRACTION 0 +#define G_ACMUX_PRIM_LOD_FRAC 6 + +/* + * combine modes + * ( A - B ) * C + D +*/ +#define G_CC_MODULATEI \ + TEXEL0, 0, SHADE, 0, \ + 0, 0, 0, SHADE +#define G_CC_MODULATEIA \ + TEXEL0, 0, SHADE, 0, \ + TEXEL0, 0, SHADE, 0 +#define G_CC_MODULATEIDECALA \ + TEXEL0, 0, SHADE, 0, \ + 0, 0, 0, TEXEL0 +#define G_CC_MODULATERGB \ + G_CC_MODULATEI +#define G_CC_MODULATERGBA \ + G_CC_MODULATEIA +#define G_CC_MODULATERGBDECALA \ + G_CC_MODULATEIDECALA +#define G_CC_MODULATEI_PRIM \ + TEXEL0, 0, PRIMITIVE, 0, \ + 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM \ + TEXEL0, 0, PRIMITIVE, 0, \ + TEXEL0, 0, PRIMITIVE, 0 +#define G_CC_MODULATEIDECALA_PRIM \ + TEXEL0, 0, PRIMITIVE, 0, \ + 0, 0, 0, TEXEL0 +#define G_CC_MODULATERGB_PRIM \ + G_CC_MODULATEI_PRIM +#define G_CC_MODULATERGBA_PRIM \ + G_CC_MODULATEIA_PRIM +#define G_CC_MODULATERGBDECALA_PRIM \ + G_CC_MODULATEIDECALA_PRIM +#define G_CC_DECALRGB \ + 0, 0, 0, TEXEL0, \ + 0, 0, 0, SHADE +#define G_CC_DECALRGBA \ + 0, 0, 0, TEXEL0, \ + 0, 0, 0, TEXEL0 +#define G_CC_BLENDI \ + ENVIRONMENT, SHADE, TEXEL0, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_BLENDIA \ + ENVIRONMENT, SHADE, TEXEL0, SHADE, \ + TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDIDECALA \ + ENVIRONMENT, SHADE, TEXEL0, SHADE, \ + 0, 0, 0, TEXEL0 +#define G_CC_BLENDRGBA \ + TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_BLENDRGBDECALA \ + TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, \ + 0, 0, 0, TEXEL0 +#define G_CC_REFLECTRGB \ + ENVIRONMENT, 0, TEXEL0, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_REFLECTRGBDECALA \ + ENVIRONMENT, 0, TEXEL0, SHADE, \ + 0, 0, 0, TEXEL0 +#define G_CC_HILITERGB \ + PRIMITIVE, SHADE, TEXEL0, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_HILITERGBA \ + PRIMITIVE, SHADE, TEXEL0, SHADE, \ + PRIMITIVE, SHADE, TEXEL0, SHADE +#define G_CC_HILITERGBDECALA \ + PRIMITIVE, SHADE, TEXEL0, SHADE, \ + 0, 0, 0, TEXEL0 +#define G_CC_1CYUV2RGB \ + TEXEL0, K4, K5, TEXEL0, \ + 0, 0, 0, SHADE +#define G_CC_PRIMITIVE \ + 0, 0, 0, PRIMITIVE, \ + 0, 0, 0, PRIMITIVE +#define G_CC_SHADE \ + 0, 0, 0, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_ADDRGB \ + 1, 0, TEXEL0, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_ADDRGBDECALA \ + 1, 0, TEXEL0, SHADE, \ + 0, 0, 0, TEXEL0 +#define G_CC_SHADEDECALA \ + 0, 0, 0, SHADE, \ + 0, 0, 0, TEXEL0 +#define G_CC_BLENDPE \ + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, \ + TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDPEDECALA \ + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, \ + 0, 0, 0, TEXEL0 +#define G_CC_TRILERP \ + TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0, \ + TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0 +#define G_CC_TEMPLERP \ + TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, \ + TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0 +#define G_CC_INTERFERENCE \ + TEXEL0, 0, TEXEL1, 0, \ + TEXEL0, 0, TEXEL1, 0 +#define _G_CC_BLENDPE \ + ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, \ + TEXEL0, 0, SHADE, 0 +#define _G_CC_BLENDPEDECALA \ + ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, \ + 0, 0, 0, TEXEL0 +#define _G_CC_SPARSEST \ + PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0, \ + PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0 +#define _G_CC_TWOCOLORTEX \ + PRIMITIVE, SHADE, TEXEL0, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_MODULATEI2 \ + COMBINED, 0, SHADE, 0, \ + 0, 0, 0, SHADE +#define G_CC_MODULATEIA2 \ + COMBINED, 0, SHADE, 0, \ + COMBINED, 0, SHADE, 0 +#define G_CC_MODULATERGB2 \ + G_CC_MODULATEI2 +#define G_CC_MODULATERGBA2 \ + G_CC_MODULATEIA2 +#define G_CC_MODULATEI_PRIM2 \ + COMBINED, 0, PRIMITIVE, 0, \ + 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM2 \ + COMBINED, 0, PRIMITIVE, 0, \ + COMBINED, 0, PRIMITIVE, 0 +#define G_CC_MODULATERGB_PRIM2 \ + G_CC_MODULATEI_PRIM2 +#define G_CC_MODULATERGBA_PRIM2 \ + G_CC_MODULATEIA_PRIM2 +#define G_CC_DECALRGB2 \ + 0, 0, 0, COMBINED, \ + 0, 0, 0, SHADE +#define G_CC_BLENDI2 \ + ENVIRONMENT, SHADE, COMBINED, SHADE, \ + 0, 0, 0, SHADE +#define G_CC_BLENDIA2 \ + ENVIRONMENT, SHADE, COMBINED, SHADE, \ + COMBINED, 0, SHADE, 0 +#define G_CC_HILITERGB2 \ + ENVIRONMENT, COMBINED, TEXEL0, COMBINED, \ + 0, 0, 0, SHADE +#define G_CC_HILITERGBA2 \ + ENVIRONMENT, COMBINED, TEXEL0, COMBINED, \ + ENVIRONMENT, COMBINED, TEXEL0, COMBINED +#define G_CC_HILITERGBDECALA2 \ + ENVIRONMENT, COMBINED, TEXEL0, COMBINED, \ + 0, 0, 0, TEXEL0 +#define G_CC_HILITERGBPASSA2 \ + ENVIRONMENT, COMBINED, TEXEL0, COMBINED, \ + 0, 0, 0, COMBINED +#define G_CC_CHROMA_KEY2 \ + TEXEL0, CENTER, SCALE, 0, \ + 0, 0, 0, 0 +#define G_CC_YUV2RGB \ + TEXEL1, K4, K5, TEXEL1, \ + 0, 0, 0, 0 +#define G_CC_PASS2 \ + 0, 0, 0, COMBINED, \ + 0, 0, 0, COMBINED +#define G_CC_LERP(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, \ + a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ + ( \ + gFL_(G_CCMUX_##a0, 4, 52) | \ + gFL_(G_CCMUX_##c0, 5, 47) | \ + gFL_(G_ACMUX_##Aa0, 3, 44) | \ + gFL_(G_ACMUX_##Ac0, 3, 41) | \ + gFL_(G_CCMUX_##a1, 4, 37) | \ + gFL_(G_CCMUX_##c1, 5, 32) | \ + gFL_(G_CCMUX_##b0, 4, 28) | \ + gFL_(G_CCMUX_##b1, 4, 24) | \ + gFL_(G_ACMUX_##Aa1, 3, 21) | \ + gFL_(G_ACMUX_##Ac1, 3, 18) | \ + gFL_(G_CCMUX_##d0, 3, 15) | \ + gFL_(G_ACMUX_##Ab0, 3, 12) | \ + gFL_(G_ACMUX_##Ad0, 3, 9) | \ + gFL_(G_CCMUX_##d1, 3, 6) | \ + gFL_(G_ACMUX_##Ab1, 3, 3) | \ + gFL_(G_ACMUX_##Ad1, 3, 0) \ + ) +#define G_CC_MODE(mode1, mode2) G_CC_LERP(mode1, mode2) + +/* scissor modes */ +#define G_SC_NON_INTERLACE gI_(0b00) +#define G_SC_EVEN_INTERLACE gI_(0b10) +#define G_SC_ODD_INTERLACE gI_(0b11) + +/* display list branch flags */ +#define G_DL_PUSH gI_(0b0) +#define G_DL_NOPUSH gI_(0b1) + +/* conditional branching flags (f3dex and f3dex2) */ +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +# define G_BZ_PERSP 0 +# define G_BZ_ORTHO 1 +#endif + +/* matrix params */ +#define G_MTX_MUL (gI_(0b0) << 1) +#define G_MTX_LOAD (gI_(0b1) << 1) + +/* matrix params for fast3d and f3dex */ +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define G_MTX_MODELVIEW (gI_(0b0) << 0) +# define G_MTX_PROJECTION (gI_(0b1) << 0) +# define G_MTX_NOPUSH (gI_(0b0) << 2) +# define G_MTX_PUSH (gI_(0b1) << 2) +#endif + +/* matrix params for f3dex2 */ +#if defined(F3DEX_GBI_2) +# define G_MTX_NOPUSH (gI_(0b0) << 0) +# define G_MTX_PUSH (gI_(0b1) << 0) +# define G_MTX_MODELVIEW (gI_(0b0) << 2) +# define G_MTX_PROJECTION (gI_(0b1) << 2) +#endif + +/* moveword indices */ +#define G_MW_MATRIX 0 +#define G_MW_NUMLIGHT 2 +#define G_MW_CLIP 4 +#define G_MW_SEGMENT 6 +#define G_MW_FOG 8 +#define G_MW_GENSTAT 8 +#define G_MW_LIGHTCOL 10 +#define G_MW_PERSPNORM 14 + +/* moveword indices for fast3d and f3dex */ +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define G_MW_POINTS 12 +#endif + +/* moveword indices for f3dex2 */ +#if defined(F3DEX_GBI_2) +# define G_MW_FORCEMTX 12 +#endif + +/* moveword offsets */ +#define G_MWO_NUMLIGHT gI_(0x00) +#define G_MWO_CLIP_RNX gI_(0x04) +#define G_MWO_CLIP_RNY gI_(0x0C) +#define G_MWO_CLIP_RPX gI_(0x14) +#define G_MWO_CLIP_RPY gI_(0x1C) +#define G_MWO_SEGMENT_0 gI_(0x00) +#define G_MWO_SEGMENT_1 gI_(0x04) +#define G_MWO_SEGMENT_2 gI_(0x08) +#define G_MWO_SEGMENT_3 gI_(0x0C) +#define G_MWO_SEGMENT_4 gI_(0x10) +#define G_MWO_SEGMENT_5 gI_(0x14) +#define G_MWO_SEGMENT_6 gI_(0x18) +#define G_MWO_SEGMENT_7 gI_(0x1C) +#define G_MWO_SEGMENT_8 gI_(0x20) +#define G_MWO_SEGMENT_9 gI_(0x24) +#define G_MWO_SEGMENT_A gI_(0x28) +#define G_MWO_SEGMENT_B gI_(0x2C) +#define G_MWO_SEGMENT_C gI_(0x30) +#define G_MWO_SEGMENT_D gI_(0x34) +#define G_MWO_SEGMENT_E gI_(0x38) +#define G_MWO_SEGMENT_F gI_(0x3C) +#define G_MWO_FOG gI_(0x00) +#define G_MWO_aLIGHT_1 gI_(0x00) +#define G_MWO_bLIGHT_1 gI_(0x04) +#define G_MWO_MATRIX_XX_XY_I gI_(0x00) +#define G_MWO_MATRIX_XZ_XW_I gI_(0x04) +#define G_MWO_MATRIX_YX_YY_I gI_(0x08) +#define G_MWO_MATRIX_YZ_YW_I gI_(0x0C) +#define G_MWO_MATRIX_ZX_ZY_I gI_(0x10) +#define G_MWO_MATRIX_ZZ_ZW_I gI_(0x14) +#define G_MWO_MATRIX_WX_WY_I gI_(0x18) +#define G_MWO_MATRIX_WZ_WW_I gI_(0x1C) +#define G_MWO_MATRIX_XX_XY_F gI_(0x20) +#define G_MWO_MATRIX_XZ_XW_F gI_(0x24) +#define G_MWO_MATRIX_YX_YY_F gI_(0x28) +#define G_MWO_MATRIX_YZ_YW_F gI_(0x2C) +#define G_MWO_MATRIX_ZX_ZY_F gI_(0x30) +#define G_MWO_MATRIX_ZZ_ZW_F gI_(0x34) +#define G_MWO_MATRIX_WX_WY_F gI_(0x38) +#define G_MWO_MATRIX_WZ_WW_F gI_(0x3C) +#define G_MWO_POINT_RGBA gI_(0x10) +#define G_MWO_POINT_ST gI_(0x14) +#define G_MWO_POINT_XYSCREEN gI_(0x18) +#define G_MWO_POINT_ZSCREEN gI_(0x1C) + +/* moveword offsets for fast3d and f3dex */ +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define G_MWO_aLIGHT_2 gI_(0x20) +# define G_MWO_bLIGHT_2 gI_(0x24) +# define G_MWO_aLIGHT_3 gI_(0x40) +# define G_MWO_bLIGHT_3 gI_(0x44) +# define G_MWO_aLIGHT_4 gI_(0x60) +# define G_MWO_bLIGHT_4 gI_(0x64) +# define G_MWO_aLIGHT_5 gI_(0x80) +# define G_MWO_bLIGHT_5 gI_(0x84) +# define G_MWO_aLIGHT_6 gI_(0xA0) +# define G_MWO_bLIGHT_6 gI_(0xA4) +# define G_MWO_aLIGHT_7 gI_(0xC0) +# define G_MWO_bLIGHT_7 gI_(0xC4) +# define G_MWO_aLIGHT_8 gI_(0xE0) +# define G_MWO_bLIGHT_8 gI_(0xE4) +#endif + +/* moveword offsets for f3dex2 */ +#if defined(F3DEX_GBI_2) +# define G_MWO_aLIGHT_2 gI_(0x18) +# define G_MWO_bLIGHT_2 gI_(0x1C) +# define G_MWO_aLIGHT_3 gI_(0x30) +# define G_MWO_bLIGHT_3 gI_(0x34) +# define G_MWO_aLIGHT_4 gI_(0x48) +# define G_MWO_bLIGHT_4 gI_(0x4C) +# define G_MWO_aLIGHT_5 gI_(0x60) +# define G_MWO_bLIGHT_5 gI_(0x64) +# define G_MWO_aLIGHT_6 gI_(0x78) +# define G_MWO_bLIGHT_6 gI_(0x7C) +# define G_MWO_aLIGHT_7 gI_(0x90) +# define G_MWO_bLIGHT_7 gI_(0x94) +# define G_MWO_aLIGHT_8 gI_(0xA8) +# define G_MWO_bLIGHT_8 gI_(0xAC) +#endif + +/* movemem params for fast3d and f3dex */ +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define G_MV_VIEWPORT 128 +# define G_MV_LOOKATY 130 +# define G_MV_LOOKATX 132 +# define G_MV_L0 134 +# define G_MV_L1 136 +# define G_MV_L2 138 +# define G_MV_L3 140 +# define G_MV_L4 142 +# define G_MV_L5 144 +# define G_MV_L6 146 +# define G_MV_L7 148 +# define G_MV_TXTATT 150 +# define G_MV_MATRIX_2 152 +# define G_MV_MATRIX_3 154 +# define G_MV_MATRIX_4 156 +# define G_MV_MATRIX_1 158 +#endif + +/* movemem params for f3dex2 */ +#if defined(F3DEX_GBI_2) +# define G_MV_MMTX 2 +# define G_MV_PMTX 6 +# define G_MV_VIEWPORT 8 +# define G_MV_LIGHT 10 +# define G_MV_POINT 12 +# define G_MV_MATRIX 14 +# define G_MVO_LOOKATX gI_(0 * 0x18) +# define G_MVO_LOOKATY gI_(1 * 0x18) +# define G_MVO_L0 gI_(2 * 0x18) +# define G_MVO_L1 gI_(3 * 0x18) +# define G_MVO_L2 gI_(4 * 0x18) +# define G_MVO_L3 gI_(5 * 0x18) +# define G_MVO_L4 gI_(6 * 0x18) +# define G_MVO_L5 gI_(7 * 0x18) +# define G_MVO_L6 gI_(8 * 0x18) +# define G_MVO_L7 gI_(9 * 0x18) +#endif + +/* frustum ratios */ +#define FRUSTRATIO_1 gI_(1) +#define FRUSTRATIO_2 gI_(2) +#define FRUSTRATIO_3 gI_(3) +#define FRUSTRATIO_4 gI_(4) +#define FRUSTRATIO_5 gI_(5) +#define FRUSTRATIO_6 gI_(6) + +/* light params */ +#define NUMLIGHTS_0 1 +#define NUMLIGHTS_1 1 +#define NUMLIGHTS_2 2 +#define NUMLIGHTS_3 3 +#define NUMLIGHTS_4 4 +#define NUMLIGHTS_5 5 +#define NUMLIGHTS_6 6 +#define NUMLIGHTS_7 7 +#define LIGHT_1 1 +#define LIGHT_2 2 +#define LIGHT_3 3 +#define LIGHT_4 4 +#define LIGHT_5 5 +#define LIGHT_6 6 +#define LIGHT_7 7 +#define LIGHT_8 8 + +/* light params for fast3d and f3dex */ +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define NUML(n) (((n) + 1) * 32 + 0x80000000) +#endif + +/* light params for f3dex2 */ +#if defined(F3DEX_GBI_2) +# define NUML(n) ((n) * 0x18) +#endif + +/* background load types */ +#define G_BGLT_LOADBLOCK gI_(0x0033) +#define G_BGLT_LOADTILE gI_(0xFFF4) + +/* background flags */ +#define G_BG_FLAG_FLIPS (gI_(0b1) << 0) +#define G_BG_FLAG_FLIPT (gI_(0b1) << 1) + +/* object load types */ +#define G_OBJLT_TXTRBLOCK gI_(0x00001033) +#define G_OBJLT_TXTRTILE gI_(0x00FC1034) +#define G_OBJLT_TLUT gI_(0x00000030) + +/* object flags */ +#define G_OBJ_FLAG_FLIPS (gI_(0b1) << 0) +#define G_OBJ_FLAG_FLIPT (gI_(0b1) << 4) + +/* color macros */ +#define G_MAXZ 0x03FF +#define G_MAXFBZ 0x3FFF +#define GPACK_RGBA5551(r, g, b, a) \ + ( \ + gF_(r, 5, 11) | \ + gF_(g, 5, 6) | \ + gF_(b, 5, 1) | \ + gF_(a, 1, 0) \ + ) +#define GPACK_RGBA8888(r, g, b, a) \ + ( \ + gF_(r, 8, 24) | \ + gF_(g, 8, 16) | \ + gF_(b, 8, 8) | \ + gF_(a, 8, 0) \ + ) +#define GPACK_RGB24A8(rgb, a) (gF_(rgb, 24, 8) | gF_(a, 8, 0)) +#define GPACK_ZDZ(z, dz) (gF_(z, 14, 2) | gF_(dz, 2, 0)) + +/* structure definition macros */ +#define gdSPDefMtx(xx, xy, xz, xw, \ + yx, yy, yz, yw, \ + zx, zy, zz, zw, \ + wx, wy, wz, ww) \ + ( \ + (Mtx) \ + { \ + .i = \ + { \ + (qs1616(xx) >> 16) & 0xFFFF, \ + (qs1616(xy) >> 16) & 0xFFFF, \ + (qs1616(xz) >> 16) & 0xFFFF, \ + (qs1616(xw) >> 16) & 0xFFFF, \ + (qs1616(yx) >> 16) & 0xFFFF, \ + (qs1616(yy) >> 16) & 0xFFFF, \ + (qs1616(yz) >> 16) & 0xFFFF, \ + (qs1616(yw) >> 16) & 0xFFFF, \ + (qs1616(zx) >> 16) & 0xFFFF, \ + (qs1616(zy) >> 16) & 0xFFFF, \ + (qs1616(zz) >> 16) & 0xFFFF, \ + (qs1616(zw) >> 16) & 0xFFFF, \ + (qs1616(wx) >> 16) & 0xFFFF, \ + (qs1616(wy) >> 16) & 0xFFFF, \ + (qs1616(wz) >> 16) & 0xFFFF, \ + (qs1616(ww) >> 16) & 0xFFFF, \ + }, \ + .f = \ + { \ + qs1616(xx) & 0xFFFF, \ + qs1616(xy) & 0xFFFF, \ + qs1616(xz) & 0xFFFF, \ + qs1616(xw) & 0xFFFF, \ + qs1616(yx) & 0xFFFF, \ + qs1616(yy) & 0xFFFF, \ + qs1616(yz) & 0xFFFF, \ + qs1616(yw) & 0xFFFF, \ + qs1616(zx) & 0xFFFF, \ + qs1616(zy) & 0xFFFF, \ + qs1616(zz) & 0xFFFF, \ + qs1616(zw) & 0xFFFF, \ + qs1616(wx) & 0xFFFF, \ + qs1616(wy) & 0xFFFF, \ + qs1616(wz) & 0xFFFF, \ + qs1616(ww) & 0xFFFF, \ + } \ + } \ + ) +#define gdSPDefLookAt(rx, ry, rz, ux, uy, uz) \ + ( \ + (LookAt) \ + { \ + .l[0].l = \ + { \ + .col = {0, 0, 0}, \ + .colc = {0, 0, 0}, \ + .dir = {rx, ry, rz}, \ + }, \ + .l[1].l = \ + { \ + .col = {0, 0x80, 0}, \ + .colc = {0, 0x80, 0}, \ + .dir = {ux, uy, uz}, \ + }, \ + } \ + ) +#define gdSPDefLights0(ar, ag, ab) \ + ( \ + (Lights0) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + } \ + } \ + ) +#define gdSPDefLights1(ar, ag, ab, \ + r1, g1, b1, x1, y1, z1) \ + ( \ + (Lights1) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + .col = {r1, g1, b1}, \ + .colc = {r1, g1, b1}, \ + .dir = {x1, y1, z1}, \ + } \ + } \ + ) +#define gdSPDefLights2(ar, ag, ab, \ + r1, g1, b1, x1, y1, z1, \ + r2, g2, b2, x2, y2, z2) \ + ( \ + (Lights2) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + .col = {r1, g1, b1}, \ + .colc = {r1, g1, b1}, \ + .dir = {x1, y1, z1}, \ + } \ + .l[1].l = \ + { \ + .col = {r2, g2, b2}, \ + .colc = {r2, g2, b2}, \ + .dir = {x2, y2, z2}, \ + } \ + } \ + ) +#define gdSPDefLights3(ar, ag, ab, \ + r1, g1, b1, x1, y1, z1, \ + r2, g2, b2, x2, y2, z2, \ + r3, g3, b3, x3, y3, z3) \ + ( \ + (Lights3) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + .col = {r1, g1, b1}, \ + .colc = {r1, g1, b1}, \ + .dir = {x1, y1, z1}, \ + } \ + .l[1].l = \ + { \ + .col = {r2, g2, b2}, \ + .colc = {r2, g2, b2}, \ + .dir = {x2, y2, z2}, \ + } \ + .l[2].l = \ + { \ + .col = {r3, g3, b3}, \ + .colc = {r3, g3, b3}, \ + .dir = {x3, y3, z3}, \ + } \ + } \ + ) +#define gdSPDefLights4(ar, ag, ab, \ + r1, g1, b1, x1, y1, z1, \ + r2, g2, b2, x2, y2, z2, \ + r3, g3, b3, x3, y3, z3, \ + r4, g4, b4, x4, y4, z4) \ + ( \ + (Lights4) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + .col = {r1, g1, b1}, \ + .colc = {r1, g1, b1}, \ + .dir = {x1, y1, z1}, \ + } \ + .l[1].l = \ + { \ + .col = {r2, g2, b2}, \ + .colc = {r2, g2, b2}, \ + .dir = {x2, y2, z2}, \ + } \ + .l[2].l = \ + { \ + .col = {r3, g3, b3}, \ + .colc = {r3, g3, b3}, \ + .dir = {x3, y3, z3}, \ + } \ + .l[3].l = \ + { \ + .col = {r4, g4, b4}, \ + .colc = {r4, g4, b4}, \ + .dir = {x4, y4, z4}, \ + } \ + } \ + ) +#define gdSPDefLights5(ar, ag, ab, \ + r1, g1, b1, x1, y1, z1, \ + r2, g2, b2, x2, y2, z2, \ + r3, g3, b3, x3, y3, z3, \ + r4, g4, b4, x4, y4, z4, \ + r5, g5, b5, x5, y5, z5) \ + ( \ + (Lights5) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + .col = {r1, g1, b1}, \ + .colc = {r1, g1, b1}, \ + .dir = {x1, y1, z1}, \ + } \ + .l[1].l = \ + { \ + .col = {r2, g2, b2}, \ + .colc = {r2, g2, b2}, \ + .dir = {x2, y2, z2}, \ + } \ + .l[2].l = \ + { \ + .col = {r3, g3, b3}, \ + .colc = {r3, g3, b3}, \ + .dir = {x3, y3, z3}, \ + } \ + .l[3].l = \ + { \ + .col = {r4, g4, b4}, \ + .colc = {r4, g4, b4}, \ + .dir = {x4, y4, z4}, \ + } \ + .l[4].l = \ + { \ + .col = {r5, g5, b5}, \ + .colc = {r5, g5, b5}, \ + .dir = {x5, y5, z5}, \ + } \ + } \ + ) +#define gdSPDefLights6(ar, ag, ab, \ + r1, g1, b1, x1, y1, z1, \ + r2, g2, b2, x2, y2, z2, \ + r3, g3, b3, x3, y3, z3, \ + r4, g4, b4, x4, y4, z4, \ + r5, g5, b5, x5, y5, z5, \ + r6, g6, b6, x6, y6, z6)\ + ( \ + (Lights6) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + .col = {r1, g1, b1}, \ + .colc = {r1, g1, b1}, \ + .dir = {x1, y1, z1}, \ + } \ + .l[1].l = \ + { \ + .col = {r2, g2, b2}, \ + .colc = {r2, g2, b2}, \ + .dir = {x2, y2, z2}, \ + } \ + .l[2].l = \ + { \ + .col = {r3, g3, b3}, \ + .colc = {r3, g3, b3}, \ + .dir = {x3, y3, z3}, \ + } \ + .l[3].l = \ + { \ + .col = {r4, g4, b4}, \ + .colc = {r4, g4, b4}, \ + .dir = {x4, y4, z4}, \ + } \ + .l[4].l = \ + { \ + .col = {r5, g5, b5}, \ + .colc = {r5, g5, b5}, \ + .dir = {x5, y5, z5}, \ + } \ + .l[5].l = \ + { \ + .col = {r6, g6, b6}, \ + .colc = {r6, g6, b6}, \ + .dir = {x6, y6, z6}, \ + } \ + } \ + ) +#define gdSPDefLights7(ar, ag, ab, \ + r1, g1, b1, x1, y1, z1, \ + r2, g2, b2, x2, y2, z2, \ + r3, g3, b3, x3, y3, z3, \ + r4, g4, b4, x4, y4, z4, \ + r5, g5, b5, x5, y5, z5, \ + r6, g6, b6, x6, y6, z6, \ + r7, g7, b7, x7, y7, z7) \ + ( \ + (Lights7) \ + { \ + .a.l = \ + { \ + .col = {ar, ag, ab}, \ + .colc = {ar, ag, ab}, \ + }, \ + .l[0].l = \ + { \ + .col = {r1, g1, b1}, \ + .colc = {r1, g1, b1}, \ + .dir = {x1, y1, z1}, \ + } \ + .l[1].l = \ + { \ + .col = {r2, g2, b2}, \ + .colc = {r2, g2, b2}, \ + .dir = {x2, y2, z2}, \ + } \ + .l[2].l = \ + { \ + .col = {r3, g3, b3}, \ + .colc = {r3, g3, b3}, \ + .dir = {x3, y3, z3}, \ + } \ + .l[3].l = \ + { \ + .col = {r4, g4, b4}, \ + .colc = {r4, g4, b4}, \ + .dir = {x4, y4, z4}, \ + } \ + .l[4].l = \ + { \ + .col = {r5, g5, b5}, \ + .colc = {r5, g5, b5}, \ + .dir = {x5, y5, z5}, \ + } \ + .l[5].l = \ + { \ + .col = {r6, g6, b6}, \ + .colc = {r6, g6, b6}, \ + .dir = {x6, y6, z6}, \ + } \ + .l[6].l = \ + { \ + .col = {r7, g7, b7}, \ + .colc = {r7, g7, b7}, \ + .dir = {x7, y7, z7}, \ + } \ + } \ + ) +#define gdSPDefVtx(x, y, z, s, t) \ + ( \ + (Vtx) \ + { \ + .v = \ + { \ + .ob = {x, y, z}, \ + .tc = {qs105(s), qs105(t)}, \ + } \ + } \ + ) +#define gdSPDefVtxC(x, y, z, s, t, cr, cg, cb, ca) \ + ( \ + (Vtx) \ + { \ + .v = \ + { \ + .ob = {x, y, z}, \ + .tc = {qs105(s), qs105(t)}, \ + .cn = {cr, cg, cb, ca}, \ + } \ + } \ + ) +#define gdSPDefVtxN(x, y, z, s, t, nx, ny, nz, ca) \ + ( \ + (Vtx) \ + { \ + .n = \ + { \ + .ob = {x, y, z}, \ + .tc = {qs105(s), qs105(t)}, \ + .n = {nx, ny, nz}, \ + .a = ca \ + } \ + } \ + ) + +/* instruction macros */ + +#define gsDPFillRectangle(ulx, uly, lrx, lry) \ + gO_( \ + G_FILLRECT, \ + gF_(lrx, 10, 14) | \ + gF_(lry, 10, 2), \ + gF_(ulx, 10, 14) | \ + gF_(uly, 10, 2)) + +#define gsDPScisFillRectangle(ulx, uly, lrx, lry) \ + gsDPFillRectangle(gScC_(ulx), gScC_(uly), gScC_(lrx), gScC_(lry)) + +#define gsDPFullSync() \ + gO_(G_RDPFULLSYNC, 0, 0) + +#define gsDPLoadSync() \ + gO_(G_RDPLOADSYNC, 0, 0) + +#define gsDPTileSync() \ + gO_(G_RDPTILESYNC, 0, 0) + +#define gsDPPipeSync() \ + gO_(G_RDPPIPESYNC, 0, 0) + +#define gsDPLoadTLUT_pal16(pal, dram) \ + gsDPLoadTLUT(16, 256 + (gI_(pal) & 0xF) * 16, dram) + +#define gsDPLoadTLUT_pal256(dram) \ + gsDPLoadTLUT(256, 256, dram) + +#define gLTB_(timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + dxt, tmem, rt, line) \ + gsDPSetTextureImage(fmt, G_SIZ_LDSIZ(siz), 1, timg), \ + gsDPSetTile( \ + fmt, G_SIZ_LDSIZ(siz), 0, tmem, G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock( \ + G_TX_LOADTILE, 0, 0, \ + G_LTB_LRS(width, height, siz), \ + dxt), \ + gsDPPipeSync(), \ + gsDPSetTile( \ + fmt, siz, line, tmem, rt, pal, \ + cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize(rt, 0, 0, qu102((width) - 1), qu102((height) - 1)) + +#define gsDPLoadTextureBlock(timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(siz, width), 0x0, G_TX_RENDERTILE, \ + ((width) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define gsDPLoadTextureBlockS(timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, 0x0, G_TX_RENDERTILE, \ + ((width) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define gsDPLoadTextureBlock_4b(timg, fmt, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, G_IM_SIZ_4b, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(G_IM_SIZ_4b, width), 0x0, G_TX_RENDERTILE, \ + ((width) * 4 + 63) / 64) + +#define gsDPLoadTextureBlock_4bS(timg, fmt, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, G_IM_SIZ_4b, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, 0x0, G_TX_RENDERTILE, \ + ((width) * 4 + 63) / 64) + +#define gsDPLoadTextureBlockYuv(timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(siz, width), 0x0, G_TX_RENDERTILE, \ + ((width) + 7) / 8) + +#define gsDPLoadTextureBlockYuvS(timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, 0x0, G_TX_RENDERTILE, \ + ((width) + 7) / 8) + +#define _gsDPLoadTextureBlock(timg, tmem, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(siz, width), tmem, G_TX_RENDERTILE, \ + ((width) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define _gsDPLoadTextureBlockS(timg, tmem, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, tmem, G_TX_RENDERTILE, \ + ((width) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define _gsDPLoadTextureBlock_4b(timg, tmem, fmt, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, G_IM_SIZ_4b, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(G_IM_SIZ_4b, width), tmem, G_TX_RENDERTILE, \ + ((width) * 4 + 63) / 64) + +#define _gsDPLoadTextureBlock_4bS(timg, tmem, fmt, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, G_IM_SIZ_4b, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, tmem, G_TX_RENDERTILE, \ + ((width) * 4 + 63) / 64) + +#define _gsDPLoadTextureBlockYuv(timg, tmem, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(siz, width), tmem, G_TX_RENDERTILE, \ + ((width) + 7) / 8) + +#define _gsDPLoadTextureBlockYuvS(timg, tmem, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, tmem, G_TX_RENDERTILE, \ + ((width) + 7) / 8) + +#define gsDPLoadMultiBlock(timg, tmem, rt, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(siz, width), tmem, rt, \ + ((width) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define gsDPLoadMultiBlockS(timg, tmem, rt, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, tmem, rt, \ + ((width) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define gsDPLoadMultiBlock_4b(timg, tmem, rt, fmt, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, G_IM_SIZ_4b, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(G_IM_SIZ_4b, width), tmem, rt, \ + ((width) * 4 + 63) / 64) + +#define gsDPLoadMultiBlock_4bS(timg, tmem, rt, fmt, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, G_IM_SIZ_4b, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, tmem, rt, \ + ((width) * 4 + 63) / 64) + +#define gsDPLoadMultiBlockYuv(timg, tmem, rt, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + G_DXT(siz, width), tmem, rt, \ + ((width) + 7) / 8) + +#define gsDPLoadMultiBlockYuvS(timg, tmem, rt, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTB_( \ + timg, fmt, siz, width, height, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0, tmem, rt, \ + ((width) + 7) / 8) + +#define gLTT_(timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, rt, line) \ + gsDPSetTextureImage(fmt, siz, width, timg), \ + gsDPSetTile( \ + fmt, siz, line, tmem, \ + G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile( \ + G_TX_LOADTILE, \ + qu102(uls), qu102(ult), \ + qu102(lrs), qu102(lrt)), \ + gsDPPipeSync(), \ + gsDPSetTile( \ + fmt, siz, line, \ + tmem, rt, pal, \ + cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize( \ + rt, \ + qu102(uls), qu102(ult), \ + qu102(lrs), qu102(lrt)) + +#define gLTT4_(timg, fmt, width, height, uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, rt) \ + gsDPSetTextureImage(fmt, G_IM_SIZ_8b, (width) / 2, timg), \ + gsDPSetTile( \ + fmt, G_IM_SIZ_8b, \ + (((lrs) - (uls) + 1) / 2 + 7) / 8, \ + tmem, G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile( \ + G_TX_LOADTILE, \ + qu102(uls) / 2, qu102(ult), \ + qu102(lrs) / 2, qu102(lrt)), \ + gsDPPipeSync(), \ + gsDPSetTile( \ + fmt, G_IM_SIZ_4b, \ + (((lrs) - (uls) + 1) / 2 + 7) / 8, \ + tmem, rt, pal, \ + cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize( \ + rt, \ + qu102(uls), qu102(ult), \ + qu102(lrs), qu102(lrt)) + +#define gsDPLoadTextureTile(timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT_( \ + timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0x0, G_TX_RENDERTILE, \ + (((lrs) - (uls) + 1) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define gsDPLoadTextureTile_4b(timg, fmt, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT4_( \ + timg, fmt, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0x0, G_TX_RENDERTILE) + +#define gsDPLoadTextureTileYuv(timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT_( \ + timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + 0x0, G_TX_RENDERTILE, \ + (((lrs) - (uls) + 1) + 7) / 8) + +#define _gsDPLoadTextureTile(timg, tmem, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT_( \ + timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, G_TX_RENDERTILE, \ + (((lrs) - (uls) + 1) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define _gsDPLoadTextureTile_4b(timg, tmem, fmt, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT4_( \ + timg, fmt, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, G_TX_RENDERTILE) + +#define _gsDPLoadTextureTileYuv(timg, tmem, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, cms, cmt, \ + masks, maskt, shifts, shiftt) \ + gLTT_( \ + timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, G_TX_RENDERTILE, \ + (((lrs) - (uls) + 1) + 7) / 8) + +#define gsDPLoadMultiTile(timg, tmem, rt, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT_( \ + timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, rt, \ + (((lrs) - (uls) + 1) * G_SIZ_LDBITS(siz) + 63) / 64) + +#define gsDPLoadMultiTile_4b(timg, tmem, rt, fmt, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT4_( \ + timg, fmt, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, rt) + +#define gsDPLoadMultiTileYuv(timg, tmem, rt, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ + gLTT_( \ + timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt, \ + tmem, rt, \ + (((lrs) - (uls) + 1) + 7) / 8) + +#define gsDPLoadBlock(tile, uls, ult, lrs, dxt) \ + gO_( \ + G_LOADBLOCK, \ + gF_(uls, 12, 12) | \ + gF_(ult, 12, 0), \ + gF_(tile, 3, 24) | \ + gF_(G_LDBLK_TXL(lrs), 12, 12) | \ + gF_(dxt, 12, 0)) + +#define gsDPNoOp() \ + gsDPNoOpTag(0) + +#define gsDPNoOpTag(tag) \ + gO_(G_NOOP, 0, tag) + +#define gsDPPipelineMode(mode) \ + gsSPSetOtherModeHi(G_MDSFT_PIPELINE, G_MDSIZ_PIPELINE, mode) + +#define gsDPSetBlendColor(r, g, b, a) \ + gO_( \ + G_SETBLENDCOLOR, \ + 0, \ + gF_(r, 8, 24) | \ + gF_(g, 8, 16) | \ + gF_(b, 8, 8) | \ + gF_(a, 8, 0)) + +#define gsDPSetEnvColor(r, g, b, a) \ + gO_( \ + G_SETENVCOLOR, \ + 0, \ + gF_(r, 8, 24) | \ + gF_(g, 8, 16) | \ + gF_(b, 8, 8) | \ + gF_(a, 8, 0)) + +#define gsDPSetFillColor(c) \ + gO_(G_SETFILLCOLOR, 0, c) + +#define gsDPSetFogColor(r, g, b, a) \ + gO_( \ + G_SETFOGCOLOR, 0, \ + gF_(r, 8, 24) | \ + gF_(g, 8, 16) | \ + gF_(b, 8, 8) | \ + gF_(a, 8, 0)) + +#define gsDPSetPrimColor(m, l, r, g, b, a) \ + gO_( \ + G_SETPRIMCOLOR, \ + gF_(m, 8, 8) | \ + gF_(l, 8, 0), \ + gF_(r, 8, 24) | \ + gF_(g, 8, 16) | \ + gF_(b, 8, 8) | \ + gF_(a, 8, 0)) + +#define gsDPSetColorImage(fmt, siz, width, img) \ + gO_( \ + G_SETCIMG, \ + gF_(fmt, 3, 21) | \ + gF_(siz, 2, 19) | \ + gF_((width) - 1, 12, 0), \ + img) + +#define gsDPSetDepthImage(img) \ + gO_(G_SETZIMG, 0, img) + +#define gsDPSetTextureImage(fmt, siz, width, img) \ + gO_( \ + G_SETTIMG, \ + gF_(fmt, 3, 21) | \ + gF_(siz, 2, 19) | \ + gF_((width) - 1, 12, 0), \ + img) + +#define gsDPSetHilite1Tile(tile, hilite, width, height) \ + gsDPSetTileSize( \ + tile, \ + ((Hilite *)(hilite))->h.x1 & 0xFFF, \ + ((Hilite *)(hilite))->h.y1 & 0xFFF, \ + (((width) - 1) * 4 + ((Hilite *)(hilite))->h.x1) & 0xFFF, \ + (((height) - 1) * 4 + ((Hilite *)(hilite))->h.y1) & 0xFFF) + +#define gsDPSetHilite2Tile(tile, hilite, width, height) \ + gsDPSetTileSize( \ + tile, \ + ((Hilite *)(hilite))->h.x2 & 0xFFF, \ + ((Hilite *)(hilite))->h.y2 & 0xFFF, \ + (((width) - 1) * 4 + ((Hilite *)(hilite))->h.x2) & 0xFFF, \ + (((height) - 1) * 4 + ((Hilite *)(hilite))->h.y2) & 0xFFF) + +#define gsDPSetAlphaCompare(mode) \ + gsSPSetOtherModeLo(G_MDSFT_ALPHACOMPARE, G_MDSIZ_ALPHACOMPARE, mode) + +#define gsDPSetAlphaDither(type) \ + gsSPSetOtherModeHi(G_MDSFT_ALPHADITHER, G_MDSIZ_ALPHADITHER, type) + +#define gsDPSetColorDither(type) \ + gsSPSetOtherModeHi(G_MDSFT_RGBDITHER, G_MDSIZ_RGBDITHER, type) + +#define gsDPSetCombineMode(mode1, mode2) \ + gsDPSetCombineLERP(mode1, mode2) + +#define gsDPSetCombineLERP(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, \ + a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ + gO_( \ + G_SETCOMBINE, \ + gF_(G_CCMUX_##a0, 4, 20) | \ + gF_(G_CCMUX_##c0, 5, 15) | \ + gF_(G_ACMUX_##Aa0, 3, 12) | \ + gF_(G_ACMUX_##Ac0, 3, 9) | \ + gF_(G_CCMUX_##a1, 4, 5) | \ + gF_(G_CCMUX_##c1, 5, 0), \ + gF_(G_CCMUX_##b0, 4, 28) | \ + gF_(G_CCMUX_##b1, 4, 24) | \ + gF_(G_ACMUX_##Aa1, 3, 21) | \ + gF_(G_ACMUX_##Ac1, 3, 18) | \ + gF_(G_CCMUX_##d0, 3, 15) | \ + gF_(G_ACMUX_##Ab0, 3, 12) | \ + gF_(G_ACMUX_##Ad0, 3, 9) | \ + gF_(G_CCMUX_##d1, 3, 6) | \ + gF_(G_ACMUX_##Ab1, 3, 3) | \ + gF_(G_ACMUX_##Ad1, 3, 0)) + +#define gsDPSetConvert(k0, k1, k2, k3, k4, k5) \ + gO_( \ + G_SETCONVERT, \ + gF_(k0, 9, 13) | \ + gF_(k1, 9, 4) | \ + gF_(gI_(k2) >> 5, 4, 0), \ + gF_(k2, 5, 27) | \ + gF_(k3, 9, 18) | \ + gF_(k4, 9, 9) | \ + gF_(k5, 9, 0)) + +#define gsDPSetTextureConvert(type) \ + gsSPSetOtherModeHi(G_MDSFT_TEXTCONV, G_MDSIZ_TEXTCONV, type) + +#define gsDPSetCycleType(type) \ + gsSPSetOtherModeHi(G_MDSFT_CYCLETYPE, G_MDSIZ_CYCLETYPE, type) + +#define gsDPSetDepthSource(source) \ + gsSPSetOtherModeLo(G_MDSFT_ZSRCSEL, G_MDSIZ_ZSRCSEL, source) + +#define gsDPSetCombineKey(type) \ + gsSPSetOtherModeHi(G_MDSFT_COMBKEY, G_MDSIZ_COMBKEY, type) + +#define gsDPSetKeyGB(cG, sG, wG, cB, sB, wB) \ + gO_( \ + G_SETKEYGB, \ + gF_(wG, 12, 12) | \ + gF_(wB, 12, 0), \ + gF_(cG, 8, 24) | \ + gF_(sG, 8, 16) | \ + gF_(cB, 8, 8) | \ + gF_(sB, 8, 0)) + +#define gsDPSetKeyR(cR, sR, wR) \ + gO_( \ + G_SETKEYR, 0, \ + gF_(wR, 12, 16) | \ + gF_(cR, 8, 8) | \ + gF_(sR, 8, 0)) + +#define gsDPSetPrimDepth(z, dz) \ + gO_( \ + G_SETPRIMDEPTH, \ + 0, \ + gF_(z, 16, 16) | \ + gF_(dz, 16, 0)) + +#define gsDPSetRenderMode(mode1, mode2) \ + gsSPSetOtherModeLo( \ + G_MDSFT_RENDERMODE, \ + G_MDSIZ_RENDERMODE, \ + gI_(mode1) | \ + gI_(mode2)) + +#define gsDPSetScissor(mode, ulx, uly, lrx, lry) \ + gsDPSetScissorFrac( \ + mode, \ + qu102(gI_(ulx)), \ + qu102(gI_(uly)), \ + qu102(gI_(lrx)), \ + qu102(gI_(lry))) + +#define gsDPSetScissorFrac(mode, ulx, uly, lrx, lry) \ + gO_( \ + G_SETSCISSOR, \ + gF_(ulx, 12, 12) | \ + gF_(uly, 12, 0), \ + gF_(mode, 2, 24) | \ + gF_(lrx, 12, 12) | \ + gF_(lry, 12, 0)) + +#define gsDPSetTextureDetail(type) \ + gsSPSetOtherModeHi(G_MDSFT_TEXTDETAIL, G_MDSIZ_TEXTDETAIL, type) + +#define gsDPSetTextureFilter(mode) \ + gsSPSetOtherModeHi(G_MDSFT_TEXTFILT, G_MDSIZ_TEXTFILT, mode) + +#define gsDPSetTextureLOD(mode) \ + gsSPSetOtherModeHi(G_MDSFT_TEXTLOD, G_MDSIZ_TEXTLOD, mode) + +#define gsDPSetTextureLUT(mode) \ + gsSPSetOtherModeHi(G_MDSFT_TEXTLUT, G_MDSIZ_TEXTLUT, mode) + +#define gsDPSetTexturePersp(enable) \ + gsSPSetOtherModeHi(G_MDSFT_TEXTPERSP, G_MDSIZ_TEXTPERSP, enable) + +#define gsDPSetTile(fmt, siz, line, tmem, tile, palette, \ + cmt, maskt, shiftt, cms, masks, shifts) \ + gO_( \ + G_SETTILE, \ + gF_(fmt, 3, 21) | \ + gF_(siz, 2, 19) | \ + gF_(line, 9, 9) | \ + gF_(tmem, 9, 0), \ + gF_(tile, 3, 24) | \ + gF_(palette, 4, 20) | \ + gF_(cmt, 2, 18) | \ + gF_(maskt, 4, 14) | \ + gF_(shiftt, 4, 10) | \ + gF_(cms, 2, 8) | \ + gF_(masks, 4, 4) | \ + gF_(shifts, 4, 0)) + +#define gsDPSetTileSize(tile, uls, ult, lrs, lrt) \ + gO_( \ + G_SETTILESIZE, \ + gF_(uls, 12, 12) | \ + gF_(ult, 12, 0), \ + gF_(tile, 3, 24) | \ + gF_(lrs, 12, 12) | \ + gF_(lrt, 12, 0)) + +#define gsSPBranchList(dl) \ + gsDisplayList(dl, 1) + +#define gsSPClipRatio(r) \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNX, (uint16_t)(r)), \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNY, (uint16_t)(r)), \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPX, (uint16_t)-(r)), \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPY, (uint16_t)-(r)) + +#define gsSPDisplayList(dl) \ + gsDisplayList(dl, 0) + +#define gsSPEndDisplayList() \ + gO_(G_ENDDL, 0, 0) + +#define gsSPFogFactor(fm, fo) \ + gsMoveWd( \ + G_MW_FOG, \ + G_MWO_FOG, \ + gF_(fm, 16, 16) | \ + gF_(fo, 16, 0)) + +#define gsSPFogPosition(min, max) \ + gsSPFogFactor( \ + (500 * 0x100) / ((max) - (min)), \ + (500 - (min)) * 0x100 / ((max) - (min))) + +#define gsSPLine3D(v0, v1, flag) \ + gsSPLineW3D(v0, v1, 0, flag) + +#define gsSPLookAt(l) \ + gsSPLookAtX(l), \ + gsSPLookAtY(gI_(l) + 0x10) + +#define gsSPSegment(seg, base) \ + gsMoveWd(G_MW_SEGMENT, (seg) * 4, base) + +#define gsSPSetLights0(lites) \ + gsSPNumLights(NUMLIGHTS_0), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).a, 2) + +#define gsSPSetLights1(lites) \ + gsSPNumLights(NUMLIGHTS_1), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).a, 2) + +#define gsSPSetLights2(lites) \ + gsSPNumLights(NUMLIGHTS_2), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).l[1], 2), \ + gsSPLight(&(lites).a, 3) + +#define gsSPSetLights3(lites) \ + gsSPNumLights(NUMLIGHTS_3), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).l[1], 2), \ + gsSPLight(&(lites).l[2], 3), \ + gsSPLight(&(lites).a, 4) + +#define gsSPSetLights4(lites) \ + gsSPNumLights(NUMLIGHTS_4), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).l[1], 2), \ + gsSPLight(&(lites).l[2], 3), \ + gsSPLight(&(lites).l[3], 4), \ + gsSPLight(&(lites).a, 5) + +#define gsSPSetLights5(lites) \ + gsSPNumLights(NUMLIGHTS_5), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).l[1], 2), \ + gsSPLight(&(lites).l[2], 3), \ + gsSPLight(&(lites).l[3], 4), \ + gsSPLight(&(lites).l[4], 5), \ + gsSPLight(&(lites).a, 6) + +#define gsSPSetLights6(lites) \ + gsSPNumLights(NUMLIGHTS_6), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).l[1], 2), \ + gsSPLight(&(lites).l[2], 3), \ + gsSPLight(&(lites).l[3], 4), \ + gsSPLight(&(lites).l[4], 5), \ + gsSPLight(&(lites).l[5], 6), \ + gsSPLight(&(lites).a, 7) + +#define gsSPSetLights7(lites) \ + gsSPNumLights(NUMLIGHTS_7), \ + gsSPLight(&(lites).l[0], 1), \ + gsSPLight(&(lites).l[1], 2), \ + gsSPLight(&(lites).l[2], 3), \ + gsSPLight(&(lites).l[3], 4), \ + gsSPLight(&(lites).l[4], 5), \ + gsSPLight(&(lites).l[5], 6), \ + gsSPLight(&(lites).l[6], 7), \ + gsSPLight(&(lites).a, 8) + +#define gsSPSetStatus(sid, val) \ + gsMoveWd(G_MW_GENSTAT, sid, val) + +#define gsSPNumLights(n) \ + gsMoveWd(G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) + +#define gsSPLightColor(Lightnum, packedcolor) \ + gsMoveWd(G_MW_LIGHTCOL, G_MWO_a##Lightnum, packedcolor), \ + gsMoveWd(G_MW_LIGHTCOL, G_MWO_b##Lightnum, packedcolor) + +#define gsSPTextureRectangle(ulx, uly, lrx, lry, tile, s, t, dsdx, dtdy) \ + gsTexRect(ulx, uly, lrx, lry, tile), \ + gsDPHalf1(gF_(s, 16, 16) | gF_(t, 16, 0)), \ + gsDPHalf2(gF_(dsdx, 16, 16) | gF_(dtdy, 16, 0)) + +#define gsSPScisTextureRectangle(ulx, uly, lrx, lry, tile, s, t, dsdx, dtdy) \ + gsTexRect(gScC_(ulx), gScC_(uly), gScC_(lrx), gScC_(lry), tile), \ + gsDPHalf1( \ + gF_(gScD_(s, ulx, dsdx), 16, 16) | \ + gF_(gScD_(t, uly, dtdy), 16, 0)), \ + gsDPHalf2(gF_(dsdx, 16, 16) | gF_(dtdy, 16, 0)) + +#define gsSPTextureRectangleFlip(ulx, uly, lrx, lry, tile, s, t, dsdx, dtdy) \ + gsTexRectFlip(ulx, uly, lrx, lry, tile), \ + gsDPHalf1(gF_(s, 16, 16) | gF_(t, 16, 0)), \ + gsDPHalf2(gF_(dsdx, 16, 16) | gF_(dtdy, 16, 0)) + +#define gsSPScisTextureRectangleFlip( \ + ulx, uly, lrx, lry, tile, s, t, dsdx, dtdy) \ + gsTexRectFlip(gScC_(ulx), gScC_(uly), gScC_(lrx), gScC_(lry), tile), \ + gsDPHalf1( \ + gF_(gScD_(s, ulx, dsdx), 16, 16) | \ + gF_(gScD_(t, uly, dtdy), 16, 0)), \ + gsDPHalf2(gF_(dsdx, 16, 16) | gF_(dtdy, 16, 0)) + +#define gsSPBgRectCopy(bg) \ + gO_(G_BG_COPY, 0, bg) + +#define gsSPBgRect1Cyc(bg) \ + gO_(G_BG_1CYC, 0, bg) + +#define gsSPObjRectangle(sp) \ + gO_(G_OBJ_RECTANGLE, 0, sp) + +#define gsSPObjRectangleR(sp) \ + gO_(G_OBJ_RECTANGLE_R, 0, sp) + +#define gsSPObjSprite(sp) \ + gO_(G_OBJ_SPRITE, 0, sp) + +#define gsSPObjMatrix(mtx) \ + gO_( \ + G_OBJ_MOVEMEM, \ + gF_(sizeof(uObjMtx) - 1, 8, 16), \ + mtx) + +#define gsSPObjSubMatrix(mtx) \ + gO_( \ + G_OBJ_MOVEMEM, \ + gF_(sizeof(uObjSubMtx) - 1, 8, 16) | \ + gF_(2, 16, 0), \ + mtx) + +#define gsSPObjRenderMode(mode) \ + gO_(G_OBJ_RENDERMODE, 0, mode) + +#define gsSPObjLoadTxtr(tx) \ + gO_(G_OBJ_LOADTXTR, 23, tx) + +#define gsSPObjLoadTxRect(txsp) \ + gO_(G_OBJ_LDTX_RECT, 47, txsp) + +#define gsSPObjLoadTxRectR(txsp) \ + gO_(G_OBJ_LDTX_RECT_R, 47, txsp) + +#define gsSPObjLoadTxSprite(txsp) \ + gO_(G_OBJ_LDTX_SPRITE, 47, txsp) + +#define gsSPSelectDL(ldl, sid, flag, mask) \ + gO_( \ + G_RDPHALF_0, \ + gF_(sid, 8, 16) | \ + gF_(ldl, 16, 0), \ + flag), \ + gO_( \ + G_SELECT_DL, \ + gF_(0x00, 8, 16) | \ + gF_(gI_(ldl) >> 16, 16, 0), \ + mask) + +#define gsSPSelectBranchDL(bdl, sid, flag, mask) \ + gO_( \ + G_RDPHALF_0, \ + gF_(sid, 8, 16) | \ + gF_(bdl, 16, 0), \ + flag), \ + gO_( \ + G_SELECT_DL, \ + gF_(0x01, 8, 16) | \ + gF_(gI_(bdl) >> 16, 16, 0), \ + mask) + +/* unlisted instructions */ + +#define gsDPLoadTLUTCmd(tile, count) \ + gO_( \ + G_LOADTLUT, \ + 0, \ + gF_(tile, 3, 24) | \ + gF_(count, 10, 14)) + +#define gsDPLoadTLUT(count, tmem, dram) \ + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram), \ + gsDPTileSync(), \ + gsDPSetTile(0, 0, 0, tmem, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0), \ + gsDPLoadSync(), \ + gsDPLoadTLUTCmd(G_TX_LOADTILE, (count) - 1), \ + gsDPPipeSync() + +#define gsDisplayList(dl, branch) \ + gO_(G_DL, gF_(branch, 8, 16), dl) + +#define gsDPLoadTile(tile, uls, ult, lrs, lrt) \ + gO_( \ + G_LOADTILE, \ + gF_(uls, 12, 12) | \ + gF_(ult, 12, 0), \ + gF_(tile, 3, 24) | \ + gF_(lrs, 12, 12) | \ + gF_(lrt, 12, 0)) + +#define gsDPSetCombine(c) \ + gO_( \ + G_SETCOMBINE, \ + (gL_(c) >> 32) & 0xFFFFFFFF, \ + (gL_(c) >> 0) & 0xFFFFFFFF) + +#define gsSPSetOtherModeLo(shift, length, data) \ + gsSPSetOtherMode(G_SETOTHERMODE_L, shift, length, data) + +#define gsSPSetOtherModeHi(shift, length, data) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, shift, length, data) + +#define gsDPSetOtherMode(mode0, mode1) \ + gO_(G_RDPSETOTHERMODE, gF_(mode0, 24, 0), mode1) + +#define gsTexRect(ulx, uly, lrx, lry, tile) \ + gO_( \ + G_TEXRECT, \ + gF_(lrx, 12, 12) | \ + gF_(lry, 12, 0), \ + gF_(tile, 3, 24) | \ + gF_(ulx, 12, 12) | \ + gF_(uly, 12, 0)) + +#define gsTexRectFlip(ulx, uly, lrx, lry, tile) \ + gO_( \ + G_TEXRECTFLIP, \ + gF_(lrx, 12, 12) | \ + gF_(lry, 12, 0), \ + gF_(tile, 3, 24) | \ + gF_(ulx, 12, 12) | \ + gF_(uly, 12, 0)) + +#define gsSPNoOp() \ + gO_(G_SPNOOP, 0, 0) + +#define gsDPHalf1(wordhi) \ + gO_(G_RDPHALF_1, 0, wordhi) + +#define gsDPHalf2(wordlo) \ + gO_(G_RDPHALF_2, 0, wordlo) + +#define gsDPWord(wordhi, wordlo) \ + gsDPHalf1(wordhi), \ + gsDPHalf2(wordlo) + +/* instruction macros for fast3d */ + +#if defined(F3D_GBI) + +# define gsSP1Triangle(v0, v1, v2, flag) \ + gO_( \ + G_TRI1, \ + 0, \ + gF_(flag, 8, 24) | \ + gF_(gI_(v0) * 10, 8, 16) | \ + gF_(gI_(v1) * 10, 8, 8) | \ + gF_(gI_(v2) * 10, 8, 0)) + +# define gsSPCullDisplayList(v0, vn) \ + gO_( \ + G_CULLDL, \ + (gI_(v0) & 0xF) * 40, \ + gI_((vn) + 1) & 0xF) * 40) + +# define gsSPLineW3D(v0, v1, wd, flag) \ + gO_( \ + G_LINE3D, \ + 0, \ + gF_(flag, 8, 24) | \ + gF_(gI_(v0) * 10, 8, 16) | \ + gF_(gI_(v1) * 10, 8, 8) | \ + gF_(wd, 8, 0)) + +# define gsSPVertex(v, n, v0) \ + gO_( \ + G_VTX, \ + gF_((n) - 1, 4, 20) | \ + gF_(v0, 4, 16) | \ + gF_(sizeof(Vtx) * (n), 16, 0), \ + v) + +#endif + +/* instruction macros for fast3d and beta f3dex */ +#if defined(F3D_GBI) || (defined(F3D_BETA) && defined(F3DEX_GBI)) + +# define gsSPModifyVertex(vtx, where, val) \ + gsMoveWd(G_MW_POINTS, (vtx) * 40 + (where), val) + +#endif + +/* instruction macros for fast3d and f3dex */ + +#if defined(F3D_GBI) || defined(F3DEX_GBI) + +# define gsSPForceMatrix(mptr) \ + gsMoveMem(16, G_MV_MATRIX_1, (char *)(mptr)), \ + gsMoveMem(16, G_MV_MATRIX_2, (char *)(mptr) + 16), \ + gsMoveMem(16, G_MV_MATRIX_3, (char *)(mptr) + 32), \ + gsMoveMem(16, G_MV_MATRIX_4, (char *)(mptr) + 48) + +# define gsSPSetGeometryMode(mode) \ + gO_(G_SETGEOMETRYMODE, 0, gI_(mode)) + +# define gsSPClearGeometryMode(mode) \ + gO_(G_CLEARGEOMETRYMODE, 0, gI_(mode)) + +# define gsSPLoadGeometryMode(mode) \ + gsSPClearGeometryMode(~gI_(0)), \ + gsSPSetGeometryMode(mode) + +# define gsSPInsertMatrix(where, num) \ + gsMoveWd(G_MW_MATRIX, where, num) + +# define gsSPLookAtX(l) \ + gsMoveMem(sizeof(Light), G_MV_LOOKATX, l) + +# define gsSPLookAtY(l) \ + gsMoveMem(sizeof(Light), G_MV_LOOKATY, l) + +# define gsSPMatrix(matrix, param) \ + gO_( \ + G_MTX, \ + gF_(param, 8, 16) | \ + gF_(sizeof(Mtx), 16, 0), \ + matrix) + +# define gsSPPopMatrix(param) \ + gO_(G_POPMTX, 0, param) + +# define gsSPLight(l, n) \ + gsMoveMem(sizeof(Light), G_MV_L0 + ((n) - 1) * 2, l) + +# define gsSPTexture(sc, tc, level, tile, on) \ + gO_( \ + G_TEXTURE, \ + gF_(level, 3, 11) | \ + gF_(tile, 3, 8) | \ + gF_(on, 8, 0), \ + gF_(sc, 16, 16) | \ + gF_(tc, 16, 0)) + +# define gsSPViewport(v) \ + gsMoveMem(sizeof(Vp), G_MV_VIEWPORT, v) + +# define gsSPSetOtherMode(opc, shift, length, data) \ + gO_( \ + opc, \ + gF_(shift, 8, 8) | \ + gF_(length, 8, 0), \ + data) + +# define gsMoveWd(index, offset, data) \ + gO_( \ + G_MOVEWORD, \ + gF_(offset, 16, 8) | \ + gF_(index, 8, 0), \ + data) + +# define gsMoveMem(size, index, address) \ + gO_( \ + G_MOVEMEM, \ + gF_(index, 8, 16) | \ + gF_(size, 16, 0), \ + address) + +#endif + +/* instruction macros for f3dex */ + +#if defined(F3DEX_GBI) + +# define gsSP1Triangle(v0, v1, v2, flag) \ + gO_( \ + G_TRI1, \ + 0, \ + gF_(gV3_(v0, v1, v2, flag) * 2, 8, 16) | \ + gF_(gV3_(v1, v2, v0, flag) * 2, 8, 8) | \ + gF_(gV3_(v2, v0, v1, flag) * 2, 8, 0)) + +# define gsSP1Quadrangle(v0, v1, v2, v3, flag) \ + gO_( \ + G_TRI2, \ + gF_(gV4_(v0, v1, v2, v3, flag) * 2, 8, 16) | \ + gF_(gV4_(v1, v2, v3, v0, flag) * 2, 8, 8) | \ + gF_(gV4_(v2, v3, v0, v1, flag) * 2, 8, 0), \ + gF_(gV4_(v0, v1, v2, v3, flag) * 2, 8, 16) | \ + gF_(gV4_(v2, v3, v0, v1, flag) * 2, 8, 8) | \ + gF_(gV4_(v3, v0, v1, v2, flag) * 2, 8, 0)) + +# define gsSPLineW3D(v0, v1, wd, flag) \ + gO_( \ + G_LINE3D, \ + 0, \ + gF_(gV2_(v0, v1, flag) * 2, 8, 16) | \ + gF_(gV2_(v1, v0, flag) * 2, 8, 8) | \ + gF_(wd, 8, 0)) + +# define gsSPVertex(v, n, v0) \ + gO_( \ + G_VTX, \ + gF_((v0) * 2, 8, 16) | \ + gF_(n, 6, 10) | \ + gF_(sizeof(Vtx) * (n) - 1, 10, 0), \ + v) + +#endif + +/* instruction macros for f3dex and f3dex2 */ + +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + +# define gsSP2Triangles(v00, v01, v02, flag0, v10, v11, v12, flag1) \ + gO_( \ + G_TRI2, \ + gF_(gV3_(v00, v01, v02, flag0) * 2, 8, 16) | \ + gF_(gV3_(v01, v02, v00, flag0) * 2, 8, 8) | \ + gF_(gV3_(v02, v00, v01, flag0) * 2, 8, 0), \ + gF_(gV3_(v10, v11, v12, flag1) * 2, 8, 16) | \ + gF_(gV3_(v11, v12, v10, flag1) * 2, 8, 8) | \ + gF_(gV3_(v12, v10, v11, flag1) * 2, 8, 0)) + +# define gsSPBranchLessZ(branchdl, vtx, zval, near, far, flag) \ + gsSPBranchLessZrg(branchdl, vtx, zval, near, far, flag, 0, G_MAXZ) + +# define gsSPBranchLessZrg(branchdl, vtx, zval, near, far, flag, zmin, zmax) \ + gsSPBranchLessZraw(branchdl, vtx, \ + G_DEPTOZSrg(zval, near, far, flag, zmin, zmax)) + +# define gsSPBranchLessZraw(branchdl, vtx, zval) \ + gsDPHalf1(branchdl), \ + gsBranchZ(vtx, zval) + +# define gsSPCullDisplayList(v0, vn) \ + gO_( \ + G_CULLDL, \ + gF_((v0) * 2, 16, 0), \ + gF_((vn) * 2, 16, 0)) + +# define gsSPLoadUcode(uc_start, uc_dstart) \ + gsSPLoadUcodeEx(uc_start, uc_dstart, 0x800) + +# define gsSPLoadUcodeL(ucode) \ + gsSPLoadUcode( \ + gI_(&ucode##TextStart) & 0x1FFFFFFF, \ + gI_(&ucode##DataStart) & 0x1FFFFFFF) + +# if !(defined(F3D_BETA) && defined(F3DEX_GBI)) +# define gsSPModifyVertex(vtx, where, val) \ + gO_( \ + G_MODIFYVTX, \ + gF_(where, 8, 16) | \ + gF_((vtx) * 2, 16, 0), \ + val) +# endif + +# define gsBranchZ(vtx, zval) \ + gO_( \ + G_BRANCH_Z, \ + gF_((vtx) * 5, 12, 12) | \ + gF_((vtx) * 2, 12, 0), \ + zval) + +# define gsLoadUcode(uc_start, uc_dsize) \ + gO_( \ + G_LOAD_UCODE, \ + gF_((uc_dsize) - 1, 16, 0), \ + uc_start) + +# define gsSPLoadUcodeEx(uc_start, uc_dstart, uc_dsize) \ + gsDPHalf1(uc_dstart), \ + gsLoadUcode(uc_start, uc_dsize) + +#endif + +/* instruction macros for f3dex2 */ + +#if defined(F3DEX_GBI_2) + +# define gsSP1Triangle(v0, v1, v2, flag) \ + gO_( \ + G_TRI1, \ + gF_(gV3_(v0, v1, v2, flag) * 2, 8, 16) | \ + gF_(gV3_(v1, v2, v0, flag) * 2, 8, 8) | \ + gF_(gV3_(v2, v0, v1, flag) * 2, 8, 0), \ + 0) + +# define gsSP1Quadrangle(v0, v1, v2, v3, flag) \ + gO_( \ + G_QUAD, \ + gF_(gV4_(v0, v1, v2, v3, flag) * 2, 8, 16) | \ + gF_(gV4_(v1, v2, v3, v0, flag) * 2, 8, 8) | \ + gF_(gV4_(v2, v3, v0, v1, flag) * 2, 8, 0), \ + gF_(gV4_(v0, v1, v2, v3, flag) * 2, 8, 16) | \ + gF_(gV4_(v2, v3, v0, v1, flag) * 2, 8, 8) | \ + gF_(gV4_(v3, v0, v1, v2, flag) * 2, 8, 0)) + +# define gsSPForceMatrix(mptr) \ + gsMoveMem(sizeof(Mtx), G_MV_MATRIX, 0, mptr), \ + gsMoveWd(G_MW_FORCEMTX, 0, 0x10000) + +# define gsSPSetGeometryMode(mode) \ + gsSPGeometryMode(0, mode) + +# define gsSPClearGeometryMode(mode) \ + gsSPGeometryMode(mode, 0) + +# define gsSPLoadGeometryMode(mode) \ + gsSPGeometryMode(~gI_(0), mode) + +# define gsSPLineW3D(v0, v1, wd, flag) \ + gO_( \ + G_LINE3D, \ + gF_(gV2_(v0, v1, flag) * 2, 8, 16) | \ + gF_(gV2_(v1, v0, flag) * 2, 8, 8) | \ + gF_(wd, 8, 0), \ + 0) + +# define gsSPLookAtX(l) \ + gsMoveMem(sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATX, l) + +# define gsSPLookAtY(l) \ + gsMoveMem(sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATY, l) + +# define gsSPMatrix(matrix, param) \ + gO_( \ + G_MTX, \ + gF_((sizeof(Mtx) - 1) / 8, 5, 19) | \ + gF_(gI_(param) ^ G_MTX_PUSH, 8, 0), \ + matrix) + +# define gsSPPopMatrix(param) \ + gsSPPopMatrixN(param, 1) + +# define gsSPPopMatrixN(param, n) \ + gO_( \ + G_POPMTX, \ + gF_((sizeof(Mtx) - 1) / 8, 5, 19) | \ + gF_(2, 8, 0), \ + sizeof(Mtx) * (n)) + +# define gsSPLight(l, n) \ + gsMoveMem(sizeof(Light), G_MV_LIGHT, ((n) + 1) * 0x18, l) + +# define gsSPTexture(sc, tc, level, tile, on) \ + gO_( \ + G_TEXTURE, \ + gF_(level, 3, 11) | \ + gF_(tile, 3, 8) | \ + gF_(on, 7, 1), \ + gF_(sc, 16, 16) | \ + gF_(tc, 16, 0)) + +# define gsSPVertex(v, n, v0) \ + gO_( \ + G_VTX, \ + gF_(n, 8, 12) | \ + gF_((v0) + (n), 7, 1), \ + v) + +# define gsSPViewport(v) \ + gsMoveMem(sizeof(Vp), G_MV_VIEWPORT, 0, v) + +# define gsSPGeometryMode(clearbits, setbits) \ + gO_( \ + G_GEOMETRYMODE, \ + gF_(~gI_(clearbits), 24, 0), \ + setbits) + +# define gsSPSetOtherMode(opc, shift, length, data) \ + gO_( \ + opc, \ + gF_(32 - (shift) - (length), 8, 8) | \ + gF_((length) - 1, 8, 0), \ + data) + +# define gsMoveWd(index, offset, data) \ + gO_( \ + G_MOVEWORD, \ + gF_(index, 8, 16) | \ + gF_(offset, 16, 0), \ + data) + +# define gsMoveMem(size, index, offset, address) \ + gO_( \ + G_MOVEMEM, \ + gF_((size - 1) / 8, 5, 19) | \ + gF_((offset) / 8, 8, 8) | \ + gF_(index, 8, 0), \ + address) + +# define gsSPDma_io(flag, dmem, dram, size) \ + gO_( \ + G_DMA_IO, \ + gF_(flag, 1, 23) | \ + gF_((dmem) / 8, 10, 13) | \ + gF_((size) - 1, 12, 0), \ + dram) + +# define gsSPDmaRead(dmem, dram, size) \ + gsSPDma_io(0, dmem, dram, size) + +# define gsSPDmaWrite(dmem, dram, size) \ + gsSPDma_io(1, dmem, dram, size) + +# define gsSpecial3(hi, lo) \ + gO_(G_SPECIAL_3, hi, lo) + +# define gsSpecial2(hi, lo) \ + gO_(G_SPECIAL_2, hi, lo) + +# define gsSpecial1(hi, lo) \ + gO_(G_SPECIAL_1, hi, lo) + +#endif + +/* instruction macros for beta fast3d and f3dex */ + +#if defined(F3D_BETA) && (defined(F3D_GBI) || defined(F3DEX_GBI)) + +# define gsSPPerspNormalize(scale) \ + gO_(G_PERSPNORM, 0, scale) + +#else + +# define gsSPPerspNormalize(scale) \ + gsMoveWd(G_MW_PERSPNORM, 0, scale) + +#endif + +/* dynamic instruction macros */ + +#define gDisplayListPut(gdl, ...) \ + ({ \ + Gfx Gd_[] = {__VA_ARGS__}; \ + for(size_t Gi_ = 0; Gi_ < sizeof(Gd_) / sizeof(Gfx); Gi_++) \ + { \ + *(Gfx *)(gdl) = Gd_[Gi_]; \ + } \ + (void)0; \ + }) +#define gDisplayListAppend(pgdl, ...) \ + ({ \ + Gfx Gd_[] = {__VA_ARGS__}; \ + for(size_t Gi_ = 0; Gi_ < sizeof(Gd_) / sizeof(Gfx); Gi_++) \ + { \ + *(*(Gfx **)(pgdl))++ = Gd_[Gi_]; \ + } \ + (void)0; \ + }) +#define gDisplayListData(pgdl, d) \ + ({ \ + Gfx **Gp_ = (void *)(pgdl); \ + struct \ + { \ + __typeof__(d) v; \ + } *Gd_, *Gs_; \ + *Gp_ -= (sizeof(*Gd_) + sizeof(Gfx) - 1) / sizeof(Gfx); \ + Gd_ = (void *)*Gp_; \ + Gs_ = (void *)&(d); \ + *Gd_ = *Gs_; \ + &Gd_->v; \ + }) +#define gDisplayListAlloc(pgdl, s) \ + ({ \ + Gfx **Gp_ = (void *)(pgdl); \ + *Gp_ -= ((s) + sizeof(Gfx) - 1) / sizeof(Gfx); \ + (void *)*Gp_; \ + }) + +#define gDPFillRectangle(gdl, ...) \ + gD_(gdl, gsDPFillRectangle, __VA_ARGS__) +#define gDPScisFillRectangle(gdl, ...) \ + gD_(gdl, gsDPScisFillRectangle, __VA_ARGS__) +#define gDPFullSync(gdl) \ + gDisplayListPut(gdl, gsDPFullSync()) +#define gDPLoadSync(gdl) \ + gDisplayListPut(gdl, gsDPLoadSync()) +#define gDPTileSync(gdl) \ + gDisplayListPut(gdl, gsDPTileSync()) +#define gDPPipeSync(gdl) \ + gDisplayListPut(gdl, gsDPPipeSync()) +#define gDPLoadTLUT_pal16(gdl, ...) \ + gD_(gdl, gsDPLoadTLUT_pal16, __VA_ARGS__) +#define gDPLoadTLUT_pal256(gdl, ...) \ + gD_(gdl, gsDPLoadTLUT_pal256, __VA_ARGS__) +#define gDPLoadTextureBlock(gdl, ...) \ + gD_(gdl, gsDPLoadTextureBlock, __VA_ARGS__) +#define gDPLoadTextureBlockS(gdl, ...) \ + gD_(gdl, gsDPLoadTextureBlockS, __VA_ARGS__) +#define gDPLoadTextureBlock_4b(gdl, ...) \ + gD_(gdl, gsDPLoadTextureBlock_4b, __VA_ARGS__) +#define gDPLoadTextureBlock_4bS(gdl, ...) \ + gD_(gdl, gsDPLoadTextureBlock_4bS, __VA_ARGS__) +#define gDPLoadTextureBlockYuv(gdl, ...) \ + gD_(gdl, gsDPLoadTextureBlockYuv, __VA_ARGS__) +#define gDPLoadTextureBlockYuvS(gdl, ...) \ + gD_(gdl, gsDPLoadTextureBlockYuvS, __VA_ARGS__) +#define _gDPLoadTextureBlock(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureBlock, __VA_ARGS__) +#define _gDPLoadTextureBlockS(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureBlockS, __VA_ARGS__) +#define _gDPLoadTextureBlock_4b(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureBlock_4b, __VA_ARGS__) +#define _gDPLoadTextureBlock_4bS(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureBlock_4bS, __VA_ARGS__) +#define _gDPLoadTextureBlockYuv(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureBlockYuv, __VA_ARGS__) +#define _gDPLoadTextureBlockYuvS(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureBlockYuvS, __VA_ARGS__) +#define gDPLoadMultiBlock(gdl, ...) \ + gD_(gdl, gsDPLoadMultiBlock, __VA_ARGS__) +#define gDPLoadMultiBlockS(gdl, ...) \ + gD_(gdl, gsDPLoadMultiBlockS, __VA_ARGS__) +#define gDPLoadMultiBlock_4b(gdl, ...) \ + gD_(gdl, gsDPLoadMultiBlock_4b, __VA_ARGS__) +#define gDPLoadMultiBlock_4bS(gdl, ...) \ + gD_(gdl, gsDPLoadMultiBlock_4bS, __VA_ARGS__) +#define gDPLoadMultiBlockYuv(gdl, ...) \ + gD_(gdl, gsDPLoadMultiBlockYuv, __VA_ARGS__) +#define gDPLoadMultiBlockYuvS(gdl, ...) \ + gD_(gdl, gsDPLoadMultiBlockYuvS, __VA_ARGS__) +#define gDPLoadTextureTile(gdl, ...) \ + gD_(gdl, gsDPLoadTextureTile, __VA_ARGS__) +#define gDPLoadTextureTile_4b(gdl, ...) \ + gD_(gdl, gsDPLoadTextureTile_4b, __VA_ARGS__) +#define gDPLoadTextureTileYuv(gdl, ...) \ + gD_(gdl, gsDPLoadTextureTileYuv, __VA_ARGS__) +#define _gDPLoadTextureTile(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureTile, __VA_ARGS__) +#define _gDPLoadTextureTile_4b(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureTile_4b, __VA_ARGS__) +#define _gDPLoadTextureTileYuv(gdl, ...) \ + gD_(gdl, _gsDPLoadTextureTileYuv, __VA_ARGS__) +#define gDPLoadMultiTile(gdl, ...) \ + gD_(gdl, gsDPLoadMultiTile, __VA_ARGS__) +#define gDPLoadMultiTile_4b(gdl, ...) \ + gD_(gdl, gsDPLoadMultiTile_4b, __VA_ARGS__) +#define gDPLoadMultiTileYuv(gdl, ...) \ + gD_(gdl, gsDPLoadMultiTileYuv, __VA_ARGS__) +#define gDPLoadBlock(gdl, ...) \ + gD_(gdl, gsDPLoadBlock, __VA_ARGS__) +#define gDPNoOp(gdl) \ + gDisplayListPut(gdl, gsDPNoOp()) +#define gDPNoOpTag(gdl, ...) \ + gD_(gdl, gsDPNoOpTag, __VA_ARGS__) +#define gDPPipelineMode(gdl, ...) \ + gD_(gdl, gsDPPipelineMode, __VA_ARGS__) +#define gDPSetBlendColor(gdl, ...) \ + gD_(gdl, gsDPSetBlendColor, __VA_ARGS__) +#define gDPSetEnvColor(gdl, ...) \ + gD_(gdl, gsDPSetEnvColor, __VA_ARGS__) +#define gDPSetFillColor(gdl, ...) \ + gD_(gdl, gsDPSetFillColor, __VA_ARGS__) +#define gDPSetFogColor(gdl, ...) \ + gD_(gdl, gsDPSetFogColor, __VA_ARGS__) +#define gDPSetPrimColor(gdl, ...) \ + gD_(gdl, gsDPSetPrimColor, __VA_ARGS__) +#define gDPSetColorImage(gdl, ...) \ + gD_(gdl, gsDPSetColorImage, __VA_ARGS__) +#define gDPSetDepthImage(gdl, ...) \ + gD_(gdl, gsDPSetDepthImage, __VA_ARGS__) +#define gDPSetTextureImage(gdl, ...) \ + gD_(gdl, gsDPSetTextureImage, __VA_ARGS__) +#define gDPSetHilite1Tile(gdl, ...) \ + gD_(gdl, gsDPSetHilite1Tile, __VA_ARGS__) +#define gDPSetHilite2Tile(gdl, ...) \ + gD_(gdl, gsDPSetHilite2Tile, __VA_ARGS__) +#define gDPSetAlphaCompare(gdl, ...) \ + gD_(gdl, gsDPSetAlphaCompare, __VA_ARGS__) +#define gDPSetAlphaDither(gdl, ...) \ + gD_(gdl, gsDPSetAlphaDither, __VA_ARGS__) +#define gDPSetColorDither(gdl, ...) \ + gD_(gdl, gsDPSetColorDither, __VA_ARGS__) +#define gDPSetCombineMode(gdl, ...) \ + gD_(gdl, gsDPSetCombineLERP, __VA_ARGS__) +#define gDPSetCombineLERP(gdl, ...) \ + gD_(gdl, gsDPSetCombineLERP, __VA_ARGS__) +#define gDPSetConvert(gdl, ...) \ + gD_(gdl, gsDPSetConvert, __VA_ARGS__) +#define gDPSetTextureConvert(gdl, ...) \ + gD_(gdl, gsDPSetTextureConvert, __VA_ARGS__) +#define gDPSetCycleType(gdl, ...) \ + gD_(gdl, gsDPSetCycleType, __VA_ARGS__) +#define gDPSetDepthSource(gdl, ...) \ + gD_(gdl, gsDPSetDepthSource, __VA_ARGS__) +#define gDPSetCombineKey(gdl, ...) \ + gD_(gdl, gsDPSetCombineKey, __VA_ARGS__) +#define gDPSetKeyGB(gdl, ...) \ + gD_(gdl, gsDPSetKeyGB, __VA_ARGS__) +#define gDPSetKeyR(gdl, ...) \ + gD_(gdl, gsDPSetKeyR, __VA_ARGS__) +#define gDPSetPrimDepth(gdl, ...) \ + gD_(gdl, gsDPSetPrimDepth, __VA_ARGS__) +#define gDPSetRenderMode(gdl, ...) \ + gD_(gdl, gsDPSetRenderMode, __VA_ARGS__) +#define gDPSetScissor(gdl, ...) \ + gD_(gdl, gsDPSetScissor, __VA_ARGS__) +#define gDPSetScissorFrac(gdl, ...) \ + gD_(gdl, gsDPSetScissorFrac, __VA_ARGS__) +#define gDPSetTextureDetail(gdl, ...) \ + gD_(gdl, gsDPSetTextureDetail, __VA_ARGS__) +#define gDPSetTextureFilter(gdl, ...) \ + gD_(gdl, gsDPSetTextureFilter, __VA_ARGS__) +#define gDPSetTextureLOD(gdl, ...) \ + gD_(gdl, gsDPSetTextureLOD, __VA_ARGS__) +#define gDPSetTextureLUT(gdl, ...) \ + gD_(gdl, gsDPSetTextureLUT, __VA_ARGS__) +#define gDPSetTexturePersp(gdl, ...) \ + gD_(gdl, gsDPSetTexturePersp, __VA_ARGS__) +#define gDPSetTile(gdl, ...) \ + gD_(gdl, gsDPSetTile, __VA_ARGS__) +#define gDPSetTileSize(gdl, ...) \ + gD_(gdl, gsDPSetTileSize, __VA_ARGS__) +#define gSP1Triangle(gdl, ...) \ + gD_(gdl, gsSP1Triangle, __VA_ARGS__) +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +# define gSP2Triangles(gdl, ...) \ + gD_(gdl, gsSP2Triangles, __VA_ARGS__) +# define gSP1Quadrangle(gdl, ...) \ + gD_(gdl, gsSP1Quadrangle, __VA_ARGS__) +# define gSPBranchLessZ(gdl, ...) \ + gD_(gdl, gsSPBranchLessZ, __VA_ARGS__) +# define gSPBranchLessZrg(gdl, ...) \ + gD_(gdl, gsSPBranchLessZrg, __VA_ARGS__) +# define gSPBranchLessZraw(gdl, ...) \ + gD_(gdl, gsSPBranchLessZraw, __VA_ARGS__) +#endif +#define gSPBranchList(gdl, ...) \ + gD_(gdl, gsSPBranchList, __VA_ARGS__) +#define gSPClipRatio(gdl, ...) \ + gD_(gdl, gsSPClipRatio, __VA_ARGS__) +#define gSPCullDisplayList(gdl, ...) \ + gD_(gdl, gsSPCullDisplayList, __VA_ARGS__) +#define gSPDisplayList(gdl, ...) \ + gD_(gdl, gsSPDisplayList, __VA_ARGS__) +#define gSPEndDisplayList(gdl) \ + gDisplayListPut(gdl, gsSPEndDisplayList()) +#define gSPFogFactor(gdl, ...) \ + gD_(gdl, gsSPFogFactor, __VA_ARGS__) +#define gSPFogPosition(gdl, ...) \ + gD_(gdl, gsSPFogPosition, __VA_ARGS__) +#define gSPForceMatrix(gdl, ...) \ + gD_(gdl, gsSPForceMatrix, __VA_ARGS__) +#define gSPSetGeometryMode(gdl, ...) \ + gD_(gdl, gsSPSetGeometryMode, __VA_ARGS__) +#define gSPClearGeometryMode(gdl, ...) \ + gD_(gdl, gsSPClearGeometryMode, __VA_ARGS__) +#define gSPLoadGeometryMode(gdl, ...) \ + gD_(gdl, gsSPLoadGeometryMode, __VA_ARGS__) +#if defined(F3D_GBI) || defined(F3DEX_GBI) +# define gSPInsertMatrix(gdl, ...) \ + gD_(gdl, gsSPInsertMatrix, __VA_ARGS__) +#endif +#define gSPLine3D(gdl, ...) \ + gD_(gdl, gsSPLine3D, __VA_ARGS__) +#define gSPLineW3D(gdl, ...) \ + gD_(gdl, gsSPLineW3D, __VA_ARGS__) +#define gSPLoadUcode(gdl, ...) \ + gD_(gdl, gsSPLoadUcode, __VA_ARGS__) +#define gSPLoadUcodeL(gdl, ...) \ + gD_(gdl, gsSPLoadUcodeL, __VA_ARGS__) +#define gSPLookAtX(gdl, ...) \ + gD_(gdl, gsSPLookAtX, __VA_ARGS__) +#define gSPLookAtY(gdl, ...) \ + gD_(gdl, gsSPLookAtY, __VA_ARGS__) +#define gSPLookAt(gdl, ...) \ + gD_(gdl, gsSPLookAt, __VA_ARGS__) +#define gSPMatrix(gdl, ...) \ + gD_(gdl, gsSPMatrix, __VA_ARGS__) +#define gSPModifyVertex(gdl, ...) \ + gD_(gdl, gsSPModifyVertex, __VA_ARGS__) +#define gSPPerspNormalize(gdl, ...) \ + gD_(gdl, gsSPPerspNormalize, __VA_ARGS__) +#define gSPPopMatrix(gdl, ...) \ + gD_(gdl, gsSPPopMatrix, __VA_ARGS__) +#if defined(F3DEX_GBI_2) +# define gSPPopMatrixN(gdl, ...) \ + gD_(gdl, gsSPPopMatrixN, __VA_ARGS__) +#endif +#define gSPSegment(gdl, ...) \ + gD_(gdl, gsSPSegment, __VA_ARGS__) +#define gSPSetLights0(gdl, ...) \ + gD_(gdl, gsSPSetLights0, __VA_ARGS__) +#define gSPSetLights1(gdl, ...) \ + gD_(gdl, gsSPSetLights1, __VA_ARGS__) +#define gSPSetLights2(gdl, ...) \ + gD_(gdl, gsSPSetLights2, __VA_ARGS__) +#define gSPSetLights3(gdl, ...) \ + gD_(gdl, gsSPSetLights3, __VA_ARGS__) +#define gSPSetLights4(gdl, ...) \ + gD_(gdl, gsSPSetLights4, __VA_ARGS__) +#define gSPSetLights5(gdl, ...) \ + gD_(gdl, gsSPSetLights5, __VA_ARGS__) +#define gSPSetLights6(gdl, ...) \ + gD_(gdl, gsSPSetLights6, __VA_ARGS__) +#define gSPSetLights7(gdl, ...) \ + gD_(gdl, gsSPSetLights7, __VA_ARGS__) +#define gSPSetStatus(gdl, ...) \ + gD_(gdl, gsSPSetStatus, __VA_ARGS__) +#define gSPNumLights(gdl, ...) \ + gD_(gdl, gsSPNumLights, __VA_ARGS__) +#define gSPLight(gdl, ...) \ + gD_(gdl, gsSPLight, __VA_ARGS__) +#define gSPLightColor(gdl, ...) \ + gD_(gdl, gsSPLightColor, __VA_ARGS__) +#define gSPTexture(gdl, ...) \ + gD_(gdl, gsSPTexture, __VA_ARGS__) +#define gSPTextureRectangle(gdl, ...) \ + gD_(gdl, gsSPTextureRectangle, __VA_ARGS__) +#define gSPScisTextureRectangle(gdl, ...) \ + gD_(gdl, gsSPScisTextureRectangle, __VA_ARGS__) +#define gSPTextureRectangleFlip(gdl, ...) \ + gD_(gdl, gsSPTextureRectangleFlip, __VA_ARGS__) +#define gSPScisTextureRectangleFlip(gdl, ...) \ + gD_(gdl, gsSPScisTextureRectangleFlip, __VA_ARGS__) +#define gSPVertex(gdl, ...) \ + gD_(gdl, gsSPVertex, __VA_ARGS__) +#define gSPViewport(gdl, ...) \ + gD_(gdl, gsSPViewport, __VA_ARGS__) +#define gSPBgRectCopy(gdl, ...) \ + gD_(gdl, gsSPBgRectCopy, __VA_ARGS__) +#define gSPBgRect1Cyc(gdl, ...) \ + gD_(gdl, gsSPBgRect1Cyc, __VA_ARGS__) +#define gSPObjRectangle(gdl, ...) \ + gD_(gdl, gsSPObjRectangle, __VA_ARGS__) +#define gSPObjRectangleR(gdl, ...) \ + gD_(gdl, gsSPObjRectangleR, __VA_ARGS__) +#define gSPObjSprite(gdl, ...) \ + gD_(gdl, gsSPObjSprite, __VA_ARGS__) +#define gSPObjMatrix(gdl, ...) \ + gD_(gdl, gsSPObjMatrix, __VA_ARGS__) +#define gSPObjSubMatrix(gdl, ...) \ + gD_(gdl, gsSPObjSubMatrix, __VA_ARGS__) +#define gSPObjRenderMode(gdl, ...) \ + gD_(gdl, gsSPObjRenderMode, __VA_ARGS__) +#define gSPObjLoadTxtr(gdl, ...) \ + gD_(gdl, gsSPObjLoadTxtr, __VA_ARGS__) +#define gSPObjLoadTxRect(gdl, ...) \ + gD_(gdl, gsSPObjLoadTxRect, __VA_ARGS__) +#define gSPObjLoadTxRectR(gdl, ...) \ + gD_(gdl, gsSPObjLoadTxRectR, __VA_ARGS__) +#define gSPObjLoadTxSprite(gdl, ...) \ + gD_(gdl, gsSPObjLoadTxSprite, __VA_ARGS__) +#define gSPSelectDL(gdl, ...) \ + gD_(gdl, gsSPSelectDL, __VA_ARGS__) +#define gSPSelectBranchDL(gdl, ...) \ + gD_(gdl, gsSPSelectBranchDL, __VA_ARGS__) +#define gDPLoadTLUTCmd(gdl, ...) \ + gD_(gdl, gsDPLoadTLUTCmd, __VA_ARGS__) +#define gDPLoadTLUT(gdl, ...) \ + gD_(gdl, gsDPLoadTLUT, __VA_ARGS__) +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +# define gBranchZ(gdl, ...) \ + gD_(gdl, gsBranchZ, __VA_ARGS__) +#endif +#define gDisplayList(gdl, ...) \ + gD_(gdl, gsDisplayList, __VA_ARGS__) +#define gDPHalf1(gdl, ...) \ + gD_(gdl, gsDPHalf1, __VA_ARGS__) +#define gDPHalf2(gdl, ...) \ + gD_(gdl, gsDPHalf2, __VA_ARGS__) +#define gDPLoadTile(gdl, ...) \ + gD_(gdl, gsDPLoadTile, __VA_ARGS__) +#define gDPSetCombine(gdl, ...) \ + gD_(gdl, gsDPSetCombine, __VA_ARGS__) +#if defined(F3DEX_GBI_2) +# define gSPGeometryMode(gdl, ...) \ + gD_(gdl, gsSPGeometryMode, __VA_ARGS__) +#endif +#define gSPSetOtherMode(gdl, ...) \ + gD_(gdl, gsSPSetOtherMode, __VA_ARGS__) +#define gSPSetOtherModeLo(gdl, ...) \ + gD_(gdl, gsSPSetOtherModeLo, __VA_ARGS__) +#define gSPSetOtherModeHi(gdl, ...) \ + gD_(gdl, gsSPSetOtherModeHi, __VA_ARGS__) +#define gDPSetOtherMode(gdl, ...) \ + gD_(gdl, gsDPSetOtherMode, __VA_ARGS__) +#define gMoveWd(gdl, ...) \ + gD_(gdl, gsMoveWd, __VA_ARGS__) +#define gMoveMem(gdl, ...) \ + gD_(gdl, gsMoveMem, __VA_ARGS__) +#if defined(F3DEX_GBI_2) +# define gSPDma_io(gdl, ...) \ + gD_(gdl, gsSPDma_io, __VA_ARGS__) +# define gSPDmaRead(gdl, ...) \ + gD_(gdl, gsSPDmaRead, __VA_ARGS__) +# define gSPDmaWrite(gdl, ...) \ + gD_(gdl, gsSPDmaWrite, __VA_ARGS__) +#endif +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +# define gLoadUcode(gdl, ...) \ + gD_(gdl, gsLoadUcode, __VA_ARGS__) +# define gSPLoadUcodeEx(gdl, ...) \ + gD_(gdl, gsSPLoadUcodeEx, __VA_ARGS__) +#endif +#define gTexRect(gdl, ...) \ + gD_(gdl, gsTexRect, __VA_ARGS__) +#define gTexRectFlip(gdl, ...) \ + gD_(gdl, gsTexRectFlip, __VA_ARGS__) +#define gSPNoOp(gdl) \ + gDisplayListPut(gdl, gsSPNoOp()) +#define gDPWord(gdl, ...) \ + gD_(gdl, gsDPWord, __VA_ARGS__) +#if defined(F3DEX_GBI_2) +# define gSpecial3(gdl, ...) \ + gD_(gdl, gsSpecial3, __VA_ARGS__) +# define gSpecial2(gdl, ...) \ + gD_(gdl, gsSpecial2, __VA_ARGS__) +# define gSpecial1(gdl, ...) \ + gD_(gdl, gsSpecial1, __VA_ARGS__) +#endif + +/* data types and structures */ +typedef uint8_t qu08_t; +typedef uint16_t qu016_t; +typedef int16_t qs48_t; +typedef int16_t qs510_t; +typedef uint16_t qu510_t; +typedef int16_t qs102_t; +typedef uint16_t qu102_t; +typedef int16_t qs105_t; +typedef uint16_t qu105_t; +typedef int16_t qs132_t; +typedef int16_t qs142_t; +typedef int32_t qs1516_t; +typedef int32_t qs1616_t; +typedef int32_t qs205_t; + +typedef uint16_t g_bglt_t; +typedef uint8_t g_ifmt_t; +typedef uint8_t g_isiz_t; +typedef uint16_t g_bgf_t; +typedef uint8_t g_objf_t; +typedef uint32_t g_objlt_t; + +typedef struct +{ + _Alignas(8) + uint32_t hi; + uint32_t lo; +} Gfx; + +typedef struct +{ + int32_t x1; + int32_t y1; + int32_t x2; + int32_t y2; +} Hilite_t; + +typedef union +{ + _Alignas(8) + Hilite_t h; +} Hilite; + +typedef int32_t Mtx_t[4][4]; + +typedef union +{ + _Alignas(8) + Mtx_t m; + int32_t l[16]; + struct + { + int16_t i[16]; + uint16_t f[16]; + }; +} Mtx; + +typedef struct +{ + uint8_t col[3]; + char pad1; + uint8_t colc[3]; + char pad2; + int8_t dir[3]; + char pad3; +} Light_t; + +typedef union +{ + _Alignas(8) + Light_t l; +} Light; + +typedef struct +{ + Light l[2]; +} LookAt; + +typedef struct +{ + uint8_t col[3]; + char pad1; + uint8_t colc[3]; + char pad2; +} Ambient_t; + +typedef union +{ + _Alignas(8) + Ambient_t l; +} Ambient; + +typedef struct +{ + Ambient a; + Light l[1]; +} Lights0, Lights1; + +typedef struct +{ + Ambient a; + Light l[2]; +} Lights2; + +typedef struct +{ + Ambient a; + Light l[3]; +} Lights3; + +typedef struct +{ + Ambient a; + Light l[4]; +} Lights4; + +typedef struct +{ + Ambient a; + Light l[5]; +} Lights5; + +typedef struct +{ + Ambient a; + Light l[6]; +} Lights6; + +typedef struct +{ + Ambient a; + Light l[7]; +} Lightsn, Lights7; + +typedef struct +{ + int16_t ob[3]; + uint16_t flag; + qs105_t tc[2]; + uint8_t cn[4]; +} Vtx_t; + +typedef struct +{ + int16_t ob[3]; + uint16_t flag; + qs105_t tc[2]; + int8_t n[3]; + uint8_t a; +} Vtx_tn; + +typedef union +{ + _Alignas(8) + Vtx_t v; + Vtx_tn n; +} Vtx; + +typedef struct +{ + qs142_t vscale[4]; + qs142_t vtrans[4]; +} Vp_t; + +typedef union +{ + _Alignas(8) + Vp_t vp; +} Vp; + +typedef struct +{ + qs1516_t A; + qs1516_t B; + qs1516_t C; + qs1516_t D; + qs102_t X; + qs102_t Y; + qu510_t BaseScaleX; + qu510_t BaseScaleY; +} uObjMtx_t; + +typedef union +{ + _Alignas(8) + uObjMtx_t m; +} uObjMtx; + +typedef struct +{ + qs102_t X; + qs102_t Y; + qu510_t BaseScaleX; + qu510_t BaseScaleY; +} uObjSubMtx_t; + +typedef union +{ + _Alignas(8) + uObjSubMtx_t m; +} uObjSubMtx; + +typedef struct +{ + qu105_t imageX; + qu102_t imageW; + qs102_t frameX; + qu102_t frameW; + qu105_t imageY; + qu102_t imageH; + qs102_t frameY; + qu102_t frameH; + uint64_t * imagePtr; + g_bglt_t imageLoad; + g_ifmt_t imageFmt; + g_isiz_t imageSiz; + uint16_t imagePal; + g_bgf_t imageFlip; + uint16_t tmemW; + qs132_t tmemH; + uint16_t tmemLoadSH; + uint16_t tmemLoadTH; + uint16_t tmemSizeW; + uint16_t tmemSize; +} uObjBg_t; + +typedef struct +{ + qu105_t imageX; + qu102_t imageW; + qs102_t frameX; + qu102_t frameW; + qu105_t imageY; + qu102_t imageH; + qs102_t frameY; + qu102_t frameH; + uint64_t * imagePtr; + g_bglt_t imageLoad; + g_ifmt_t imageFmt; + g_isiz_t imageSiz; + uint16_t imagePal; + g_bgf_t imageFlip; + qu510_t scaleW; + qu510_t scaleH; + qs205_t imageYorig; + char padding[4]; +} uObjScaleBg_t; + +typedef union +{ + _Alignas(8) + uObjBg_t b; + uObjScaleBg_t s; +} uObjBg; + +typedef struct +{ + qs102_t objX; + qu510_t scaleW; + qu105_t imageW; + uint16_t paddingX; + qs102_t objY; + qu510_t scaleH; + qu105_t imageH; + uint16_t paddingY; + uint16_t imageStride; + uint16_t imageAdrs; + g_ifmt_t imageFmt; + g_isiz_t imageSiz; + uint16_t imagePal; + g_objf_t imageFlags; +} uObjSprite_t; + +typedef union +{ + _Alignas(8) + uObjSprite_t s; +} uObjSprite; + +typedef struct +{ + g_objlt_t type; + uint64_t * image; + uint16_t tmem; + uint16_t tsize; + uint16_t tline; + uint16_t sid; + uint32_t flag; + uint32_t mask; +} uObjTxtrBlock_t; + +typedef struct +{ + g_objlt_t type; + uint64_t * image; + uint16_t tmem; + uint16_t twidth; + uint16_t theight; + uint16_t sid; + uint32_t flag; + uint32_t mask; +} uObjTxtrTile_t; + +typedef struct +{ + g_objlt_t type; + uint64_t * image; + uint16_t phead; + uint16_t pnum; + uint16_t zero; + uint16_t sid; + uint32_t flag; + uint32_t mask; +} uObjTxtrTLUT_t; + +typedef union +{ + _Alignas(8) + uObjTxtrBlock_t block; + uObjTxtrTile_t tile; + uObjTxtrTLUT_t tlut; +} uObjTxtr; + +typedef struct +{ + uObjTxtr txtr; + uObjSprite sprite; +} uObjTxSprite; + +/* rectangle scissoring macros */ +#define gScC_(c) ((c) < 0 ? 0 : (c)) +#define gScD_(t, c, d) \ + ( \ + (c) < 0 ? \ + ( \ + (d) < 0 ? \ + (t) + (c) * (d) / 0x80 : \ + (t) - (c) * (d) / 0x80 \ + ) : \ + (t) \ + ) + +/* texture loading helper macros */ +#define G_SIZ_LDSIZ(siz) ((siz) < G_IM_SIZ_16b ? G_IM_SIZ_16b : (siz)) +#define G_SIZ_BITS(siz) (4 << gI_(siz)) +#define G_SIZ_LDBITS(siz) ((siz) < G_IM_SIZ_16b ? G_SIZ_BITS(siz) : 16) +#define G_DXT(siz, width) \ + ( \ + (width) * G_SIZ_BITS(siz) > 64 ? \ + ((1 << 11) + (width) * G_SIZ_BITS(siz) / 64 - 1) / \ + ((width) * G_SIZ_BITS(siz) / 64) : \ + (1 << 11) \ + ) +#define G_LTB_LRS(width, height, siz) \ + ( \ + (((width) * (height) + 1) * G_SIZ_BITS(siz) - 1) / \ + G_SIZ_BITS(G_SIZ_LDSIZ(siz)) - 1 \ + ) +#define G_LDBLK_TXL(txl) \ + ( \ + (txl) > G_TX_LDBLK_MAX_TXL ? \ + G_TX_LDBLK_MAX_TXL : \ + (txl) \ + ) + +/* depth value macros */ +#define gZp_(zval, near, far) \ + ( \ + (1.f - (float)(near) / (float)(zval)) / \ + (1.f - (float)(near) / (float)(far)) \ + ) +#define gZo_(zval, near, far) \ + ( \ + ((float)(zval) - (float)(near)) / \ + ((float)(far) - (float)(near)) \ + ) +#define gZf_(zval, near, far, flag) \ + qs1616 \ + ( \ + (flag) == G_BZ_PERSP ? \ + gZp_(zval, near, far) : \ + gZo_(zval, near, far) \ + ) +#define G_DEPTOZSrg(zval, near, far, flag, zmin, zmax) \ + ( \ + gZf_(zval, near, far, flag) * \ + ((int32_t)((zmax) - (zmin)) & ~(int32_t)1) + \ + qs1616(zmin) \ + ) +#define G_DEPTOZS(zval, near, far, flag) \ + G_DEPTOZSrg(zval, near, far, flag, 0, G_MAXZ) + +/* vertex ordering macros */ +#define gV2_(v0, v1, flag) \ + ( \ + (flag) == 0 ? gI_(v0) : \ + gI_(v1) \ + ) +#define gV3_(v0, v1, v2, flag) \ + ( \ + (flag) == 0 ? gI_(v0) : \ + (flag) == 1 ? gI_(v1) : \ + gI_(v2) \ + ) +#define gV4_(v0, v1, v2, v3, flag) \ + ( \ + (flag) == 0 ? gI_(v0) : \ + (flag) == 1 ? gI_(v1) : \ + (flag) == 2 ? gI_(v2) : \ + gI_(v3) \ + ) + +/* sprite texture parameter macros */ +#define GS_PIX2TMEM(pix, siz) ((pix) * G_SIZ_BITS(siz) / 64) +#define GS_TB_TSIZE(pix, siz) (GS_PIX2TMEM(pix, siz) - 1) +#define GS_TB_TLINE(pix, siz) (((1 << 11) - 1) / GS_PIX2TMEM(pix, siz) + 1) +#define GS_TT_TWIDTH(pix, siz) (GS_PIX2TMEM(pix, siz) * 4 - 1) +#define GS_TT_THEIGHT(pix, siz) ((pix) * 4 - 1) +#define GS_PAL_HEAD(head) ((head) + 256) +#define GS_PAL_NUM(num) ((num) - 1) + +/* fixed-point conversion macros */ +#define qu08(n) ((qu08_t)((n) * 0x100)) +#define qu016(n) ((qu016_t)((n) * 0x10000)) +#define qs48(n) ((qs48_t)((n) * 0x0100)) +#define qs510(n) ((qs510_t)((n) * 0x0400)) +#define qu510(n) ((qu510_t)((n) * 0x0400)) +#define qs102(n) ((qs102_t)((n) * 0x0004)) +#define qu102(n) ((qu102_t)((n) * 0x0004)) +#define qs105(n) ((qs105_t)((n) * 0x0020)) +#define qu105(n) ((qu105_t)((n) * 0x0020)) +#define qs132(n) ((qs132_t)((n) * 0x0004)) +#define qs142(n) ((qs142_t)((n) * 0x0004)) +#define qs1516(n) ((qs1516_t)((n) * 0x00010000)) +#define qs1616(n) ((qs1616_t)((n) * 0x00010000)) +#define qs205(n) ((qs205_t)((n) * 0x00000020)) + +/* private helper macros */ +#define gI_(i) ((uint32_t)(i)) +#define gL_(l) ((uint64_t)(l)) +#define gF_(i, n, s) ((gI_(i) & ((gI_(1) << (n)) - 1)) << (s)) +#define gFL_(l, n, s) ((gL_(l) & ((gL_(1) << (n)) - 1)) << (s)) +#define gO_(opc, hi, lo) ((Gfx){gF_(opc, 8, 24) | gI_(hi), gI_(lo)}) +#define gD_(gdl, m, ...) gDisplayListPut(gdl, m(__VA_ARGS__)) + +#endif diff --git a/ZAPDTR/lib/libgfxd/gfxd.c b/ZAPDTR/lib/libgfxd/gfxd.c new file mode 100644 index 000000000..76d7ded8a --- /dev/null +++ b/ZAPDTR/lib/libgfxd/gfxd.c @@ -0,0 +1,863 @@ +#include +#include +#include +#include +#include +#ifdef _WIN32 +# include +# define read _read +# define write _write +#else +# include +#endif +#include "gbi.h" +#include "gfxd.h" +#include "priv.h" + +static TLOCAL struct gfxd_state state; + +static int buffer_input_fn(void *buf, int count) +{ + if (count > config.input_buf_size) + count = config.input_buf_size; + memcpy(buf, config.input_buf, count); + config.input_buf += count; + config.input_buf_size -= count; + return count; +} + +static int buffer_output_fn(const char *buf, int count) +{ + if (count > config.output_buf_size) + count = config.output_buf_size; + memcpy(config.output_buf, buf, count); + config.output_buf += count; + config.output_buf_size -= count; + return count; +} + +static int fd_input_fn(void *buf, int count) +{ + return read(config.input_fd, buf, count); +} + +static int fd_output_fn(const char *buf, int count) +{ + return write(config.output_fd, buf, count); +} + +static void swap_words(Gfx *gfx) +{ + uint8_t b[8]; + uint8_t *pw = (void *) gfx; + uint8_t *pb = b; + + int endian = config.endian; + int wordsize = config.wordsize; + + for (int i = 0; i < 8 / wordsize; i++) + { + if (endian == gfxd_endian_host) + { + switch (wordsize) + { + case 1: + { + uint8_t w = *(uint8_t *) pw; + *pb++ = w >> 0; + break; + } + case 2: + { + uint16_t w = *(uint16_t *) pw; + *pb++ = w >> 8; + *pb++ = w >> 0; + break; + } + case 4: + { + uint32_t w = *(uint32_t *) pw; + *pb++ = w >> 24; + *pb++ = w >> 16; + *pb++ = w >> 8; + *pb++ = w >> 0; + break; + } + case 8: + { + uint64_t w = *(uint64_t *) pw; + *pb++ = w >> 56; + *pb++ = w >> 48; + *pb++ = w >> 40; + *pb++ = w >> 32; + *pb++ = w >> 24; + *pb++ = w >> 16; + *pb++ = w >> 8; + *pb++ = w >> 0; + break; + } + } + } + else + { + for (int j = 0; j < wordsize; j++) + { + if (endian == gfxd_endian_little) + *pb++ = pw[wordsize - 1 - j]; + else + *pb++ = pw[j]; + } + } + pw += wordsize; + } + + gfx->hi = ((uint32_t) b[0] << 24) + | ((uint32_t) b[1] << 16) + | ((uint32_t) b[2] << 8) + | ((uint32_t) b[3] << 0); + gfx->lo = ((uint32_t) b[4] << 24) + | ((uint32_t) b[5] << 16) + | ((uint32_t) b[6] << 8) + | ((uint32_t) b[7] << 0); +} + +static void get_more_input(void) +{ + if (state.end_input != 0) + return; + + char *recv_buf = (void *) &state.gfx[0]; + + while (state.n_gfx < sizeof(state.gfx) / sizeof(state.gfx[0])) + { + int n_read = sizeof(state.gfx) - state.n_byte; + n_read = config.input_fn(&recv_buf[state.n_byte], n_read); + if (n_read == 0) + return; + state.n_byte += n_read; + + while (state.n_gfx < state.n_byte / sizeof(Gfx)) + { + Gfx gfx = state.gfx[state.n_gfx]; + gfxd_macro_t *m = &state.macro[state.n_gfx]; + + swap_words(&gfx); + + int ret = config.ucode->disas_fn(m, gfx.hi, gfx.lo); + if (ret != 0 && config.stop_on_invalid != 0) + { + state.end_input = 1; + state.ret = ret; + return; + } + + state.n_gfx++; + } + } +} + +static int32_t typed_arg_i(int type, int idx) +{ + const gfxd_value_t *v = gfxd_value_by_type(type, idx); + if (v != NULL) + return v->i; + else + return -1; +} + +static uint32_t typed_arg_u(int type, int idx) +{ + const gfxd_value_t *v = gfxd_value_by_type(type, idx); + if (v != NULL) + return v->u; + else + return 0; +} + + +TLOCAL struct gfxd_config config = +{ + .ucode = NULL, + .endian = gfxd_endian_big, + .wordsize = 4, + .arg = NULL, + + .stop_on_invalid = 1, + .stop_on_end = 1, + .emit_dec_color = 0, + .emit_q_macro = 0, + .emit_ext_macro = 0, + + .input_buf = NULL, + .input_buf_size = 0, + .input_fn = &buffer_input_fn, + + .output_buf = NULL, + .output_buf_size = 0, + .output_fn = &buffer_output_fn, + + .macro_fn = &gfxd_macro_dflt, + .arg_fn = &gfxd_arg_dflt, + + .tlut_fn = NULL, + .timg_fn = NULL, + .cimg_fn = NULL, + .zimg_fn = NULL, + .dl_fn = NULL, + .mtx_fn = NULL, + .lookat_fn = NULL, + .light_fn = NULL, + .seg_fn = NULL, + .vtx_fn = NULL, + .vp_fn = NULL, + .uctext_fn = NULL, + .ucdata_fn = NULL, + .dram_fn = NULL, +}; + +void gfxd_input_buffer(const void *buf, int size) +{ + config.input_buf = buf; + config.input_buf_size = size; + config.input_fn = &buffer_input_fn; +} + +void gfxd_output_buffer(char *buf, int size) +{ + config.output_buf = buf; + config.output_buf_size = size; + config.output_fn = &buffer_output_fn; +} + +void gfxd_input_fd(int fd) +{ + config.input_fd = fd; + config.input_fn = &fd_input_fn; +} + +void gfxd_output_fd(int fd) +{ + config.output_fd = fd; + config.output_fn = &fd_output_fn; +} + +void gfxd_input_callback(gfxd_input_fn_t *fn) +{ + if (fn != NULL) + config.input_fn = fn; + else + gfxd_input_buffer(NULL, 0); +} + +void gfxd_output_callback(gfxd_output_fn_t *fn) +{ + if (fn != NULL) + config.output_fn = fn; + else + gfxd_output_buffer(NULL, 0); +} + +void gfxd_macro_fn(gfxd_macro_fn_t *fn) +{ + if (fn != NULL) + config.macro_fn = fn; + else + config.macro_fn = gfxd_macro_dflt; +} + +void gfxd_arg_fn(gfxd_arg_fn_t *fn) +{ + if (fn != NULL) + config.arg_fn = fn; + else + config.arg_fn = gfxd_arg_dflt; +} + +int gfxd_write(const void *buf, int count) +{ + return config.output_fn(buf, count); +} + +int gfxd_puts(const char *str) +{ + return gfxd_write(str, strlen(str)); +} + +int gfxd_printf(const char *fmt, ...) +{ + char s[256]; + + va_list arg; + va_start(arg, fmt); + int n = vsnprintf(s, sizeof(s), fmt, arg); + va_end(arg); + + if (n > sizeof(s) - 1) + n = sizeof(s) - 1; + + return gfxd_write(s, n); +} + +int gfxd_print_value(int type, const gfxd_value_t *value) +{ + return config.ucode->arg_tbl[type].fn(value); +} + +int gfxd_macro_dflt(void) +{ + gfxd_macro_t *m = &state.macro[0]; + const gfxd_macro_type_t *t = &config.ucode->macro_tbl[m->id]; + + const char *name = gfxd_macro_name(); + if (name == NULL) + { + if (config.arg != NULL) + { + gfxd_puts(config.arg); + gfxd_puts(" = "); + } + + gfxd_puts("(Gfx){"); + } + else + { + gfxd_puts(name); + gfxd_puts("("); + + if (config.arg != NULL) + { + gfxd_puts(config.arg); + if (t->n_arg != 0) + gfxd_puts(", "); + } + } + + for (int i = 0; i < t->n_arg; i++) + { + if (i != 0) + gfxd_puts(", "); + + config.arg_fn(i); + } + + if (name == NULL) + gfxd_puts("}"); + else + gfxd_puts(")"); + + return 0; +} + +int gfxd_arg_callbacks(int arg_num) +{ + int id = gfxd_macro_id(); + + switch (gfxd_arg_type(arg_num)) + { + case gfxd_Tlut: + { + if (config.tlut_fn != NULL) + { + int32_t num; + if (id == gfxd_DPLoadTLUT_pal16) + num = 16; + else if (id == gfxd_DPLoadTLUT_pal256) + num = 256; + else + num = typed_arg_i(gfxd_Num, 0); + return config.tlut_fn( + typed_arg_u(gfxd_Tlut, 0), + typed_arg_i(gfxd_Pal, 0), + num); + } + break; + } + case gfxd_Timg: + { + if (config.timg_fn != NULL) + { + int32_t siz = typed_arg_i(gfxd_Siz, 0); + if (siz == -1) + siz = G_IM_SIZ_4b; + return config.timg_fn( + typed_arg_u(gfxd_Timg, 0), + typed_arg_i(gfxd_Fmt, 0), + siz, + typed_arg_i(gfxd_Dim, 0), + typed_arg_i(gfxd_Dim, 1), + typed_arg_i(gfxd_Pal, 0)); + } + break; + } + case gfxd_Cimg: + { + if (config.cimg_fn != NULL) + { + return config.cimg_fn( + typed_arg_u(gfxd_Cimg, 0), + typed_arg_i(gfxd_Fmt, 0), + typed_arg_i(gfxd_Siz, 0), + typed_arg_i(gfxd_Dim, 0)); + } + break; + } + case gfxd_Zimg: + { + if (config.zimg_fn != NULL) + { + return config.zimg_fn( + typed_arg_u(gfxd_Zimg, 0)); + } + break; + } + case gfxd_Dl: + { + if (config.dl_fn != NULL) + { + return config.dl_fn( + typed_arg_u(gfxd_Dl, 0)); + } + break; + } + case gfxd_Mtxptr: + { + if (config.mtx_fn != NULL) + { + return config.mtx_fn( + typed_arg_u(gfxd_Mtxptr, 0)); + } + break; + } + case gfxd_Lookatptr: + { + if (config.lookat_fn != NULL) + { + int32_t num; + if (id == gfxd_SPLookAt) + num = 2; + else + num = 1; + return config.lookat_fn( + typed_arg_u(gfxd_Lookatptr, 0), + num); + } + break; + } + case gfxd_Lightptr: + { + if (config.light_fn != NULL) + { + int32_t num; + if (id == gfxd_SPSetLights1) + num = 1; + else if (id == gfxd_SPSetLights2) + num = 2; + else if (id == gfxd_SPSetLights3) + num = 3; + else if (id == gfxd_SPSetLights4) + num = 4; + else if (id == gfxd_SPSetLights5) + num = 5; + else if (id == gfxd_SPSetLights6) + num = 6; + else if (id == gfxd_SPSetLights7) + num = 7; + else + num = 1; + return config.light_fn( + typed_arg_u(gfxd_Lightptr, 0), + num); + } + break; + + } + case gfxd_Segptr: + { + if (config.seg_fn != NULL) + { + return config.seg_fn( + typed_arg_u(gfxd_Segptr, 0), + typed_arg_i(gfxd_Seg, 0)); + } + break; + } + case gfxd_Vtxptr: + { + if (config.vtx_fn != NULL) + { + return config.vtx_fn( + typed_arg_u(gfxd_Vtxptr, 0), + typed_arg_i(gfxd_Num, 0)); + } + break; + } + case gfxd_Vpptr: + { + if (config.vp_fn != NULL) + { + return config.vp_fn( + typed_arg_u(gfxd_Vpptr, 0)); + } + break; + } + case gfxd_Uctext: + { + if (config.uctext_fn != NULL) + { + return config.uctext_fn( + typed_arg_u(gfxd_Uctext, 0), + 0x1000); + } + break; + } + case gfxd_Ucdata: + { + if (config.ucdata_fn != NULL) + { + uint32_t size; + if (id == gfxd_SPLoadUcodeEx) + size = typed_arg_u(gfxd_Size, 0); + else + size = 0x800; + return config.ucdata_fn( + typed_arg_u(gfxd_Ucdata, 0), + size); + } + break; + } + case gfxd_Dram: + { + if (config.dram_fn != NULL) + { + return config.dram_fn( + typed_arg_u(gfxd_Dram, 0), + typed_arg_u(gfxd_Size, 0)); + } + break; + } + } + + return 0; +} + +void gfxd_arg_dflt(int arg_num) +{ + if (gfxd_arg_callbacks(arg_num) == 0) + { + gfxd_arg_t *a = &state.macro[0].arg[arg_num]; + + gfxd_print_value(a->type, &a->value); + } +} + +void gfxd_tlut_callback(gfxd_tlut_fn_t *fn) +{ + config.tlut_fn = fn; +} + +void gfxd_timg_callback(gfxd_timg_fn_t *fn) +{ + config.timg_fn = fn; +} + +void gfxd_cimg_callback(gfxd_cimg_fn_t *fn) +{ + config.cimg_fn = fn; +} + +void gfxd_zimg_callback(gfxd_zimg_fn_t *fn) +{ + config.zimg_fn = fn; +} + +void gfxd_dl_callback(gfxd_dl_fn_t *fn) +{ + config.dl_fn = fn; +} + +void gfxd_mtx_callback(gfxd_mtx_fn_t *fn) +{ + config.mtx_fn = fn; +} + +void gfxd_lookat_callback(gfxd_lookat_fn_t *fn) +{ + config.lookat_fn = fn; +} + +void gfxd_light_callback(gfxd_light_fn_t *fn) +{ + config.light_fn = fn; +} + +void gfxd_seg_callback(gfxd_seg_fn_t *fn) +{ + config.seg_fn = fn; +} + +void gfxd_vtx_callback(gfxd_vtx_fn_t *fn) +{ + config.vtx_fn = fn; +} + +void gfxd_vp_callback(gfxd_vp_fn_t *fn) +{ + config.vp_fn = fn; +} + +void gfxd_uctext_callback(gfxd_uctext_fn_t *fn) +{ + config.uctext_fn = fn; +} + +void gfxd_ucdata_callback(gfxd_ucdata_fn_t *fn) +{ + config.ucdata_fn = fn; +} + +void gfxd_dram_callback(gfxd_dram_fn_t *fn) +{ + config.dram_fn = fn; +} + +void gfxd_target(gfxd_ucode_t ucode) +{ + config.ucode = ucode; +} + +void gfxd_endian(int endian, int wordsize) +{ + config.endian = endian; + config.wordsize = wordsize; +} + +void gfxd_dynamic(const char *arg) +{ + config.arg = arg; +} + +void gfxd_enable(int cap) +{ + switch (cap) + { + case gfxd_stop_on_invalid: + config.stop_on_invalid = 1; + break; + + case gfxd_stop_on_end: + config.stop_on_end = 1; + break; + + case gfxd_emit_dec_color: + config.emit_dec_color = 1; + break; + + case gfxd_emit_q_macro: + config.emit_q_macro = 1; + break; + + case gfxd_emit_ext_macro: + config.emit_ext_macro = 1; + break; + } +} + +void gfxd_disable(int cap) +{ + switch (cap) + { + case gfxd_stop_on_invalid: + config.stop_on_invalid = 0; + return; + + case gfxd_stop_on_end: + config.stop_on_end = 0; + return; + + case gfxd_emit_dec_color: + config.emit_dec_color = 0; + break; + + case gfxd_emit_q_macro: + config.emit_q_macro = 0; + break; + + case gfxd_emit_ext_macro: + config.emit_ext_macro = 0; + break; + } +} + +void gfxd_udata_set(void *ptr) +{ + config.udata = ptr; +} + +void *gfxd_udata_get(void) +{ + return config.udata; +} + +int gfxd_execute(void) +{ + state.macro_offset = 0; + state.n_byte = 0; + state.n_gfx = 0; + state.end_input = 0; + state.ret = 0; + + for (;;) + { + get_more_input(); + if (state.n_gfx == 0) + break; + + gfxd_macro_t *m = &state.macro[0]; + config.ucode->combine_fn(m, state.n_gfx); + + const gfxd_macro_type_t *t = &config.ucode->macro_tbl[m->id]; + if (t->ext != 0 && config.emit_ext_macro == 0) + { + Gfx gfx = state.gfx[0]; + swap_words(&gfx); + + t = &config.ucode->macro_tbl[gfxd_Invalid]; + t->disas_fn(m, gfx.hi, gfx.lo); + } + + int ret = config.macro_fn(); + if (ret != 0) + { + state.ret = ret; + break; + } + + if (config.stop_on_end != 0 + && (m->id == gfxd_SPBranchList + || m->id == gfxd_SPEndDisplayList)) + { + break; + } + + int n_pop = config.ucode->macro_tbl[m->id].n_gfx; + int n_rem = state.n_gfx - n_pop; + { + int n_byte = n_rem * sizeof(gfxd_macro_t); + memmove(&state.macro[0], &state.macro[n_pop], n_byte); + state.n_gfx = n_rem; + } + { + int n_byte = n_rem * sizeof(Gfx); + memmove(&state.gfx[0], &state.gfx[n_pop], n_byte); + state.n_byte = n_byte; + } + state.macro_offset += n_pop * sizeof(Gfx); + } + + return state.ret; +} + +int gfxd_macro_offset(void) +{ + return state.macro_offset; +} + +int gfxd_macro_packets(void) +{ + return config.ucode->macro_tbl[state.macro[0].id].n_gfx; +} + +const void *gfxd_macro_data(void) +{ + return state.gfx; +} + +int gfxd_macro_id(void) +{ + return state.macro[0].id; +} + +const char *gfxd_macro_name(void) +{ + int id = state.macro[0].id; + const gfxd_macro_type_t *t = &config.ucode->macro_tbl[id]; + + if (t->prefix == NULL && t->suffix == NULL) + { + return NULL; + } + else + { + static TLOCAL char buf[32]; + + char *p = buf; + if (t->prefix != NULL) + { + const char *s = t->prefix; + while (*s != '\0') + *p++ = *s++; + } + *p++ = 'g'; + if (config.arg == NULL) + *p++ = 's'; + if (t->suffix != NULL) + { + const char *s = t->suffix; + while (*s != '\0') + *p++ = *s++; + } + *p++ = '\0'; + + return buf; + } +} + +int gfxd_arg_count(void) +{ + return config.ucode->macro_tbl[state.macro[0].id].n_arg; +} + +int gfxd_arg_type(int arg_num) +{ + return state.macro[0].arg[arg_num].type; +} + +const char *gfxd_arg_name(int arg_num) +{ + return state.macro[0].arg[arg_num].name; +} + +int gfxd_arg_fmt(int arg_num) +{ + return config.ucode->arg_tbl[state.macro[0].arg[arg_num].type].fmt; +} + +const gfxd_value_t *gfxd_arg_value(int arg_num) +{ + return &state.macro[0].arg[arg_num].value; +} + +const gfxd_value_t *gfxd_value_by_type(int type, int idx) +{ + gfxd_macro_t *m = &state.macro[0]; + const gfxd_macro_type_t *t = &config.ucode->macro_tbl[m->id]; + + for (int i = 0; i < t->n_arg; i++) + { + gfxd_arg_t *a = &m->arg[i]; + if (a->type == type) + { + if (idx == 0) + return &a->value; + else + idx--; + } + } + + return NULL; +} + +int gfxd_arg_valid(int arg_num) +{ + return state.macro[0].arg[arg_num].bad == 0; +} diff --git a/ZAPDTR/lib/libgfxd/gfxd.h b/ZAPDTR/lib/libgfxd/gfxd.h new file mode 100644 index 000000000..268bbfa10 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/gfxd.h @@ -0,0 +1,387 @@ +#ifndef GFXD_H +#define GFXD_H +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum +{ + gfxd_Word, /* generic word */ + gfxd_Opcode, /* command opcode (G_*) */ + gfxd_Coordi, /* integer coordinate */ + gfxd_Coordq, /* fractional (q10.2) coordinate */ + gfxd_Pal, /* palette index */ + gfxd_Tlut, /* tlut pointer */ + gfxd_Timg, /* texture image pointer */ + gfxd_Tmem, /* tmem address */ + gfxd_Tile, /* tile index */ + gfxd_Fmt, /* texture format */ + gfxd_Siz, /* texture pixel size */ + gfxd_Dim, /* integer dimension (width / height) */ + gfxd_Cm, /* clamp and mirror flags */ + gfxd_Tm, /* tile mask */ + gfxd_Ts, /* tile shift */ + gfxd_Dxt, /* texture dxt */ + gfxd_Tag, /* generic tag */ + gfxd_Pm, /* pipeline mode */ + gfxd_Colorpart, /* color component */ + gfxd_Color, /* color */ + gfxd_Lodfrac, /* lod fraction (q0.8) */ + gfxd_Cimg, /* color image pointer */ + gfxd_Zimg, /* depth image pointer */ + gfxd_Ac, /* alpha compare mode */ + gfxd_Ad, /* alpha dither mode */ + gfxd_Cd, /* color dither mode */ + gfxd_Ccpre, /* color combiner preset index */ + gfxd_Ccmuxa, /* color mux operand (a) */ + gfxd_Ccmuxb, /* color mux operand (b) */ + gfxd_Ccmuxc, /* color mux operand (c) */ + gfxd_Ccmuxd, /* color mux operand (d) */ + gfxd_Acmuxabd, /* alpha mux operand (a, b, or d) */ + gfxd_Acmuxc, /* alpha mux operand (c) */ + gfxd_Cv, /* color convert operand */ + gfxd_Tc, /* texture convert mode */ + gfxd_Cyc, /* cycle type */ + gfxd_Zs, /* depth source mode */ + gfxd_Ck, /* combine key mode */ + gfxd_Keyscale, /* combine key scale */ + gfxd_Keywidth, /* combine key width */ + gfxd_Zi, /* integer depth */ + gfxd_Rm1, /* cycle 1 render mode */ + gfxd_Rm2, /* cycle 2 render mode */ + gfxd_Sc, /* scissor mode */ + gfxd_Td, /* texture detail mode */ + gfxd_Tf, /* texture filter mode */ + gfxd_Tl, /* texture LOD mode */ + gfxd_Tt, /* textuure LUT mode */ + gfxd_Tp, /* texture perspective mode */ + gfxd_Line, /* texture line size */ + gfxd_Vtx, /* vertex index */ + gfxd_Vtxflag, /* vertex flag */ + gfxd_Dl, /* display list pointer */ + gfxd_Zraw, /* raw depth value (q16.16) */ + gfxd_Dlflag, /* display list flag */ + gfxd_Cr, /* clip ratio */ + gfxd_Num, /* element count */ + gfxd_Fogz, /* fog factor */ + gfxd_Fogp, /* fog position (0 - 1000) */ + gfxd_Mtxptr, /* matrix pointer */ + gfxd_Gm, /* geometry mode */ + gfxd_Mwo_matrix, /* matrix moveword offset */ + gfxd_Linewd, /* line width (1.5 + q7.1) */ + gfxd_Uctext, /* microcode text pointer */ + gfxd_Ucdata, /* microcode data pointer */ + gfxd_Size, /* data size */ + gfxd_Lookatptr, /* lookat pointer */ + gfxd_Mtxparam, /* matrix param */ + gfxd_Mtxstack, /* matrix param (stack select only) */ + gfxd_Mwo_point, /* vertex moveword offset */ + gfxd_Wscale, /* w-component scale (perspnorm) */ + gfxd_Seg, /* segment number */ + gfxd_Segptr, /* segment pointer */ + gfxd_Lightsn, /* dereferenced Lighstn pointer */ + gfxd_Numlights, /* light count (NUMLIGHTS_*) */ + gfxd_Lightnum, /* light number (LIGHT_*) */ + gfxd_Lightptr, /* light pointer */ + gfxd_Tcscale, /* texture coordinate scale */ + gfxd_Switch, /* on-off value */ + gfxd_St, /* vertex coordinate (q10.5) */ + gfxd_Stdelta, /* vertex coordinate delta (q5.10) */ + gfxd_Vtxptr, /* vertex pointer */ + gfxd_Vpptr, /* viewport pointer */ + gfxd_Dram, /* generic dram address */ + gfxd_Sftlo, /* othermode lo shift */ + gfxd_Othermodelo, /* othermode lo value */ + gfxd_Sfthi, /* othermode hi shift */ + gfxd_Othermodehi, /* othermode hi value */ + gfxd_Mw, /* moveword index */ + gfxd_Mwo, /* moveword offset */ + gfxd_Mwo_clip, /* clip ratio moveword offset */ + gfxd_Mwo_lightcol, /* light color moveword offset */ + gfxd_Mv, /* movemem index */ + gfxd_Mvo, /* movemem offset */ + gfxd_Dmem, /* dmem address */ + gfxd_Dmaflag, /* dma io flag */ +}; + +enum +{ + gfxd_Invalid, + gfxd_DPFillRectangle, + gfxd_DPFullSync, + gfxd_DPLoadSync, + gfxd_DPTileSync, + gfxd_DPPipeSync, + gfxd_DPLoadTLUT_pal16, + gfxd_DPLoadTLUT_pal256, + gfxd_DPLoadMultiBlockYuvS, + gfxd_DPLoadMultiBlockYuv, + gfxd_DPLoadMultiBlock_4bS, + gfxd_DPLoadMultiBlock_4b, + gfxd_DPLoadMultiBlockS, + gfxd_DPLoadMultiBlock, + gfxd__DPLoadTextureBlockYuvS, + gfxd__DPLoadTextureBlockYuv, + gfxd__DPLoadTextureBlock_4bS, + gfxd__DPLoadTextureBlock_4b, + gfxd__DPLoadTextureBlockS, + gfxd__DPLoadTextureBlock, + gfxd_DPLoadTextureBlockYuvS, + gfxd_DPLoadTextureBlockYuv, + gfxd_DPLoadTextureBlock_4bS, + gfxd_DPLoadTextureBlock_4b, + gfxd_DPLoadTextureBlockS, + gfxd_DPLoadTextureBlock, + gfxd_DPLoadMultiTileYuv, + gfxd_DPLoadMultiTile_4b, + gfxd_DPLoadMultiTile, + gfxd__DPLoadTextureTileYuv, + gfxd__DPLoadTextureTile_4b, + gfxd__DPLoadTextureTile, + gfxd_DPLoadTextureTileYuv, + gfxd_DPLoadTextureTile_4b, + gfxd_DPLoadTextureTile, + gfxd_DPLoadBlock, + gfxd_DPNoOp, + gfxd_DPNoOpTag, + gfxd_DPPipelineMode, + gfxd_DPSetBlendColor, + gfxd_DPSetEnvColor, + gfxd_DPSetFillColor, + gfxd_DPSetFogColor, + gfxd_DPSetPrimColor, + gfxd_DPSetColorImage, + gfxd_DPSetDepthImage, + gfxd_DPSetTextureImage, + gfxd_DPSetAlphaCompare, + gfxd_DPSetAlphaDither, + gfxd_DPSetColorDither, + gfxd_DPSetCombineMode, + gfxd_DPSetCombineLERP, + gfxd_DPSetConvert, + gfxd_DPSetTextureConvert, + gfxd_DPSetCycleType, + gfxd_DPSetDepthSource, + gfxd_DPSetCombineKey, + gfxd_DPSetKeyGB, + gfxd_DPSetKeyR, + gfxd_DPSetPrimDepth, + gfxd_DPSetRenderMode, + gfxd_DPSetScissor, + gfxd_DPSetScissorFrac, + gfxd_DPSetTextureDetail, + gfxd_DPSetTextureFilter, + gfxd_DPSetTextureLOD, + gfxd_DPSetTextureLUT, + gfxd_DPSetTexturePersp, + gfxd_DPSetTile, + gfxd_DPSetTileSize, + gfxd_SP1Triangle, + gfxd_SP2Triangles, + gfxd_SP1Quadrangle, + gfxd_SPBranchLessZraw, + gfxd_SPBranchList, + gfxd_SPClipRatio, + gfxd_SPCullDisplayList, + gfxd_SPDisplayList, + gfxd_SPEndDisplayList, + gfxd_SPFogFactor, + gfxd_SPFogPosition, + gfxd_SPForceMatrix, + gfxd_SPSetGeometryMode, + gfxd_SPClearGeometryMode, + gfxd_SPLoadGeometryMode, + gfxd_SPInsertMatrix, + gfxd_SPLine3D, + gfxd_SPLineW3D, + gfxd_SPLoadUcode, + gfxd_SPLookAtX, + gfxd_SPLookAtY, + gfxd_SPLookAt, + gfxd_SPMatrix, + gfxd_SPModifyVertex, + gfxd_SPPerspNormalize, + gfxd_SPPopMatrix, + gfxd_SPPopMatrixN, + gfxd_SPSegment, + gfxd_SPSetLights1, + gfxd_SPSetLights2, + gfxd_SPSetLights3, + gfxd_SPSetLights4, + gfxd_SPSetLights5, + gfxd_SPSetLights6, + gfxd_SPSetLights7, + gfxd_SPNumLights, + gfxd_SPLight, + gfxd_SPLightColor, + gfxd_SPTexture, + gfxd_SPTextureRectangle, + gfxd_SPTextureRectangleFlip, + gfxd_SPVertex, + gfxd_SPViewport, + gfxd_DPLoadTLUTCmd, + gfxd_DPLoadTLUT, + gfxd_BranchZ, + gfxd_DisplayList, + gfxd_DPHalf1, + gfxd_DPHalf2, + gfxd_DPWord, + gfxd_DPLoadTile, + gfxd_SPGeometryMode, + gfxd_SPSetOtherMode, + gfxd_SPSetOtherModeLo, + gfxd_SPSetOtherModeHi, + gfxd_DPSetOtherMode, + gfxd_MoveWd, + gfxd_MoveMem, + gfxd_SPDma_io, + gfxd_SPDmaRead, + gfxd_SPDmaWrite, + gfxd_LoadUcode, + gfxd_SPLoadUcodeEx, + gfxd_TexRect, + gfxd_TexRectFlip, + gfxd_SPNoOp, + gfxd_Special3, + gfxd_Special2, + gfxd_Special1, +}; + +enum +{ + gfxd_stop_on_invalid, + gfxd_stop_on_end, + gfxd_emit_dec_color, + gfxd_emit_q_macro, + gfxd_emit_ext_macro, +}; + +enum +{ + gfxd_endian_big, + gfxd_endian_little, + gfxd_endian_host, +}; + +enum +{ + gfxd_argfmt_i, + gfxd_argfmt_u, + gfxd_argfmt_f, +}; + +typedef union +{ + int32_t i; + uint32_t u; + float f; +} gfxd_value_t; + +typedef const struct gfxd_ucode *gfxd_ucode_t; + +typedef int gfxd_input_fn_t(void *buf, int count); +void gfxd_input_buffer(const void *buf, int size); +void gfxd_input_fd(int fd); +void gfxd_input_callback(gfxd_input_fn_t *fn); + +typedef int gfxd_output_fn_t(const char *buf, int count); +void gfxd_output_buffer(char *buf, int size); +void gfxd_output_fd(int fd); +void gfxd_output_callback(gfxd_output_fn_t *fn); + +typedef int gfxd_macro_fn_t(void); +void gfxd_macro_fn(gfxd_macro_fn_t *fn); +gfxd_macro_fn_t gfxd_macro_dflt; + +typedef void gfxd_arg_fn_t(int arg_num); +void gfxd_arg_fn(gfxd_arg_fn_t *fn); +gfxd_arg_fn_t gfxd_arg_dflt; + +typedef int gfxd_tlut_fn_t(uint32_t tlut, int32_t idx, int32_t count); +void gfxd_tlut_callback(gfxd_tlut_fn_t *fn); + +typedef int gfxd_timg_fn_t(uint32_t timg, int32_t fmt, int32_t siz, + int32_t width, int32_t height, int32_t pal); +void gfxd_timg_callback(gfxd_timg_fn_t *fn); + +typedef int gfxd_cimg_fn_t(uint32_t cimg, int32_t fmt, int32_t siz, + int32_t width); +void gfxd_cimg_callback(gfxd_cimg_fn_t *fn); + +typedef int gfxd_zimg_fn_t(uint32_t zimg); +void gfxd_zimg_callback(gfxd_zimg_fn_t *fn); + +typedef int gfxd_dl_fn_t(uint32_t dl); +void gfxd_dl_callback(gfxd_dl_fn_t *fn); + +typedef int gfxd_mtx_fn_t(uint32_t mtx); +void gfxd_mtx_callback(gfxd_mtx_fn_t *fn); + +typedef int gfxd_lookat_fn_t(uint32_t lookat, int32_t count); +void gfxd_lookat_callback(gfxd_lookat_fn_t *fn); + +typedef int gfxd_light_fn_t(uint32_t light, int32_t count); +void gfxd_light_callback(gfxd_light_fn_t *fn); + +typedef int gfxd_seg_fn_t(uint32_t seg, int32_t num); +void gfxd_seg_callback(gfxd_seg_fn_t *fn); + +typedef int gfxd_vtx_fn_t(uint32_t vtx, int32_t num); +void gfxd_vtx_callback(gfxd_vtx_fn_t *fn); + +typedef int gfxd_vp_fn_t(uint32_t vp); +void gfxd_vp_callback(gfxd_vp_fn_t *fn); + +typedef int gfxd_uctext_fn_t(uint32_t text, uint32_t size); +void gfxd_uctext_callback(gfxd_uctext_fn_t *fn); + +typedef int gfxd_ucdata_fn_t(uint32_t data, uint32_t size); +void gfxd_ucdata_callback(gfxd_ucdata_fn_t *fn); + +typedef int gfxd_dram_fn_t(uint32_t dram, uint32_t size); +void gfxd_dram_callback(gfxd_dram_fn_t *fn); + +int gfxd_write(const void *buf, int count); +int gfxd_puts(const char *str); +int gfxd_printf(const char *fmt, ...); +int gfxd_print_value(int type, const gfxd_value_t *value); + +void gfxd_target(gfxd_ucode_t ucode); +void gfxd_endian(int endian, int wordsize); +void gfxd_dynamic(const char *arg); +void gfxd_enable(int cap); +void gfxd_disable(int cap); +void gfxd_udata_set(void *ptr); +void *gfxd_udata_get(void); + +int gfxd_execute(void); + +int gfxd_macro_offset(void); +int gfxd_macro_packets(void); +const void *gfxd_macro_data(void); +int gfxd_macro_id(void); +const char *gfxd_macro_name(void); + +int gfxd_arg_count(void); +int gfxd_arg_type(int arg_num); +const char *gfxd_arg_name(int arg_num); +int gfxd_arg_fmt(int arg_num); +const gfxd_value_t *gfxd_arg_value(int arg_num); +const gfxd_value_t *gfxd_value_by_type(int type, int idx); +int gfxd_arg_valid(int arg_num); +int gfxd_arg_callbacks(int arg_num); + +extern const gfxd_ucode_t gfxd_f3d; +extern const gfxd_ucode_t gfxd_f3db; +extern const gfxd_ucode_t gfxd_f3dex; +extern const gfxd_ucode_t gfxd_f3dexb; +extern const gfxd_ucode_t gfxd_f3dex2; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ZAPDTR/lib/libgfxd/priv.h b/ZAPDTR/lib/libgfxd/priv.h new file mode 100644 index 000000000..34d96f675 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/priv.h @@ -0,0 +1,125 @@ +#ifndef GFXD_PRIV_H +#define GFXD_PRIV_H +#include "gfxd.h" + +#define CONFIG_MT + +#ifdef CONFIG_MT +# ifdef _MSC_VER +# define TLOCAL __declspec(thread) +# else +# define TLOCAL _Thread_local +# endif +#else +#define TLOCAL +#endif + +#define UCFUNC static inline + +#define config gfxd_config__ + +typedef int gfxd_argfn_t(const gfxd_value_t *v); + +typedef struct +{ + int fmt; + gfxd_argfn_t * fn; +} gfxd_arg_type_t; + +typedef struct +{ + int type; + const char * name; + gfxd_value_t value; + int bad; +} gfxd_arg_t; + +typedef struct +{ + int id; + gfxd_arg_t arg[18]; +} gfxd_macro_t; + +typedef int gfxd_disas_fn_t(gfxd_macro_t *macro, uint32_t hi, uint32_t lo); +typedef int gfxd_combine_fn_t(gfxd_macro_t *macro, int n_macro); + +typedef struct +{ + const char * prefix; + const char * suffix; + int opcode; + int n_arg; + int n_gfx; + gfxd_disas_fn_t * disas_fn; + gfxd_combine_fn_t * combine_fn; + int alias; + int ext; +} gfxd_macro_type_t; + +struct gfxd_ucode +{ + gfxd_disas_fn_t * disas_fn; + gfxd_combine_fn_t * combine_fn; + const gfxd_arg_type_t * arg_tbl; + const gfxd_macro_type_t * macro_tbl; +}; + +struct gfxd_state +{ + int macro_offset; + + Gfx gfx[9]; + int n_byte; + int n_gfx; + gfxd_macro_t macro[9]; + + int end_input; + int ret; +}; + +struct gfxd_config +{ + gfxd_ucode_t ucode; + int endian; + int wordsize; + const char * arg; + void * udata; + + int stop_on_invalid; + int stop_on_end; + int emit_dec_color; + int emit_q_macro; + int emit_ext_macro; + + const char * input_buf; + int input_buf_size; + int input_fd; + gfxd_input_fn_t * input_fn; + + char * output_buf; + int output_buf_size; + int output_fd; + gfxd_output_fn_t * output_fn; + + gfxd_macro_fn_t * macro_fn; + gfxd_arg_fn_t * arg_fn; + + gfxd_tlut_fn_t * tlut_fn; + gfxd_timg_fn_t * timg_fn; + gfxd_cimg_fn_t * cimg_fn; + gfxd_zimg_fn_t * zimg_fn; + gfxd_dl_fn_t * dl_fn; + gfxd_mtx_fn_t * mtx_fn; + gfxd_lookat_fn_t * lookat_fn; + gfxd_light_fn_t * light_fn; + gfxd_seg_fn_t * seg_fn; + gfxd_vtx_fn_t * vtx_fn; + gfxd_vp_fn_t * vp_fn; + gfxd_uctext_fn_t * uctext_fn; + gfxd_ucdata_fn_t * ucdata_fn; + gfxd_dram_fn_t * dram_fn; +}; + +extern TLOCAL struct gfxd_config gfxd_config__; + +#endif diff --git a/ZAPDTR/lib/libgfxd/uc.c b/ZAPDTR/lib/libgfxd/uc.c new file mode 100644 index 000000000..7efb09105 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include "gbi.h" +#include "gfxd.h" +#include "priv.h" + +#include "uc_argfn.c" +#include "uc_argtbl.c" +#include "uc_macrofn.c" +#include "uc_macrotbl.c" + +UCFUNC int disas(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int opcode = (hi >> 24) & 0xFF; + + for (int i = 0; i < sizeof(macro_tbl) / sizeof(macro_tbl[0]); i++) + { + const gfxd_macro_type_t *t = ¯o_tbl[i]; + if (t->disas_fn != NULL && t->opcode == opcode) + return t->disas_fn(m, hi, lo); + } + + return d_Invalid(m, hi, lo); +} + +UCFUNC int combine(gfxd_macro_t *m, int num) +{ + int opcode = macro_tbl[m->id].opcode; + + for (int i = 0; i < sizeof(macro_tbl) / sizeof(macro_tbl[0]); i++) + { + const gfxd_macro_type_t *t = ¯o_tbl[i]; + if (t->combine_fn != NULL + && t->opcode == opcode + && (t->ext == 0 || config.emit_ext_macro != 0)) + { + if (t->combine_fn(m, num) == 0) + return 0; + } + } + + return -1; +} + +static const struct gfxd_ucode uc = +{ + .disas_fn = disas, + .combine_fn = combine, + .arg_tbl = arg_tbl, + .macro_tbl = macro_tbl, +}; + +const gfxd_ucode_t uc_name = &uc; diff --git a/ZAPDTR/lib/libgfxd/uc_argfn.c b/ZAPDTR/lib/libgfxd/uc_argfn.c new file mode 100644 index 000000000..f65feb60f --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_argfn.c @@ -0,0 +1,1814 @@ +#define MDMASK(md) ((((uint32_t)1 << G_MDSIZ_##md) - 1) << G_MDSFT_##md) +#define MDMASK_RM_C1 ((uint32_t)0xCCCC0000) +#define MDMASK_RM_C2 ((uint32_t)0x33330000) +#define MDMASK_RM_LO ((uint32_t)0x0000FFF8) + +UCFUNC int argfn_i(const gfxd_value_t *v) +{ + return gfxd_printf("%" PRIi32, v->i); +} + +UCFUNC int argfn_u(const gfxd_value_t *v) +{ + return gfxd_printf("%" PRIu32, v->u); +} + +UCFUNC int argfn_x8(const gfxd_value_t *v) +{ + return gfxd_printf("0x%02" PRIX32, v->u); +} + +UCFUNC int argfn_x16(const gfxd_value_t *v) +{ + return gfxd_printf("0x%04" PRIX32, v->u); +} + +UCFUNC int argfn_x32(const gfxd_value_t *v) +{ + return gfxd_printf("0x%08" PRIX32, v->u); +} + +UCFUNC int argfn_color(const gfxd_value_t *v) +{ + if (config.emit_dec_color) + return gfxd_printf("%" PRIu32, v->u); + else + return gfxd_printf("0x%02" PRIX32, v->u); +} + +UCFUNC int argfn_qu08(const gfxd_value_t *v) +{ + if (v->u == 0) + return gfxd_puts("0"); + else if (config.emit_q_macro) + return gfxd_printf("qu08(%.16g)", v->u / 256.f); + else + return gfxd_printf("0x%02" PRIX32, v->u); +} + +UCFUNC int argfn_qu016(const gfxd_value_t *v) +{ + if (v->u == 0) + return gfxd_puts("0"); + else if (config.emit_q_macro) + return gfxd_printf("qu016(%.16g)", v->u / 65536.f); + else + return gfxd_printf("0x%04" PRIX32, v->u); +} + +UCFUNC int argfn_qs48(const gfxd_value_t *v) +{ + if (v->i == 0) + return gfxd_puts("0"); + else if (config.emit_q_macro) + return gfxd_printf("qs48(%.16g)", v->i / 256.f); + else + { + if (v->i < 0) + return gfxd_printf("-0x%04" PRIX32, (uint32_t)-v->i); + else + return gfxd_printf("0x%04" PRIX32, (uint32_t)v->i); + } +} + +UCFUNC int argfn_qs510(const gfxd_value_t *v) +{ + if (v->i == 0) + return gfxd_puts("0"); + else if (config.emit_q_macro) + return gfxd_printf("qs510(%.16g)", v->i / 1024.f); + else + { + if (v->i < 0) + return gfxd_printf("-0x%04" PRIX32, (uint32_t)-v->i); + else + return gfxd_printf("0x%04" PRIX32, (uint32_t)v->i); + } +} + +UCFUNC int argfn_qu102(const gfxd_value_t *v) +{ + if (v->u == 0) + return gfxd_puts("0"); + else if (config.emit_q_macro) + return gfxd_printf("qu102(%.16g)", v->u / 4.f); + else + return gfxd_printf("0x%04" PRIX32, v->u); +} + +UCFUNC int argfn_qs105(const gfxd_value_t *v) +{ + if (v->i == 0) + return gfxd_puts("0"); + else if (config.emit_q_macro) + return gfxd_printf("qs105(%.16g)", v->i / 32.f); + else + { + if (v->i < 0) + return gfxd_printf("-0x%04" PRIX32, (uint32_t)-v->i); + else + return gfxd_printf("0x%04" PRIX32, (uint32_t)v->i); + } +} + +UCFUNC int argfn_qs1616(const gfxd_value_t *v) +{ + if (v->i == 0) + return gfxd_puts("0"); + else if (config.emit_q_macro) + return gfxd_printf("qs1616(%.16g)", v->i / 65536.f); + else + { + if (v->i < 0) + return gfxd_printf("-0x%08" PRIX32, (uint32_t)-v->i); + else + return gfxd_printf("0x%08" PRIX32, (uint32_t)v->i); + } +} + +UCFUNC int argfn_opc(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_SPNOOP: + return gfxd_puts("G_SPNOOP"); + case G_MTX: + return gfxd_puts("G_MTX"); + case G_MOVEMEM: + return gfxd_puts("G_MOVEMEM"); + case G_VTX: + return gfxd_puts("G_VTX"); + case G_DL: + return gfxd_puts("G_DL"); + case G_RDPHALF_2: + return gfxd_puts("G_RDPHALF_2"); + case G_RDPHALF_1: + return gfxd_puts("G_RDPHALF_1"); +#if defined(F3D_BETA) && (defined(F3D_GBI) || defined(F3DEX_GBI)) + case G_PERSPNORM: + return gfxd_puts("G_PERSPNORM"); +#endif + case G_LINE3D: + return gfxd_puts("G_LINE3D"); +#if defined(F3D_GBI) || defined(F3DEX_GBI) + case G_CLEARGEOMETRYMODE: + return gfxd_puts("G_CLEARGEOMETRYMODE"); + case G_SETGEOMETRYMODE: + return gfxd_puts("G_SETGEOMETRYMODE"); +#endif + case G_ENDDL: + return gfxd_puts("G_ENDDL"); + case G_SETOTHERMODE_L: + return gfxd_puts("G_SETOTHERMODE_L"); + case G_SETOTHERMODE_H: + return gfxd_puts("G_SETOTHERMODE_H"); + case G_TEXTURE: + return gfxd_puts("G_TEXTURE"); + case G_MOVEWORD: + return gfxd_puts("G_MOVEWORD"); + case G_POPMTX: + return gfxd_puts("G_POPMTX"); + case G_CULLDL: + return gfxd_puts("G_CULLDL"); + case G_TRI1: + return gfxd_puts("G_TRI1"); + case G_NOOP: + return gfxd_puts("G_NOOP"); +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + case G_LOAD_UCODE: + return gfxd_puts("G_LOAD_UCODE"); + case G_BRANCH_Z: + return gfxd_puts("G_BRANCH_Z"); + case G_TRI2: + return gfxd_puts("G_TRI2"); +# if !(defined(F3D_BETA) && defined(F3DEX_GBI)) + case G_MODIFYVTX: + return gfxd_puts("G_MODIFYVTX"); +# endif +#endif +#if defined(F3DEX_GBI_2) + case G_QUAD: + return gfxd_puts("G_QUAD"); + case G_SPECIAL_3: + return gfxd_puts("G_SPECIAL_3"); + case G_SPECIAL_2: + return gfxd_puts("G_SPECIAL_2"); + case G_SPECIAL_1: + return gfxd_puts("G_SPECIAL_1"); + case G_DMA_IO: + return gfxd_puts("G_DMA_IO"); + case G_GEOMETRYMODE: + return gfxd_puts("G_GEOMETRYMODE"); +#endif + case G_TEXRECT: + return gfxd_puts("G_TEXRECT"); + case G_TEXRECTFLIP: + return gfxd_puts("G_TEXRECTFLIP"); + case G_RDPLOADSYNC: + return gfxd_puts("G_RDPLOADSYNC"); + case G_RDPPIPESYNC: + return gfxd_puts("G_RDPPIPESYNC"); + case G_RDPTILESYNC: + return gfxd_puts("G_RDPTILESYNC"); + case G_RDPFULLSYNC: + return gfxd_puts("G_RDPFULLSYNC"); + case G_SETKEYGB: + return gfxd_puts("G_SETKEYGB"); + case G_SETKEYR: + return gfxd_puts("G_SETKEYR"); + case G_SETCONVERT: + return gfxd_puts("G_SETCONVERT"); + case G_SETSCISSOR: + return gfxd_puts("G_SETSCISSOR"); + case G_SETPRIMDEPTH: + return gfxd_puts("G_SETPRIMDEPTH"); + case G_RDPSETOTHERMODE: + return gfxd_puts("G_RDPSETOTHERMODE"); + case G_LOADTLUT: + return gfxd_puts("G_LOADTLUT"); + case G_SETTILESIZE: + return gfxd_puts("G_SETTILESIZE"); + case G_LOADBLOCK: + return gfxd_puts("G_LOADBLOCK"); + case G_LOADTILE: + return gfxd_puts("G_LOADTILE"); + case G_SETTILE: + return gfxd_puts("G_SETTILE"); + case G_FILLRECT: + return gfxd_puts("G_FILLRECT"); + case G_SETFILLCOLOR: + return gfxd_puts("G_SETFILLCOLOR"); + case G_SETFOGCOLOR: + return gfxd_puts("G_SETFOGCOLOR"); + case G_SETBLENDCOLOR: + return gfxd_puts("G_SETBLENDCOLOR"); + case G_SETPRIMCOLOR: + return gfxd_puts("G_SETPRIMCOLOR"); + case G_SETENVCOLOR: + return gfxd_puts("G_SETENVCOLOR"); + case G_SETCOMBINE: + return gfxd_puts("G_SETCOMBINE"); + case G_SETTIMG: + return gfxd_puts("G_SETTIMG"); + case G_SETZIMG: + return gfxd_puts("G_SETZIMG"); + case G_SETCIMG: + return gfxd_puts("G_SETCIMG"); + default: + return gfxd_printf("0x%02" PRIX32, (uint32_t)v->i); + } +} + +UCFUNC int argfn_fmt(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_IM_FMT_RGBA: + return gfxd_puts("G_IM_FMT_RGBA"); + case G_IM_FMT_YUV: + return gfxd_puts("G_IM_FMT_YUV"); + case G_IM_FMT_CI: + return gfxd_puts("G_IM_FMT_CI"); + case G_IM_FMT_IA: + return gfxd_puts("G_IM_FMT_IA"); + case G_IM_FMT_I: + return gfxd_puts("G_IM_FMT_I"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int argfn_siz(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_IM_SIZ_4b: + return gfxd_puts("G_IM_SIZ_4b"); + case G_IM_SIZ_8b: + return gfxd_puts("G_IM_SIZ_8b"); + case G_IM_SIZ_16b: + return gfxd_puts("G_IM_SIZ_16b"); + case G_IM_SIZ_32b: + return gfxd_puts("G_IM_SIZ_32b"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int argfn_cm(const gfxd_value_t *v) +{ + int n = 0; + if (v->u & G_TX_MIRROR) + n += gfxd_puts("G_TX_MIRROR"); + else + n += gfxd_puts("G_TX_NOMIRROR"); + if (v->u & G_TX_CLAMP) + n += gfxd_puts(" | G_TX_CLAMP"); + else + n += gfxd_puts(" | G_TX_WRAP"); + return n; +} + +UCFUNC int argfn_tm(const gfxd_value_t *v) +{ + if (v->i == 0) + return gfxd_puts("G_TX_NOMASK"); + else + return gfxd_printf("%" PRIi32, v->i); +} + +UCFUNC int argfn_ts(const gfxd_value_t *v) +{ + if (v->i == 0) + return gfxd_puts("G_TX_NOLOD"); + else + return gfxd_printf("%" PRIi32, v->i); +} + +UCFUNC int argfn_switch(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_ON: + return gfxd_puts("G_ON"); + case G_OFF: + return gfxd_puts("G_OFF"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int argfn_tile(const gfxd_value_t *v) +{ + if (v->i == G_TX_LOADTILE) + return gfxd_puts("G_TX_LOADTILE"); + else if (v->i == G_TX_RENDERTILE) + return gfxd_puts("G_TX_RENDERTILE"); + else + return gfxd_printf("%" PRIi32, v->i); +} + +UCFUNC int argfn_gm(const gfxd_value_t *v) +{ + int n = 0; + uint32_t arg = v->u; + if (arg & G_ZBUFFER) + n += gfxd_puts("G_ZBUFFER"); + if (arg & G_TEXTURE_ENABLE) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_TEXTURE_ENABLE"); + } + if (arg & G_SHADE) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_SHADE"); + } + if ((arg & G_CULL_BOTH) == G_CULL_BOTH) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_CULL_BOTH"); + } + else + { + if (arg & G_CULL_FRONT) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_CULL_FRONT"); + } + if (arg & G_CULL_BACK) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_CULL_BACK"); + } + } + if (arg & G_FOG) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_FOG"); + } + if (arg & G_LIGHTING) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_LIGHTING"); + } + if (arg & G_TEXTURE_GEN) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_TEXTURE_GEN"); + } + if (arg & G_TEXTURE_GEN_LINEAR) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_TEXTURE_GEN_LINEAR"); + } + if (arg & G_LOD) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_LOD"); + } + if (arg & G_SHADING_SMOOTH) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_SHADING_SMOOTH"); + } + if (arg & G_CLIPPING) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("G_CLIPPING"); + } + arg = arg & ~(G_ZBUFFER | G_TEXTURE_ENABLE | G_SHADE | G_CULL_BOTH | + G_FOG | G_LIGHTING | G_TEXTURE_GEN | + G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH | + G_CLIPPING); + if (arg) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_printf("0x%08" PRIX32, arg); + } + return n; +} + +UCFUNC int argfn_sftlo(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_MDSFT_ALPHACOMPARE: + return gfxd_puts("G_MDSFT_ALPHACOMPARE"); + case G_MDSFT_ZSRCSEL: + return gfxd_puts("G_MDSFT_ZSRCSEL"); + case G_MDSFT_RENDERMODE: + return gfxd_puts("G_MDSFT_RENDERMODE"); + case G_MDSFT_BLENDER: + return gfxd_puts("G_MDSFT_BLENDER"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int rm_mode_str(uint32_t arg) +{ + int n = 0; + if (arg & AA_EN) + n += gfxd_puts("AA_EN"); + if (arg & Z_CMP) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("Z_CMP"); + } + if (arg & Z_UPD) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("Z_UPD"); + } + if (arg & IM_RD) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("IM_RD"); + } + if (arg & CLR_ON_CVG) + { + if (n > 0) + n += gfxd_puts(" | "); + n += gfxd_puts("CLR_ON_CVG"); + } + if (n > 0) + n += gfxd_puts(" | "); + int cvg = arg & 0x00000300; + switch (cvg) + { + case CVG_DST_CLAMP: + n += gfxd_puts("CVG_DST_CLAMP"); + break; + case CVG_DST_WRAP: + n += gfxd_puts("CVG_DST_WRAP"); + break; + case CVG_DST_FULL: + n += gfxd_puts("CVG_DST_FULL"); + break; + case CVG_DST_SAVE: + n += gfxd_puts("CVG_DST_SAVE"); + break; + } + int zmode = arg & 0x00000C00; + switch (zmode) + { + case ZMODE_OPA: + n += gfxd_puts(" | ZMODE_OPA"); + break; + case ZMODE_INTER: + n += gfxd_puts(" | ZMODE_INTER"); + break; + case ZMODE_XLU: + n += gfxd_puts(" | ZMODE_XLU"); + break; + case ZMODE_DEC: + n += gfxd_puts(" | ZMODE_DEC"); + break; + } + if (arg & CVG_X_ALPHA) + n += gfxd_puts(" | CVG_X_ALPHA"); + if (arg & ALPHA_CVG_SEL) + n += gfxd_puts(" | ALPHA_CVG_SEL"); + if (arg & FORCE_BL) + n += gfxd_puts(" | FORCE_BL"); + return n; +} + +UCFUNC int rm_cbl_str(uint32_t arg, int c) +{ + int n = 0; + if (c == 2) + arg <<= 2; + int bp = (arg >> 30) & 0b11; + switch (bp) + { + case G_BL_CLR_IN: + n += gfxd_printf("GBL_c%i(G_BL_CLR_IN", c); + break; + case G_BL_CLR_MEM: + n += gfxd_printf("GBL_c%i(G_BL_CLR_MEM", c); + break; + case G_BL_CLR_BL: + n += gfxd_printf("GBL_c%i(G_BL_CLR_BL", c); + break; + case G_BL_CLR_FOG: + n += gfxd_printf("GBL_c%i(G_BL_CLR_FOG", c); + break; + } + int ba = (arg >> 26) & 0b11; + switch (ba) + { + case G_BL_A_IN: + n += gfxd_puts(", G_BL_A_IN"); + break; + case G_BL_A_FOG: + n += gfxd_puts(", G_BL_A_FOG"); + break; + case G_BL_A_SHADE: + n += gfxd_puts(", G_BL_A_SHADE"); + break; + case G_BL_0: + n += gfxd_puts(", G_BL_0"); + break; + } + int bm = (arg >> 22) & 0b11; + switch (bm) + { + case G_BL_CLR_IN: + n += gfxd_puts(", G_BL_CLR_IN"); + break; + case G_BL_CLR_MEM: + n += gfxd_puts(", G_BL_CLR_MEM"); + break; + case G_BL_CLR_BL: + n += gfxd_puts(", G_BL_CLR_BL"); + break; + case G_BL_CLR_FOG: + n += gfxd_puts(", G_BL_CLR_FOG"); + break; + } + int bb = (arg >> 18) & 0b11; + switch (bb) + { + case G_BL_1MA: + n += gfxd_puts(", G_BL_1MA)"); + break; + case G_BL_A_MEM: + n += gfxd_puts(", G_BL_A_MEM)"); + break; + case G_BL_1: + n += gfxd_puts(", G_BL_1)"); + break; + case G_BL_0: + n += gfxd_puts(", G_BL_0)"); + break; + } + return n; +} + +struct rm_preset +{ + uint32_t rm; + const char * name; +}; + +static const struct rm_preset rm_presets[] = +{ + {G_RM_OPA_SURF, "G_RM_OPA_SURF"}, + {G_RM_OPA_SURF2, "G_RM_OPA_SURF2"}, + {G_RM_AA_OPA_SURF, "G_RM_AA_OPA_SURF"}, + {G_RM_AA_OPA_SURF2, "G_RM_AA_OPA_SURF2"}, + {G_RM_RA_OPA_SURF, "G_RM_RA_OPA_SURF"}, + {G_RM_RA_OPA_SURF2, "G_RM_RA_OPA_SURF2"}, + {G_RM_ZB_OPA_SURF, "G_RM_ZB_OPA_SURF"}, + {G_RM_ZB_OPA_SURF2, "G_RM_ZB_OPA_SURF2"}, + {G_RM_AA_ZB_OPA_SURF, "G_RM_AA_ZB_OPA_SURF"}, + {G_RM_AA_ZB_OPA_SURF2, "G_RM_AA_ZB_OPA_SURF2"}, + {G_RM_RA_ZB_OPA_SURF, "G_RM_RA_ZB_OPA_SURF"}, + {G_RM_RA_ZB_OPA_SURF2, "G_RM_RA_ZB_OPA_SURF2"}, + {G_RM_XLU_SURF, "G_RM_XLU_SURF"}, + {G_RM_XLU_SURF2, "G_RM_XLU_SURF2"}, + {G_RM_AA_XLU_SURF, "G_RM_AA_XLU_SURF"}, + {G_RM_AA_XLU_SURF2, "G_RM_AA_XLU_SURF2"}, + {G_RM_ZB_XLU_SURF, "G_RM_ZB_XLU_SURF"}, + {G_RM_ZB_XLU_SURF2, "G_RM_ZB_XLU_SURF2"}, + {G_RM_AA_ZB_XLU_SURF, "G_RM_AA_ZB_XLU_SURF"}, + {G_RM_AA_ZB_XLU_SURF2, "G_RM_AA_ZB_XLU_SURF2"}, + {G_RM_ZB_OPA_DECAL, "G_RM_ZB_OPA_DECAL"}, + {G_RM_ZB_OPA_DECAL2, "G_RM_ZB_OPA_DECAL2"}, + {G_RM_AA_ZB_OPA_DECAL, "G_RM_AA_ZB_OPA_DECAL"}, + {G_RM_AA_ZB_OPA_DECAL2, "G_RM_AA_ZB_OPA_DECAL2"}, + {G_RM_RA_ZB_OPA_DECAL, "G_RM_RA_ZB_OPA_DECAL"}, + {G_RM_RA_ZB_OPA_DECAL2, "G_RM_RA_ZB_OPA_DECAL2"}, + {G_RM_ZB_XLU_DECAL, "G_RM_ZB_XLU_DECAL"}, + {G_RM_ZB_XLU_DECAL2, "G_RM_ZB_XLU_DECAL2"}, + {G_RM_AA_ZB_XLU_DECAL, "G_RM_AA_ZB_XLU_DECAL"}, + {G_RM_AA_ZB_XLU_DECAL2, "G_RM_AA_ZB_XLU_DECAL2"}, + {G_RM_AA_ZB_OPA_INTER, "G_RM_AA_ZB_OPA_INTER"}, + {G_RM_AA_ZB_OPA_INTER2, "G_RM_AA_ZB_OPA_INTER2"}, + {G_RM_RA_ZB_OPA_INTER, "G_RM_RA_ZB_OPA_INTER"}, + {G_RM_RA_ZB_OPA_INTER2, "G_RM_RA_ZB_OPA_INTER2"}, + {G_RM_AA_ZB_XLU_INTER, "G_RM_AA_ZB_XLU_INTER"}, + {G_RM_AA_ZB_XLU_INTER2, "G_RM_AA_ZB_XLU_INTER2"}, + {G_RM_AA_XLU_LINE, "G_RM_AA_XLU_LINE"}, + {G_RM_AA_XLU_LINE2, "G_RM_AA_XLU_LINE2"}, + {G_RM_AA_ZB_XLU_LINE, "G_RM_AA_ZB_XLU_LINE"}, + {G_RM_AA_ZB_XLU_LINE2, "G_RM_AA_ZB_XLU_LINE2"}, + {G_RM_AA_DEC_LINE, "G_RM_AA_DEC_LINE"}, + {G_RM_AA_DEC_LINE2, "G_RM_AA_DEC_LINE2"}, + {G_RM_AA_ZB_DEC_LINE, "G_RM_AA_ZB_DEC_LINE"}, + {G_RM_AA_ZB_DEC_LINE2, "G_RM_AA_ZB_DEC_LINE2"}, + {G_RM_TEX_EDGE, "G_RM_TEX_EDGE"}, + {G_RM_TEX_EDGE2, "G_RM_TEX_EDGE2"}, + {G_RM_AA_TEX_EDGE, "G_RM_AA_TEX_EDGE"}, + {G_RM_AA_TEX_EDGE2, "G_RM_AA_TEX_EDGE2"}, + {G_RM_AA_ZB_TEX_EDGE, "G_RM_AA_ZB_TEX_EDGE"}, + {G_RM_AA_ZB_TEX_EDGE2, "G_RM_AA_ZB_TEX_EDGE2"}, + {G_RM_AA_ZB_TEX_INTER, "G_RM_AA_ZB_TEX_INTER"}, + {G_RM_AA_ZB_TEX_INTER2, "G_RM_AA_ZB_TEX_INTER2"}, + {G_RM_AA_SUB_SURF, "G_RM_AA_SUB_SURF"}, + {G_RM_AA_SUB_SURF2, "G_RM_AA_SUB_SURF2"}, + {G_RM_AA_ZB_SUB_SURF, "G_RM_AA_ZB_SUB_SURF"}, + {G_RM_AA_ZB_SUB_SURF2, "G_RM_AA_ZB_SUB_SURF2"}, + {G_RM_PCL_SURF, "G_RM_PCL_SURF"}, + {G_RM_PCL_SURF2, "G_RM_PCL_SURF2"}, + {G_RM_AA_PCL_SURF, "G_RM_AA_PCL_SURF"}, + {G_RM_AA_PCL_SURF2, "G_RM_AA_PCL_SURF2"}, + {G_RM_ZB_PCL_SURF, "G_RM_ZB_PCL_SURF"}, + {G_RM_ZB_PCL_SURF2, "G_RM_ZB_PCL_SURF2"}, + {G_RM_AA_ZB_PCL_SURF, "G_RM_AA_ZB_PCL_SURF"}, + {G_RM_AA_ZB_PCL_SURF2, "G_RM_AA_ZB_PCL_SURF2"}, + {G_RM_AA_OPA_TERR, "G_RM_AA_OPA_TERR"}, + {G_RM_AA_OPA_TERR2, "G_RM_AA_OPA_TERR2"}, + {G_RM_AA_ZB_OPA_TERR, "G_RM_AA_ZB_OPA_TERR"}, + {G_RM_AA_ZB_OPA_TERR2, "G_RM_AA_ZB_OPA_TERR2"}, + {G_RM_AA_TEX_TERR, "G_RM_AA_TEX_TERR"}, + {G_RM_AA_TEX_TERR2, "G_RM_AA_TEX_TERR2"}, + {G_RM_AA_ZB_TEX_TERR, "G_RM_AA_ZB_TEX_TERR"}, + {G_RM_AA_ZB_TEX_TERR2, "G_RM_AA_ZB_TEX_TERR2"}, + {G_RM_AA_SUB_TERR, "G_RM_AA_SUB_TERR"}, + {G_RM_AA_SUB_TERR2, "G_RM_AA_SUB_TERR2"}, + {G_RM_AA_ZB_SUB_TERR, "G_RM_AA_ZB_SUB_TERR"}, + {G_RM_AA_ZB_SUB_TERR2, "G_RM_AA_ZB_SUB_TERR2"}, + {G_RM_CLD_SURF, "G_RM_CLD_SURF"}, + {G_RM_CLD_SURF2, "G_RM_CLD_SURF2"}, + {G_RM_ZB_CLD_SURF, "G_RM_ZB_CLD_SURF"}, + {G_RM_ZB_CLD_SURF2, "G_RM_ZB_CLD_SURF2"}, + {G_RM_ZB_OVL_SURF, "G_RM_ZB_OVL_SURF"}, + {G_RM_ZB_OVL_SURF2, "G_RM_ZB_OVL_SURF2"}, + {G_RM_ADD, "G_RM_ADD"}, + {G_RM_ADD2, "G_RM_ADD2"}, + {G_RM_VISCVG, "G_RM_VISCVG"}, + {G_RM_VISCVG2, "G_RM_VISCVG2"}, + {G_RM_OPA_CI, "G_RM_OPA_CI"}, + {G_RM_OPA_CI2, "G_RM_OPA_CI2"}, + {G_RM_RA_SPRITE, "G_RM_RA_SPRITE"}, + {G_RM_RA_SPRITE2, "G_RM_RA_SPRITE2"}, +}; + +static const struct rm_preset bl1_presets[] = +{ + {G_RM_FOG_SHADE_A, "G_RM_FOG_SHADE_A"}, + {G_RM_FOG_PRIM_A, "G_RM_FOG_PRIM_A"}, + {G_RM_PASS, "G_RM_PASS"}, + {G_RM_NOOP, "G_RM_NOOP"}, +}; + +static const struct rm_preset bl2_presets[] = +{ + {G_RM_NOOP2, "G_RM_NOOP2"}, +}; + +UCFUNC int othermodelo_str(uint32_t arg, uint32_t which) +{ + int n = 0; + uint32_t rm_c1_mask = MDMASK_RM_C1; + uint32_t rm_c2_mask = MDMASK_RM_C2; + uint32_t rm_mode_lo = MDMASK_RM_LO; + uint32_t rm_mask = rm_c1_mask | rm_c2_mask | rm_mode_lo; + const struct rm_preset *pre_c1 = NULL; + const struct rm_preset *pre_c2 = NULL; + int n_rm_presets = sizeof(rm_presets) / sizeof(*rm_presets); + for (int i = 0; i < n_rm_presets; i++) + { + const struct rm_preset *pre = &rm_presets[i]; + uint32_t pre_extra = pre->rm & ~rm_mask; + uint32_t rm_c1 = arg & (rm_c1_mask | rm_mode_lo | pre_extra); + if (!pre_c1 && rm_c1 == pre->rm) + pre_c1 = pre; + uint32_t rm_c2 = arg & (rm_c2_mask | rm_mode_lo | pre_extra); + if (!pre_c2 && rm_c2 == pre->rm) + pre_c2 = pre; + } + if (!pre_c1 || !pre_c2 || pre_c1 + 1 != pre_c2) + { + int n_bl1_presets = sizeof(bl1_presets) / sizeof(*bl1_presets); + for (int i = 0; i < n_bl1_presets; i++) + { + const struct rm_preset *pre = &bl1_presets[i]; + uint32_t pre_extra = pre->rm & ~rm_mask; + uint32_t rm_c1 = arg & (rm_c1_mask | pre_extra); + if (rm_c1 == pre->rm) + { + pre_c1 = pre; + break; + } + } + int n_bl2_presets = sizeof(bl2_presets) / sizeof(*bl2_presets); + for (int i = 0; i < n_bl2_presets; i++) + { + const struct rm_preset *pre = &bl2_presets[i]; + uint32_t pre_extra = pre->rm & ~rm_mask; + uint32_t rm_c2 = arg & (rm_c2_mask | pre_extra); + if (rm_c2 == pre->rm) + { + pre_c2 = pre; + break; + } + } + } + uint32_t pre_rm = 0; + if (pre_c1) + pre_rm |= pre_c1->rm; + if (pre_c2) + pre_rm |= pre_c2->rm; + uint32_t ac_mask = MDMASK(ALPHACOMPARE); + if (((arg & ~pre_rm) | which) & ac_mask) + { + uint32_t ac = arg & ac_mask; + switch (ac) + { + case G_AC_NONE: + n += gfxd_puts("G_AC_NONE"); + break; + case G_AC_THRESHOLD: + n += gfxd_puts("G_AC_THRESHOLD"); + break; + case G_AC_DITHER: + n += gfxd_puts("G_AC_DITHER"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, ac); + break; + } + } + uint32_t zs_mask = MDMASK(ZSRCSEL); + if (((arg & ~pre_rm) | which) & zs_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t zs = arg & zs_mask; + switch (zs) + { + case G_ZS_PIXEL: + n += gfxd_puts("G_ZS_PIXEL"); + break; + case G_ZS_PRIM: + n += gfxd_puts("G_ZS_PRIM"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, zs); + break; + } + } + uint32_t rm = arg & (rm_mask | pre_rm); + if (((arg & ~pre_rm) | which) & rm_mode_lo) + { + if (n > 0) + n += gfxd_puts(" | "); + n += rm_mode_str(rm); + } + int c = 0; + if (which & rm_c1_mask) + c |= 1; + if (which & rm_c2_mask) + c |= 2; + if (c & 1 || (c == 0 && arg & rm_c1_mask)) + { + if (n > 0) + n += gfxd_puts(" | "); + if (pre_c1) + n += gfxd_printf("%s", pre_c1->name); + else + n += rm_cbl_str(rm, 1); + } + if (c & 2 || (c == 0 && arg & rm_c2_mask)) + { + if (n > 0) + n += gfxd_puts(" | "); + if (pre_c2) + n += gfxd_printf("%s", pre_c2->name); + else + n += rm_cbl_str(rm, 2); + } + uint32_t unk_mask = ~(rm_mask | ac_mask | zs_mask); + if (arg & unk_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t unk = arg & unk_mask; + n += gfxd_printf("0x%08" PRIX32, unk); + } + return n; +} + +UCFUNC int argfn_ac(const gfxd_value_t *v) +{ + return othermodelo_str(v->u, MDMASK(ALPHACOMPARE)); +} + +UCFUNC int argfn_zs(const gfxd_value_t *v) +{ + return othermodelo_str(v->u, MDMASK(ZSRCSEL)); +} + +UCFUNC int argfn_rm1(const gfxd_value_t *v) +{ + return othermodelo_str(v->u, MDMASK_RM_C1); +} + +UCFUNC int argfn_rm2(const gfxd_value_t *v) +{ + return othermodelo_str(v->u, MDMASK_RM_C2); +} + +UCFUNC int argfn_othermodelo(const gfxd_value_t *v) +{ + uint32_t mask = MDMASK(ALPHACOMPARE) | MDMASK(ZSRCSEL) | MDMASK_RM_C1 | + MDMASK_RM_C2; + return othermodelo_str(v->u, mask); +} + +UCFUNC int argfn_sfthi(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_MDSFT_ALPHADITHER: + return gfxd_puts("G_MDSFT_ALPHADITHER"); + case G_MDSFT_RGBDITHER: + return gfxd_puts("G_MDSFT_RGBDITHER"); + case G_MDSFT_COMBKEY: + return gfxd_puts("G_MDSFT_COMBKEY"); + case G_MDSFT_TEXTCONV: + return gfxd_puts("G_MDSFT_TEXTCONV"); + case G_MDSFT_TEXTFILT: + return gfxd_puts("G_MDSFT_TEXTFILT"); + case G_MDSFT_TEXTLUT: + return gfxd_puts("G_MDSFT_TEXTLUT"); + case G_MDSFT_TEXTLOD: + return gfxd_puts("G_MDSFT_TEXTLOD"); + case G_MDSFT_TEXTDETAIL: + return gfxd_puts("G_MDSFT_TEXTDETAIL"); + case G_MDSFT_TEXTPERSP: + return gfxd_puts("G_MDSFT_TEXTPERSP"); + case G_MDSFT_CYCLETYPE: + return gfxd_puts("G_MDSFT_CYCLETYPE"); + case G_MDSFT_PIPELINE: + return gfxd_puts("G_MDSFT_PIPELINE"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int othermodehi_str(uint32_t arg, uint32_t which) +{ + int n = 0; + uint32_t ad_mask = MDMASK(ALPHADITHER); + if ((arg | which) & ad_mask) + { + uint32_t ad = arg & ad_mask; + switch (ad) + { + case G_AD_PATTERN: + n += gfxd_puts("G_AD_PATTERN"); + break; + case G_AD_NOTPATTERN: + n += gfxd_puts("G_AD_NOTPATTERN"); + break; + case G_AD_NOISE: + n += gfxd_puts("G_AD_NOISE"); + break; + case G_AD_DISABLE: + n += gfxd_puts("G_AD_DISABLE"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, ad); + break; + } + } + uint32_t cd_mask = MDMASK(RGBDITHER); + if ((arg | which) & cd_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t cd = arg & cd_mask; + switch (cd) + { + case G_CD_MAGICSQ: + n += gfxd_puts("G_CD_MAGICSQ"); + break; + case G_CD_BAYER: + n += gfxd_puts("G_CD_BAYER"); + break; + case G_CD_NOISE: + n += gfxd_puts("G_CD_NOISE"); + break; + case G_CD_DISABLE: + n += gfxd_puts("G_CD_DISABLE"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, cd); + break; + } + } + uint32_t ck_mask = MDMASK(COMBKEY); + if ((arg | which) & ck_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t ck = arg & ck_mask; + switch (ck) + { + case G_CK_NONE: + n += gfxd_puts("G_CK_NONE"); + break; + case G_CK_KEY: + n += gfxd_puts("G_CK_KEY"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, ck); + break; + } + } + uint32_t tc_mask = MDMASK(TEXTCONV); + if ((arg | which) & tc_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t tc = arg & tc_mask; + switch (tc) + { + case G_TC_CONV: + n += gfxd_puts("G_TC_CONV"); + break; + case G_TC_FILTCONV: + n += gfxd_puts("G_TC_FILTCONV"); + break; + case G_TC_FILT: + n += gfxd_puts("G_TC_FILT"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, tc); + break; + } + } + uint32_t tf_mask = MDMASK(TEXTFILT); + if ((arg | which) & tf_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t tf = arg & tf_mask; + switch (tf) + { + case G_TF_POINT: + n += gfxd_puts("G_TF_POINT"); + break; + case G_TF_BILERP: + n += gfxd_puts("G_TF_BILERP"); + break; + case G_TF_AVERAGE: + n += gfxd_puts("G_TF_AVERAGE"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, tf); + break; + } + } + uint32_t tt_mask = MDMASK(TEXTLUT); + if ((arg | which) & tt_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t tt = arg & tt_mask; + switch (tt) + { + case G_TT_NONE: + n += gfxd_puts("G_TT_NONE"); + break; + case G_TT_RGBA16: + n += gfxd_puts("G_TT_RGBA16"); + break; + case G_TT_IA16: + n += gfxd_puts("G_TT_IA16"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, tt); + break; + } + } + uint32_t tl_mask = MDMASK(TEXTLOD); + if ((arg | which) & tl_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t tl = arg & tl_mask; + switch (tl) + { + case G_TL_TILE: + n += gfxd_puts("G_TL_TILE"); + break; + case G_TL_LOD: + n += gfxd_puts("G_TL_LOD"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, tl); + break; + } + } + uint32_t td_mask = MDMASK(TEXTDETAIL); + if ((arg | which) & td_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t td = arg & td_mask; + switch (td) + { + case G_TD_CLAMP: + n += gfxd_puts("G_TD_CLAMP"); + break; + case G_TD_SHARPEN: + n += gfxd_puts("G_TD_SHARPEN"); + break; + case G_TD_DETAIL: + n += gfxd_puts("G_TD_DETAIL"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, td); + break; + } + } + uint32_t tp_mask = MDMASK(TEXTPERSP); + if ((arg | which) & tp_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t tp = arg & tp_mask; + switch (tp) + { + case G_TP_NONE: + n += gfxd_puts("G_TP_NONE"); + break; + case G_TP_PERSP: + n += gfxd_puts("G_TP_PERSP"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, tp); + break; + } + } + uint32_t cyc_mask = MDMASK(CYCLETYPE); + if ((arg | which) & cyc_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t cyc = arg & cyc_mask; + switch (cyc) + { + case G_CYC_1CYCLE: + n += gfxd_puts("G_CYC_1CYCLE"); + break; + case G_CYC_2CYCLE: + n += gfxd_puts("G_CYC_2CYCLE"); + break; + case G_CYC_COPY: + n += gfxd_puts("G_CYC_COPY"); + break; + case G_CYC_FILL: + n += gfxd_puts("G_CYC_FILL"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, cyc); + break; + } + } + uint32_t pm_mask = MDMASK(PIPELINE); + if ((arg | which) & pm_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t pm = arg & pm_mask; + switch (pm) + { + case G_PM_NPRIMITIVE: + n += gfxd_puts("G_PM_NPRIMITIVE"); + break; + case G_PM_1PRIMITIVE: + n += gfxd_puts("G_PM_1PRIMITIVE"); + break; + default: + n += gfxd_printf("0x%08" PRIX32, pm); + break; + } + } + uint32_t unk_mask = ~(ad_mask | cd_mask | ck_mask | tc_mask | tf_mask | + tt_mask | tl_mask | td_mask | tp_mask | + cyc_mask | pm_mask); + if (arg & unk_mask) + { + if (n > 0) + n += gfxd_puts(" | "); + uint32_t unk = arg & unk_mask; + n += gfxd_printf("0x%08" PRIX32, unk); + } + return n; +} + +UCFUNC int argfn_ad(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(ALPHADITHER)); +} + +UCFUNC int argfn_cd(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(RGBDITHER)); +} + +UCFUNC int argfn_ck(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(COMBKEY)); +} + +UCFUNC int argfn_tc(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(TEXTCONV)); +} + +UCFUNC int argfn_tf(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(TEXTFILT)); +} + +UCFUNC int argfn_tt(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(TEXTLUT)); +} + +UCFUNC int argfn_tl(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(TEXTLOD)); +} + +UCFUNC int argfn_td(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(TEXTDETAIL)); +} + +UCFUNC int argfn_tp(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(TEXTPERSP)); +} + +UCFUNC int argfn_cyc(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(CYCLETYPE)); +} + +UCFUNC int argfn_pm(const gfxd_value_t *v) +{ + return othermodehi_str(v->u, MDMASK(PIPELINE)); +} + +UCFUNC int argfn_othermodehi(const gfxd_value_t *v) +{ + uint32_t mask = MDMASK(ALPHADITHER) | + MDMASK(RGBDITHER) | + MDMASK(COMBKEY) | + MDMASK(TEXTCONV) | + MDMASK(TEXTFILT) | + MDMASK(TEXTLUT) | + MDMASK(TEXTLOD) | + MDMASK(TEXTDETAIL) | + MDMASK(TEXTPERSP) | + MDMASK(CYCLETYPE) | + MDMASK(PIPELINE); + return othermodehi_str(v->u, mask); +} + +UCFUNC int argfn_cv(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_CV_K0: + return gfxd_puts("G_CV_K0"); + case G_CV_K1: + return gfxd_puts("G_CV_K1"); + case G_CV_K2: + return gfxd_puts("G_CV_K2"); + case G_CV_K3: + return gfxd_puts("G_CV_K3"); + case G_CV_K4: + return gfxd_puts("G_CV_K4"); + case G_CV_K5: + return gfxd_puts("G_CV_K5"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int argfn_ccmuxa(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_CCMUX_COMBINED: + return gfxd_puts("COMBINED"); + case G_CCMUX_TEXEL0: + return gfxd_puts("TEXEL0"); + case G_CCMUX_TEXEL1: + return gfxd_puts("TEXEL1"); + case G_CCMUX_PRIMITIVE: + return gfxd_puts("PRIMITIVE"); + case G_CCMUX_SHADE: + return gfxd_puts("SHADE"); + case G_CCMUX_ENVIRONMENT: + return gfxd_puts("ENVIRONMENT"); + case G_CCMUX_1: + return gfxd_puts("1"); + case G_CCMUX_NOISE: + return gfxd_puts("NOISE"); + default: + return gfxd_puts("0"); + } +} + +UCFUNC int argfn_ccmuxb(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_CCMUX_COMBINED: + return gfxd_puts("COMBINED"); + case G_CCMUX_TEXEL0: + return gfxd_puts("TEXEL0"); + case G_CCMUX_TEXEL1: + return gfxd_puts("TEXEL1"); + case G_CCMUX_PRIMITIVE: + return gfxd_puts("PRIMITIVE"); + case G_CCMUX_SHADE: + return gfxd_puts("SHADE"); + case G_CCMUX_ENVIRONMENT: + return gfxd_puts("ENVIRONMENT"); + case G_CCMUX_CENTER: + return gfxd_puts("CENTER"); + case G_CCMUX_K4: + return gfxd_puts("K4"); + default: + return gfxd_puts("0"); + } +} + +UCFUNC int argfn_ccmuxc(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_CCMUX_COMBINED: + return gfxd_puts("COMBINED"); + case G_CCMUX_TEXEL0: + return gfxd_puts("TEXEL0"); + case G_CCMUX_TEXEL1: + return gfxd_puts("TEXEL1"); + case G_CCMUX_PRIMITIVE: + return gfxd_puts("PRIMITIVE"); + case G_CCMUX_SHADE: + return gfxd_puts("SHADE"); + case G_CCMUX_ENVIRONMENT: + return gfxd_puts("ENVIRONMENT"); + case G_CCMUX_SCALE: + return gfxd_puts("SCALE"); + case G_CCMUX_COMBINED_ALPHA: + return gfxd_puts("COMBINED_ALPHA"); + case G_CCMUX_TEXEL0_ALPHA: + return gfxd_puts("TEXEL0_ALPHA"); + case G_CCMUX_TEXEL1_ALPHA: + return gfxd_puts("TEXEL1_ALPHA"); + case G_CCMUX_PRIMITIVE_ALPHA: + return gfxd_puts("PRIMITIVE_ALPHA"); + case G_CCMUX_SHADE_ALPHA: + return gfxd_puts("SHADE_ALPHA"); + case G_CCMUX_ENV_ALPHA: + return gfxd_puts("ENV_ALPHA"); + case G_CCMUX_LOD_FRACTION: + return gfxd_puts("LOD_FRACTION"); + case G_CCMUX_PRIM_LOD_FRAC: + return gfxd_puts("PRIM_LOD_FRAC"); + case G_CCMUX_K5: + return gfxd_puts("K5"); + default: + return gfxd_puts("0"); + } +} + +UCFUNC int argfn_ccmuxd(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_CCMUX_COMBINED: + return gfxd_puts("COMBINED"); + case G_CCMUX_TEXEL0: + return gfxd_puts("TEXEL0"); + case G_CCMUX_TEXEL1: + return gfxd_puts("TEXEL1"); + case G_CCMUX_PRIMITIVE: + return gfxd_puts("PRIMITIVE"); + case G_CCMUX_SHADE: + return gfxd_puts("SHADE"); + case G_CCMUX_ENVIRONMENT: + return gfxd_puts("ENVIRONMENT"); + case G_CCMUX_1: + return gfxd_puts("1"); + default: + return gfxd_puts("0"); + } +} + +UCFUNC int argfn_acmuxabd(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_ACMUX_COMBINED: + return gfxd_puts("COMBINED"); + case G_ACMUX_TEXEL0: + return gfxd_puts("TEXEL0"); + case G_ACMUX_TEXEL1: + return gfxd_puts("TEXEL1"); + case G_ACMUX_PRIMITIVE: + return gfxd_puts("PRIMITIVE"); + case G_ACMUX_SHADE: + return gfxd_puts("SHADE"); + case G_ACMUX_ENVIRONMENT: + return gfxd_puts("ENVIRONMENT"); + case G_ACMUX_1: + return gfxd_puts("1"); + default: + return gfxd_puts("0"); + } +} + +UCFUNC int argfn_acmuxc(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_ACMUX_LOD_FRACTION: + return gfxd_puts("LOD_FRACTION"); + case G_ACMUX_TEXEL0: + return gfxd_puts("TEXEL0"); + case G_ACMUX_TEXEL1: + return gfxd_puts("TEXEL1"); + case G_ACMUX_PRIMITIVE: + return gfxd_puts("PRIMITIVE"); + case G_ACMUX_SHADE: + return gfxd_puts("SHADE"); + case G_ACMUX_ENVIRONMENT: + return gfxd_puts("ENVIRONMENT"); + case G_ACMUX_PRIM_LOD_FRAC: + return gfxd_puts("PRIM_LOD_FRAC"); + default: + return gfxd_puts("0"); + } +} + +struct cc_mode +{ + int a; + int b; + int c; + int d; + int Aa; + int Ab; + int Ac; + int Ad; +}; + +struct cc_preset +{ + struct cc_mode mode; + const char * name; +}; + +#define CC_(a,b,c,d,Aa,Ab,Ac,Ad) \ + { \ + G_CCMUX_##a, G_CCMUX_##b, \ + G_CCMUX_##c, G_CCMUX_##d, \ + G_ACMUX_##Aa, G_ACMUX_##Ab, \ + G_ACMUX_##Ac, G_ACMUX_##Ad \ + } +#define CC(m) CC_(m) +static const struct cc_preset cc_presets[] = +{ + {CC(G_CC_MODULATEI), "G_CC_MODULATEI"}, + {CC(G_CC_MODULATEIA), "G_CC_MODULATEIA"}, + {CC(G_CC_MODULATEIDECALA), "G_CC_MODULATEIDECALA"}, + {CC(G_CC_MODULATERGB), "G_CC_MODULATERGB"}, + {CC(G_CC_MODULATERGBA), "G_CC_MODULATERGBA"}, + {CC(G_CC_MODULATERGBDECALA), "G_CC_MODULATERGBDECALA"}, + {CC(G_CC_MODULATEI_PRIM), "G_CC_MODULATEI_PRIM"}, + {CC(G_CC_MODULATEIA_PRIM), "G_CC_MODULATEIA_PRIM"}, + {CC(G_CC_MODULATEIDECALA_PRIM), "G_CC_MODULATEIDECALA_PRIM"}, + {CC(G_CC_MODULATERGB_PRIM), "G_CC_MODULATERGB_PRIM"}, + {CC(G_CC_MODULATERGBA_PRIM), "G_CC_MODULATERGBA_PRIM"}, + {CC(G_CC_MODULATERGBDECALA_PRIM), "G_CC_MODULATERGBDECALA_PRIM"}, + {CC(G_CC_DECALRGB), "G_CC_DECALRGB"}, + {CC(G_CC_DECALRGBA), "G_CC_DECALRGBA"}, + {CC(G_CC_BLENDI), "G_CC_BLENDI"}, + {CC(G_CC_BLENDIA), "G_CC_BLENDIA"}, + {CC(G_CC_BLENDIDECALA), "G_CC_BLENDIDECALA"}, + {CC(G_CC_BLENDRGBA), "G_CC_BLENDRGBA"}, + {CC(G_CC_BLENDRGBDECALA), "G_CC_BLENDRGBDECALA"}, + {CC(G_CC_REFLECTRGB), "G_CC_REFLECTRGB"}, + {CC(G_CC_REFLECTRGBDECALA), "G_CC_REFLECTRGBDECALA"}, + {CC(G_CC_HILITERGB), "G_CC_HILITERGB"}, + {CC(G_CC_HILITERGBA), "G_CC_HILITERGBA"}, + {CC(G_CC_HILITERGBDECALA), "G_CC_HILITERGBDECALA"}, + {CC(G_CC_1CYUV2RGB), "G_CC_1CYUV2RGB"}, + {CC(G_CC_PRIMITIVE), "G_CC_PRIMITIVE"}, + {CC(G_CC_SHADE), "G_CC_SHADE"}, + {CC(G_CC_ADDRGB), "G_CC_ADDRGB"}, + {CC(G_CC_ADDRGBDECALA), "G_CC_ADDRGBDECALA"}, + {CC(G_CC_SHADEDECALA), "G_CC_SHADEDECALA"}, + {CC(G_CC_BLENDPE), "G_CC_BLENDPE"}, + {CC(G_CC_BLENDPEDECALA), "G_CC_BLENDPEDECALA"}, + {CC(G_CC_TRILERP), "G_CC_TRILERP"}, + {CC(G_CC_TEMPLERP), "G_CC_TEMPLERP"}, + {CC(G_CC_INTERFERENCE), "G_CC_INTERFERENCE"}, + {CC(_G_CC_BLENDPE), "_G_CC_BLENDPE"}, + {CC(_G_CC_BLENDPEDECALA), "_G_CC_BLENDPEDECALA"}, + {CC(_G_CC_SPARSEST), "_G_CC_SPARSEST"}, + {CC(_G_CC_TWOCOLORTEX), "_G_CC_TWOCOLORTEX"}, + {CC(G_CC_MODULATEI2), "G_CC_MODULATEI2"}, + {CC(G_CC_MODULATEIA2), "G_CC_MODULATEIA2"}, + {CC(G_CC_MODULATERGB2), "G_CC_MODULATERGB2"}, + {CC(G_CC_MODULATERGBA2), "G_CC_MODULATERGBA2"}, + {CC(G_CC_MODULATEI_PRIM2), "G_CC_MODULATEI_PRIM2"}, + {CC(G_CC_MODULATEIA_PRIM2), "G_CC_MODULATEIA_PRIM2"}, + {CC(G_CC_MODULATERGB_PRIM2), "G_CC_MODULATERGB_PRIM2"}, + {CC(G_CC_MODULATERGBA_PRIM2), "G_CC_MODULATERGBA_PRIM2"}, + {CC(G_CC_DECALRGB2), "G_CC_DECALRGB2"}, + {CC(G_CC_BLENDI2), "G_CC_BLENDI2"}, + {CC(G_CC_BLENDIA2), "G_CC_BLENDIA2"}, + {CC(G_CC_HILITERGB2), "G_CC_HILITERGB2"}, + {CC(G_CC_HILITERGBA2), "G_CC_HILITERGBA2"}, + {CC(G_CC_HILITERGBDECALA2), "G_CC_HILITERGBDECALA2"}, + {CC(G_CC_HILITERGBPASSA2), "G_CC_HILITERGBPASSA2"}, + {CC(G_CC_CHROMA_KEY2), "G_CC_CHROMA_KEY2"}, + {CC(G_CC_YUV2RGB), "G_CC_YUV2RGB"}, + {CC(G_CC_PASS2), "G_CC_PASS2"}, +}; +#undef CC_ +#undef CC + +UCFUNC int argfn_ccpre(const gfxd_value_t *v) +{ + return gfxd_printf("%s", cc_presets[v->i].name); +} + +UCFUNC int argfn_sc(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_SC_NON_INTERLACE: + return gfxd_puts("G_SC_NON_INTERLACE"); + case G_SC_EVEN_INTERLACE: + return gfxd_puts("G_SC_EVEN_INTERLACE"); + case G_SC_ODD_INTERLACE: + return gfxd_puts("G_SC_ODD_INTERLACE"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int argfn_bz(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_BZ_PERSP: + return gfxd_puts("G_BZ_PERSP"); + default: + return gfxd_puts("G_BZ_ORTHO"); + } +} +#endif + +UCFUNC int argfn_dlf(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_DL_PUSH: + return gfxd_puts("G_DL_PUSH"); + case G_DL_NOPUSH: + return gfxd_puts("G_DL_NOPUSH"); + default: + return gfxd_printf("%" PRIi32, v->i);; + } +} + +UCFUNC int argfn_mp(const gfxd_value_t *v) +{ + int n = 0; + if (v->u & G_MTX_PUSH) + n += gfxd_puts("G_MTX_PUSH"); + else + n += gfxd_puts("G_MTX_NOPUSH"); + if (v->u & G_MTX_LOAD) + n += gfxd_puts(" | G_MTX_LOAD"); + else + n += gfxd_puts(" | G_MTX_MUL"); + if (v->u & G_MTX_PROJECTION) + n += gfxd_puts(" | G_MTX_PROJECTION"); + else + n += gfxd_puts(" | G_MTX_MODELVIEW"); + for (int i = 3; i < 8; i++) + if (v->u & (1 << i)) + n += gfxd_printf(" | 0x%02x", 1 << i); + return n; +} + +UCFUNC int argfn_ms(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_MTX_MODELVIEW: + return gfxd_puts("G_MTX_MODELVIEW"); + case G_MTX_PROJECTION: + return gfxd_puts("G_MTX_PROJECTION"); + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int argfn_mw(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_MW_MATRIX: + return gfxd_puts("G_MW_MATRIX"); + case G_MW_NUMLIGHT: + return gfxd_puts("G_MW_NUMLIGHT"); + case G_MW_CLIP: + return gfxd_puts("G_MW_CLIP"); + case G_MW_SEGMENT: + return gfxd_puts("G_MW_SEGMENT"); + case G_MW_FOG: + return gfxd_puts("G_MW_FOG"); + case G_MW_LIGHTCOL: + return gfxd_puts("G_MW_LIGHTCOL"); + case G_MW_PERSPNORM: + return gfxd_puts("G_MW_PERSPNORM"); +#if defined(F3D_GBI) || defined(F3DEX_GBI) + case G_MW_POINTS: + return gfxd_puts("G_MW_POINTS"); +#endif +#if defined(F3DEX_GBI_2) + case G_MW_FORCEMTX: + return gfxd_puts("G_MW_FORCEMTX"); +#endif + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int argfn_mwo_clip(const gfxd_value_t *v) +{ + switch (v->u) + { + case G_MWO_CLIP_RNX: + return gfxd_puts("G_MWO_CLIP_RNX"); + case G_MWO_CLIP_RNY: + return gfxd_puts("G_MWO_CLIP_RNY"); + case G_MWO_CLIP_RPX: + return gfxd_puts("G_MWO_CLIP_RPX"); + case G_MWO_CLIP_RPY: + return gfxd_puts("G_MWO_CLIP_RPY"); + default: + return gfxd_printf("0x%04" PRIX32, v->u); + } +} + +UCFUNC int argfn_mwo_lightcol(const gfxd_value_t *v) +{ + switch (v->u) + { + case G_MWO_aLIGHT_1: + return gfxd_puts("G_MWO_aLIGHT_1"); + case G_MWO_bLIGHT_1: + return gfxd_puts("G_MWO_bLIGHT_1"); + case G_MWO_aLIGHT_2: + return gfxd_puts("G_MWO_aLIGHT_2"); + case G_MWO_bLIGHT_2: + return gfxd_puts("G_MWO_bLIGHT_2"); + case G_MWO_aLIGHT_3: + return gfxd_puts("G_MWO_aLIGHT_3"); + case G_MWO_bLIGHT_3: + return gfxd_puts("G_MWO_bLIGHT_3"); + case G_MWO_aLIGHT_4: + return gfxd_puts("G_MWO_aLIGHT_4"); + case G_MWO_bLIGHT_4: + return gfxd_puts("G_MWO_bLIGHT_4"); + case G_MWO_aLIGHT_5: + return gfxd_puts("G_MWO_aLIGHT_5"); + case G_MWO_bLIGHT_5: + return gfxd_puts("G_MWO_bLIGHT_5"); + case G_MWO_aLIGHT_6: + return gfxd_puts("G_MWO_aLIGHT_6"); + case G_MWO_bLIGHT_6: + return gfxd_puts("G_MWO_bLIGHT_6"); + case G_MWO_aLIGHT_7: + return gfxd_puts("G_MWO_aLIGHT_7"); + case G_MWO_bLIGHT_7: + return gfxd_puts("G_MWO_bLIGHT_7"); + case G_MWO_aLIGHT_8: + return gfxd_puts("G_MWO_aLIGHT_8"); + case G_MWO_bLIGHT_8: + return gfxd_puts("G_MWO_bLIGHT_8"); + default: + return gfxd_printf("0x%04" PRIX32, v->u); + } +} + +UCFUNC int argfn_lightnum(const gfxd_value_t *v) +{ + return gfxd_printf("LIGHT_%" PRIi32, v->i); +} + +UCFUNC int argfn_lightsn(const gfxd_value_t *v) +{ + return gfxd_printf("*(Lightsn *)0x%08" PRIX32, v->u); +} + +UCFUNC int argfn_mwo_matrix(const gfxd_value_t *v) +{ + switch (v->u) + { + case G_MWO_MATRIX_XX_XY_I: + return gfxd_puts("G_MWO_MATRIX_XX_XY_I"); + case G_MWO_MATRIX_XZ_XW_I: + return gfxd_puts("G_MWO_MATRIX_XZ_XW_I"); + case G_MWO_MATRIX_YX_YY_I: + return gfxd_puts("G_MWO_MATRIX_YX_YY_I"); + case G_MWO_MATRIX_YZ_YW_I: + return gfxd_puts("G_MWO_MATRIX_YZ_YW_I"); + case G_MWO_MATRIX_ZX_ZY_I: + return gfxd_puts("G_MWO_MATRIX_ZX_ZY_I"); + case G_MWO_MATRIX_ZZ_ZW_I: + return gfxd_puts("G_MWO_MATRIX_ZZ_ZW_I"); + case G_MWO_MATRIX_WX_WY_I: + return gfxd_puts("G_MWO_MATRIX_WX_WY_I"); + case G_MWO_MATRIX_WZ_WW_I: + return gfxd_puts("G_MWO_MATRIX_WZ_WW_I"); + case G_MWO_MATRIX_XX_XY_F: + return gfxd_puts("G_MWO_MATRIX_XX_XY_F"); + case G_MWO_MATRIX_XZ_XW_F: + return gfxd_puts("G_MWO_MATRIX_XZ_XW_F"); + case G_MWO_MATRIX_YX_YY_F: + return gfxd_puts("G_MWO_MATRIX_YX_YY_F"); + case G_MWO_MATRIX_YZ_YW_F: + return gfxd_puts("G_MWO_MATRIX_YZ_YW_F"); + case G_MWO_MATRIX_ZX_ZY_F: + return gfxd_puts("G_MWO_MATRIX_ZX_ZY_F"); + case G_MWO_MATRIX_ZZ_ZW_F: + return gfxd_puts("G_MWO_MATRIX_ZZ_ZW_F"); + case G_MWO_MATRIX_WX_WY_F: + return gfxd_puts("G_MWO_MATRIX_WX_WY_F"); + case G_MWO_MATRIX_WZ_WW_F: + return gfxd_puts("G_MWO_MATRIX_WZ_WW_F"); + default: + return gfxd_printf("0x%04" PRIX32, v->u); + } +} + +UCFUNC int argfn_mwo_point(const gfxd_value_t *v) +{ + switch (v->u) + { + case G_MWO_POINT_RGBA: + return gfxd_puts("G_MWO_POINT_RGBA"); + case G_MWO_POINT_ST: + return gfxd_puts("G_MWO_POINT_ST"); + case G_MWO_POINT_XYSCREEN: + return gfxd_puts("G_MWO_POINT_XYSCREEN"); + case G_MWO_POINT_ZSCREEN: + return gfxd_puts("G_MWO_POINT_ZSCREEN"); + default: + return gfxd_printf("0x%04" PRIX32, v->u); + } +} + +UCFUNC int argfn_mv(const gfxd_value_t *v) +{ + switch (v->i) + { + case G_MV_VIEWPORT: + return gfxd_puts("G_MV_VIEWPORT"); +#if defined(F3D_GBI) || defined(F3DEX_GBI) + case G_MV_LOOKATY: + return gfxd_puts("G_MV_LOOKATY"); + case G_MV_LOOKATX: + return gfxd_puts("G_MV_LOOKATX"); + case G_MV_L0: + return gfxd_puts("G_MV_L0"); + case G_MV_L1: + return gfxd_puts("G_MV_L1"); + case G_MV_L2: + return gfxd_puts("G_MV_L2"); + case G_MV_L3: + return gfxd_puts("G_MV_L3"); + case G_MV_L4: + return gfxd_puts("G_MV_L4"); + case G_MV_L5: + return gfxd_puts("G_MV_L5"); + case G_MV_L6: + return gfxd_puts("G_MV_L6"); + case G_MV_L7: + return gfxd_puts("G_MV_L7"); + case G_MV_TXTATT: + return gfxd_puts("G_MV_TXTATT"); + case G_MV_MATRIX_2: + return gfxd_puts("G_MV_MATRIX_2"); + case G_MV_MATRIX_3: + return gfxd_puts("G_MV_MATRIX_3"); + case G_MV_MATRIX_4: + return gfxd_puts("G_MV_MATRIX_4"); + case G_MV_MATRIX_1: + return gfxd_puts("G_MV_MATRIX_1"); +#elif defined(F3DEX_GBI_2) + case G_MV_MMTX: + return gfxd_puts("G_MV_MMTX"); + case G_MV_PMTX: + return gfxd_puts("G_MV_PMTX"); + case G_MV_LIGHT: + return gfxd_puts("G_MV_LIGHT"); + case G_MV_POINT: + return gfxd_puts("G_MV_POINT"); + case G_MV_MATRIX: + return gfxd_puts("G_MV_MATRIX"); +#endif + default: + return gfxd_printf("%" PRIi32, v->i); + } +} + +UCFUNC int argfn_cr(const gfxd_value_t *v) +{ + switch (v->u) + { + case FRUSTRATIO_1: + return gfxd_puts("FRUSTRATIO_1"); + case FRUSTRATIO_2: + return gfxd_puts("FRUSTRATIO_2"); + case FRUSTRATIO_3: + return gfxd_puts("FRUSTRATIO_3"); + case FRUSTRATIO_4: + return gfxd_puts("FRUSTRATIO_4"); + case FRUSTRATIO_5: + return gfxd_puts("FRUSTRATIO_5"); + case FRUSTRATIO_6: + return gfxd_puts("FRUSTRATIO_6"); + default: + return gfxd_printf("%" PRIu32, v->u); + } +} diff --git a/ZAPDTR/lib/libgfxd/uc_argtbl.c b/ZAPDTR/lib/libgfxd/uc_argtbl.c new file mode 100644 index 000000000..22a0b6500 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_argtbl.c @@ -0,0 +1,485 @@ +static const gfxd_arg_type_t arg_tbl[] = +{ + [gfxd_Word] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Opcode] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_opc, + }, + [gfxd_Coordi] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_u, + }, + [gfxd_Coordq] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_qu102, + }, + [gfxd_Pal] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Tlut] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Timg] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Tmem] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x16, + }, + [gfxd_Tile] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_tile, + }, + [gfxd_Fmt] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_fmt, + }, + [gfxd_Siz] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_siz, + }, + [gfxd_Dim] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Cm] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_cm, + }, + [gfxd_Tm] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_tm, + }, + [gfxd_Ts] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_ts, + }, + [gfxd_Dxt] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_u, + }, + [gfxd_Tag] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Pm] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_pm, + }, + [gfxd_Colorpart] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_color, + }, + [gfxd_Color] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Lodfrac] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_qu08, + }, + [gfxd_Cimg] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Zimg] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Ac] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_ac, + }, + [gfxd_Ad] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_ad, + }, + [gfxd_Cd] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_cd, + }, + [gfxd_Ccpre] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_ccpre, + }, + [gfxd_Ccmuxa] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_ccmuxa, + }, + [gfxd_Ccmuxb] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_ccmuxb, + }, + [gfxd_Ccmuxc] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_ccmuxc, + }, + [gfxd_Ccmuxd] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_ccmuxd, + }, + [gfxd_Acmuxabd] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_acmuxabd, + }, + [gfxd_Acmuxc] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_acmuxc, + }, + [gfxd_Cv] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_cv, + }, + [gfxd_Tc] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_tc, + }, + [gfxd_Cyc] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_cyc, + }, + [gfxd_Zs] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_zs, + }, + [gfxd_Ck] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_ck, + }, + [gfxd_Keyscale] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_color, + }, + [gfxd_Keywidth] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_qs48, + }, + [gfxd_Zi] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Rm1] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_rm1, + }, + [gfxd_Rm2] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_rm2, + }, + [gfxd_Sc] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_sc, + }, + [gfxd_Td] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_td, + }, + [gfxd_Tf] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_tf, + }, + [gfxd_Tl] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_tl, + }, + [gfxd_Tt] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_tt, + }, + [gfxd_Tp] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_tp, + }, + [gfxd_Line] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Vtx] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Vtxflag] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Dl] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + [gfxd_Zraw] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_qs1616, + }, +#endif + [gfxd_Dlflag] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_dlf, + }, + [gfxd_Cr] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_cr, + }, + [gfxd_Num] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Fogz] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Fogp] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Mtxptr] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Gm] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_gm, + }, + [gfxd_Mwo_matrix] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_mwo_matrix, + }, + [gfxd_Linewd] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Uctext] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Ucdata] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Size] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x16, + }, + [gfxd_Lookatptr] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Mtxparam] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_mp, + }, + [gfxd_Mtxstack] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_ms, + }, + [gfxd_Mwo_point] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_mwo_point, + }, + [gfxd_Wscale] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_qu016, + }, + [gfxd_Seg] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x8, + }, + [gfxd_Segptr] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Lightsn] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_lightsn, + }, + [gfxd_Numlights] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, + [gfxd_Lightnum] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_lightnum, + }, + [gfxd_Lightptr] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Tcscale] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_qu016, + }, + [gfxd_Switch] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_switch, + }, + [gfxd_St] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_qs105, + }, + [gfxd_Stdelta] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_qs510, + }, + [gfxd_Vtxptr] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Vpptr] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Dram] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x32, + }, + [gfxd_Sftlo] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_sftlo, + }, + [gfxd_Othermodelo] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_othermodelo, + }, + [gfxd_Sfthi] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_sfthi, + }, + [gfxd_Othermodehi] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_othermodehi, + }, + [gfxd_Mw] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_mw, + }, + [gfxd_Mwo] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x16, + }, + [gfxd_Mwo_clip] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_mwo_clip, + }, + [gfxd_Mwo_lightcol] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_mwo_lightcol, + }, + [gfxd_Mv] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_mv, + }, + [gfxd_Mvo] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x16, + }, + [gfxd_Dmem] = + { + .fmt = gfxd_argfmt_u, + .fn = argfn_x16, + }, + [gfxd_Dmaflag] = + { + .fmt = gfxd_argfmt_i, + .fn = argfn_i, + }, +}; diff --git a/ZAPDTR/lib/libgfxd/uc_f3d.c b/ZAPDTR/lib/libgfxd/uc_f3d.c new file mode 100644 index 000000000..946551510 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_f3d.c @@ -0,0 +1,4 @@ +#define uc_name gfxd_f3d +#define F3D_GBI + +#include "uc.c" diff --git a/ZAPDTR/lib/libgfxd/uc_f3db.c b/ZAPDTR/lib/libgfxd/uc_f3db.c new file mode 100644 index 000000000..cd4990412 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_f3db.c @@ -0,0 +1,5 @@ +#define uc_name gfxd_f3db +#define F3D_GBI +#define F3D_BETA + +#include "uc.c" diff --git a/ZAPDTR/lib/libgfxd/uc_f3dex.c b/ZAPDTR/lib/libgfxd/uc_f3dex.c new file mode 100644 index 000000000..2ec70d8ab --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_f3dex.c @@ -0,0 +1,4 @@ +#define uc_name gfxd_f3dex +#define F3DEX_GBI + +#include "uc.c" diff --git a/ZAPDTR/lib/libgfxd/uc_f3dex2.c b/ZAPDTR/lib/libgfxd/uc_f3dex2.c new file mode 100644 index 000000000..9a19c9903 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_f3dex2.c @@ -0,0 +1,4 @@ +#define uc_name gfxd_f3dex2 +#define F3DEX_GBI_2 + +#include "uc.c" diff --git a/ZAPDTR/lib/libgfxd/uc_f3dexb.c b/ZAPDTR/lib/libgfxd/uc_f3dexb.c new file mode 100644 index 000000000..2b5c30a4a --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_f3dexb.c @@ -0,0 +1,5 @@ +#define uc_name gfxd_f3dexb +#define F3DEX_GBI +#define F3D_BETA + +#include "uc.c" diff --git a/ZAPDTR/lib/libgfxd/uc_macrofn.c b/ZAPDTR/lib/libgfxd/uc_macrofn.c new file mode 100644 index 000000000..46d4b4ee8 --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_macrofn.c @@ -0,0 +1,2507 @@ +static inline uint32_t getfield(uint32_t w, int n, int s) +{ + return (w >> s) & (((uint32_t)1 << n) - 1); +} + +static inline int32_t argvi(gfxd_macro_t *m, int idx) +{ + return m->arg[idx].value.i; +} + +static inline uint32_t argvu(gfxd_macro_t *m, int idx) +{ + return m->arg[idx].value.u; +} + +static inline float argvf(gfxd_macro_t *m, int idx) +{ + return m->arg[idx].value.f; +} + +static inline void argi(gfxd_macro_t *m, int idx, const char *name, + int32_t value, int type) +{ + m->arg[idx].type = type; + m->arg[idx].name = name; + m->arg[idx].value.i = value; + m->arg[idx].bad = 0; +} + +static inline void argu(gfxd_macro_t *m, int idx, const char *name, + uint32_t value, int type) +{ + m->arg[idx].type = type; + m->arg[idx].name = name; + m->arg[idx].value.u = value; + m->arg[idx].bad = 0; +} + +static inline void argf(gfxd_macro_t *m, int idx, const char *name, + float value, int type) +{ + m->arg[idx].type = type; + m->arg[idx].name = name; + m->arg[idx].value.f = value; + m->arg[idx].bad = 0; +} + +static inline void badarg(gfxd_macro_t *m, int idx) +{ + m->arg[idx].bad = 1; +} + +UCFUNC int32_t sx(uint32_t n, int bits) +{ + int32_t smin = (int32_t)1 << (bits - 1); + int32_t smax = (int32_t)1 << bits; + int32_t i = n; + if (i < smin) + return i; + else + return i - smax; +} + +UCFUNC int d_Invalid(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_Invalid; + argu(m, 0, "hi", hi, gfxd_Word); + argu(m, 1, "lo", lo, gfxd_Word); + return -1; +} + +UCFUNC int d_DPFillRectangle(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPFillRectangle; + argu(m, 0, "ulx", getfield(lo, 10, 14), gfxd_Coordi); + argu(m, 1, "uly", getfield(lo, 10, 2), gfxd_Coordi); + argu(m, 2, "lrx", getfield(hi, 10, 14), gfxd_Coordi); + argu(m, 3, "lry", getfield(hi, 10, 2), gfxd_Coordi); + return 0; +} + +UCFUNC int d_DPFullSync(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPFullSync; + return 0; +} + +UCFUNC int d_DPLoadSync(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPLoadSync; + return 0; +} + +UCFUNC int d_DPTileSync(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPTileSync; + return 0; +} + +UCFUNC int d_DPPipeSync(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPPipeSync; + return 0; +} + +UCFUNC int c_DPLoadTLUT_pal16(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 6) + return -1; + if (m[0].id != gfxd_DPSetTextureImage + || argvi(&m[0], 0) != G_IM_FMT_RGBA + || argvi(&m[0], 1) != G_IM_SIZ_16b + || argvi(&m[0], 2) != 1) + { + return -1; + } + uint32_t dram = argvu(&m[0], 3); + if (m[1].id != gfxd_DPTileSync) + return -1; + if (m[2].id != gfxd_DPSetTile + || argvi(&m[2], 0) != 0 + || argvi(&m[2], 1) != 0 + || argvi(&m[2], 2) != 0 + || argvu(&m[2], 3) < 0x100 + || argvu(&m[2], 3) % 0x10 != 0 + || argvi(&m[2], 4) != G_TX_LOADTILE + || argvi(&m[2], 5) != 0 + || argvu(&m[2], 6) != 0 + || argvi(&m[2], 7) != 0 + || argvi(&m[2], 8) != 0 + || argvu(&m[2], 9) != 0 + || argvi(&m[2], 10) != 0 + || argvi(&m[2], 11) != 0) + { + return -1; + } + int pal = (argvu(&m[2], 3) - 0x100) / 0x10; + if (m[3].id != gfxd_DPLoadSync) + return -1; + if (m[4].id != gfxd_DPLoadTLUTCmd + || argvi(&m[4], 0) != G_TX_LOADTILE + || argvi(&m[4], 1) != 15) + { + return -1; + } + if (m[5].id != gfxd_DPPipeSync) + return -1; + m->id = gfxd_DPLoadTLUT_pal16; + argi(m, 0, "pal", pal, gfxd_Pal); + argu(m, 1, "dram", dram, gfxd_Tlut); + return 0; +} + +UCFUNC int c_DPLoadTLUT_pal256(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 6) + return -1; + if (m[0].id != gfxd_DPSetTextureImage + || argvi(&m[0], 0) != G_IM_FMT_RGBA + || argvi(&m[0], 1) != G_IM_SIZ_16b + || argvi(&m[0], 2) != 1) + { + return -1; + } + uint32_t dram = argvu(&m[0], 3); + if (m[1].id != gfxd_DPTileSync) + return -1; + if (m[2].id != gfxd_DPSetTile + || argvi(&m[2], 0) != 0 + || argvi(&m[2], 1) != 0 + || argvi(&m[2], 2) != 0 + || argvu(&m[2], 3) != 0x100 + || argvi(&m[2], 4) != G_TX_LOADTILE + || argvi(&m[2], 5) != 0 + || argvu(&m[2], 6) != 0 + || argvi(&m[2], 7) != 0 + || argvi(&m[2], 8) != 0 + || argvu(&m[2], 9) != 0 + || argvi(&m[2], 10) != 0 + || argvi(&m[2], 11) != 0) + { + return -1; + } + if (m[3].id != gfxd_DPLoadSync) + return -1; + if (m[4].id != gfxd_DPLoadTLUTCmd + || argvi(&m[4], 0) != G_TX_LOADTILE + || argvi(&m[4], 1) != 255) + { + return -1; + } + if (m[5].id != gfxd_DPPipeSync) + return -1; + m->id = gfxd_DPLoadTLUT_pal256; + argu(m, 0, "dram", dram, gfxd_Tlut); + return 0; +} + +UCFUNC int c_ltb(gfxd_macro_t *m, int n_macro, int id, int mdxt, int mtmem, + int mrt, int myuv, int m4b) +{ + if (n_macro < 7) + return -1; + if (m[0].id != gfxd_DPSetTextureImage || argvi(&m[0], 2) != 1) + return -1; + g_ifmt_t fmt = argvi(&m[0], 0); + g_isiz_t ldsiz = argvi(&m[0], 1); + uint32_t timg = argvu(&m[0], 3); + if (myuv && fmt != G_IM_FMT_YUV) + return -1; + if (m[1].id != gfxd_DPSetTile + || argvi(&m[1], 0) != fmt + || argvi(&m[1], 1) != ldsiz + || argvi(&m[1], 2) != 0 + || argvi(&m[1], 4) != G_TX_LOADTILE + || argvi(&m[1], 5) != 0) + { + return -1; + } + uint32_t tmem = argvu(&m[1], 3); + unsigned cms = argvu(&m[1], 9); + unsigned cmt = argvu(&m[1], 6); + int masks = argvi(&m[1], 10); + int maskt = argvi(&m[1], 7); + int shifts = argvi(&m[1], 11); + int shiftt = argvi(&m[1], 8); + if (m[2].id != gfxd_DPLoadSync) + return -1; + if (m[3].id != gfxd_DPLoadBlock + || argvi(&m[3], 0) != G_TX_LOADTILE + || argvu(&m[3], 1) != 0 + || argvu(&m[3], 2) != 0) + { + return -1; + } + qu102_t ldlrs = argvu(&m[3], 3); + unsigned lddxt = argvu(&m[3], 4); + if (m[4].id != gfxd_DPPipeSync) + return -1; + if (m[5].id != gfxd_DPSetTile + || argvi(&m[5], 0) != fmt + || G_SIZ_LDSIZ(argvi(&m[5], 1)) != ldsiz + || argvu(&m[5], 3) != tmem + || argvu(&m[5], 6) != cmt + || argvi(&m[5], 7) != maskt + || argvi(&m[5], 8) != shiftt + || argvu(&m[5], 9) != cms + || argvi(&m[5], 10) != masks + || argvi(&m[5], 11) != shifts) + { + return -1; + } + int siz = argvi(&m[5], 1); + int rdline = argvi(&m[5], 2); + int rt = argvi(&m[5], 4); + int pal = argvi(&m[5], 5); + if (m4b && siz != G_IM_SIZ_4b) + return -1; + if (!(mrt && rt != G_TX_RENDERTILE && tmem == 0) + && (tmem != 0) != mtmem) + { + return -1; + } + if ((rt != G_TX_RENDERTILE) != mrt) + return -1; + if (m[6].id != gfxd_DPSetTileSize + || argvi(&m[6], 0) != rt + || argvu(&m[6], 1) != 0 + || argvu(&m[6], 2) != 0 + || (argvu(&m[6], 3) & 3) + || (argvu(&m[6], 4) & 3)) + { + return -1; + } + int width = (argvu(&m[6], 3) >> 2) + 1; + int height = (argvu(&m[6], 4) >> 2) + 1; + unsigned lrs = G_LDBLK_TXL(G_LTB_LRS(width, height, siz)); + unsigned dxt = 0; + if (!mdxt) + dxt = G_DXT(siz, width); + int line; + if (myuv) + line = (width + 7) / 8; + else + line = (width * G_SIZ_LDBITS(siz) + 63) / 64; + if (ldlrs != lrs || lddxt != dxt || rdline != line) + return -1; + m->id = id; + int i = 0; + argu(m, i++, "timg", timg, gfxd_Timg); + if (mtmem) + argu(m, i++, "tmem", tmem, gfxd_Tmem); + if (mrt) + argi(m, i++, "rtile", rt, gfxd_Tile); + argi(m, i++, "fmt", fmt, gfxd_Fmt); + if (!m4b) + argi(m, i++, "siz", siz, gfxd_Siz); + argi(m, i++, "width", width, gfxd_Dim); + argi(m, i++, "height", height, gfxd_Dim); + argi(m, i++, "pal", pal, gfxd_Pal); + argu(m, i++, "cms", cms, gfxd_Cm); + argu(m, i++, "cmt", cmt, gfxd_Cm); + argi(m, i++, "masks", masks, gfxd_Tm); + argi(m, i++, "maskt", maskt, gfxd_Tm); + argi(m, i++, "shifts", shifts, gfxd_Ts); + argi(m, i++, "shiftt", shiftt, gfxd_Ts); + return 0; +} + +UCFUNC int c_DPLoadMultiBlockYuvS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadMultiBlockYuvS, 1, 1, 1, 1, 0); +} + +UCFUNC int c_DPLoadMultiBlockYuv(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadMultiBlockYuv, 0, 1, 1, 1, 0); +} + +UCFUNC int c_DPLoadMultiBlock_4bS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadMultiBlock_4bS, 1, 1, 1, 0, 1); +} + +UCFUNC int c_DPLoadMultiBlock_4b(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadMultiBlock_4b, 0, 1, 1, 0, 1); +} + +UCFUNC int c_DPLoadMultiBlockS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadMultiBlockS, 1, 1, 1, 0, 0); +} + +UCFUNC int c_DPLoadMultiBlock(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadMultiBlock, 0, 1, 1, 0, 0); +} + +UCFUNC int c__DPLoadTextureBlockYuvS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd__DPLoadTextureBlockYuvS, 1, 1, 0, 1, 0); +} + +UCFUNC int c__DPLoadTextureBlockYuv(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd__DPLoadTextureBlockYuv, 0, 1, 0, 1, 0); +} + +UCFUNC int c__DPLoadTextureBlock_4bS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd__DPLoadTextureBlock_4bS, 1, 1, 0, 0, 1); +} + +UCFUNC int c__DPLoadTextureBlock_4b(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd__DPLoadTextureBlock_4b, 0, 1, 0, 0, 1); +} + +UCFUNC int c__DPLoadTextureBlockS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd__DPLoadTextureBlockS, 1, 1, 0, 0, 0); +} + +UCFUNC int c__DPLoadTextureBlock(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd__DPLoadTextureBlock, 0, 1, 0, 0, 0); +} + +UCFUNC int c_DPLoadTextureBlockYuvS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadTextureBlockYuvS, 1, 0, 0, 1, 0); +} + +UCFUNC int c_DPLoadTextureBlockYuv(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadTextureBlockYuv, 0, 0, 0, 1, 0); +} + +UCFUNC int c_DPLoadTextureBlock_4bS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadTextureBlock_4bS, 1, 0, 0, 0, 1); +} + +UCFUNC int c_DPLoadTextureBlock_4b(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadTextureBlock_4b, 0, 0, 0, 0, 1); +} + +UCFUNC int c_DPLoadTextureBlockS(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadTextureBlockS, 1, 0, 0, 0, 0); +} + +UCFUNC int c_DPLoadTextureBlock(gfxd_macro_t *m, int n_macro) +{ + return c_ltb(m, n_macro, gfxd_DPLoadTextureBlock, 0, 0, 0, 0, 0); +} + +UCFUNC int c_ltt(gfxd_macro_t *m, int n_macro, int id, int mtmem, int mrt, + int myuv, int m4b) +{ + if (n_macro < 7) + return -1; + if (m[0].id != gfxd_DPSetTextureImage) + return -1; + g_ifmt_t fmt = argvi(&m[0], 0); + g_isiz_t ldsiz = argvi(&m[0], 1); + int width = argvi(&m[0], 2); + if (m4b) + { + if (ldsiz != G_IM_SIZ_8b) + return -1; + width *= 2; + } + uint32_t timg = argvu(&m[0], 3); + if (myuv && fmt != G_IM_FMT_YUV) + return -1; + if (m[1].id != gfxd_DPSetTile + || argvi(&m[1], 0) != fmt + || argvi(&m[1], 1) != ldsiz + || argvi(&m[1], 4) != G_TX_LOADTILE + || argvi(&m[1], 5) != 0) + { + return -1; + } + int ldline = argvi(&m[1], 2); + uint32_t tmem = argvu(&m[1], 3); + unsigned cms = argvu(&m[1], 9); + unsigned cmt = argvu(&m[1], 6); + int masks = argvi(&m[1], 10); + int maskt = argvi(&m[1], 7); + int shifts = argvi(&m[1], 11); + int shiftt = argvi(&m[1], 8); + if (m[2].id != gfxd_DPLoadSync) + return -1; + if (m[3].id != gfxd_DPLoadTile + || argvi(&m[3], 0) != G_TX_LOADTILE + || (argvu(&m[3], 1) & 1) + || (argvu(&m[3], 2) & 3) + || (argvu(&m[3], 3) & 1) + || (argvu(&m[3], 4) & 3)) + { + return -1; + } + qu102_t lduls = argvu(&m[3], 1); + qu102_t ldult = argvu(&m[3], 2); + qu102_t ldlrs = argvu(&m[3], 3); + qu102_t ldlrt = argvu(&m[3], 4); + if (m[4].id != gfxd_DPPipeSync) + return -1; + if (m[5].id != gfxd_DPSetTile + || argvi(&m[5], 0) != fmt + || argvi(&m[5], 2) != ldline + || argvu(&m[5], 3) != tmem + || argvu(&m[5], 6) != cmt + || argvi(&m[5], 7) != maskt + || argvi(&m[5], 8) != shiftt + || argvu(&m[5], 9) != cms + || argvi(&m[5], 10) != masks + || argvi(&m[5], 11) != shifts) + { + return -1; + } + int siz = argvi(&m[5], 1); + int rt = argvi(&m[5], 4); + int pal = argvi(&m[5], 5); + if (m4b) + { + if (siz != G_IM_SIZ_4b) + return -1; + } + else if (siz != ldsiz) + return -1; + if (!(mrt && rt != G_TX_RENDERTILE && tmem == 0) + && (tmem != 0) != mtmem) + { + return -1; + } + if ((rt != G_TX_RENDERTILE) != mrt) + return -1; + if (m[6].id != gfxd_DPSetTileSize + || argvi(&m[6], 0) != rt + || (argvu(&m[6], 1) & 3) + || (argvu(&m[6], 2) & 3) + || (argvu(&m[6], 3) & 3) + || (argvu(&m[6], 4) & 3)) + { + return -1; + } + unsigned uls = argvu(&m[6], 1) >> 2; + unsigned ult = argvu(&m[6], 2) >> 2; + unsigned lrs = argvu(&m[6], 3) >> 2; + unsigned lrt = argvu(&m[6], 4) >> 2; + int line; + if (myuv) + line = ((lrs - uls + 1) + 7) / 8; + else if (m4b) + line = ((lrs - uls + 1) / 2 + 7) / 8; + else + line = ((lrs - uls + 1) * G_SIZ_LDBITS(siz) + 63) / 64; + if (m4b) + { + if (lduls != qu102(uls) / 2 || ldlrs != qu102(lrs) / 2) + return -1; + } + else if (lduls != qu102(uls) || ldlrs != qu102(lrs)) + return -1; + if (ldult != qu102(ult) || ldlrt != qu102(lrt) || ldline != line) + return -1; + m->id = id; + int i = 0; + argu(m, i++, "timg", timg, gfxd_Timg); + if (mtmem) + argu(m, i++, "tmem", tmem, gfxd_Tmem); + if (mrt) + argi(m, i++, "rtile", rt, gfxd_Tile); + argi(m, i++, "fmt", fmt, gfxd_Fmt); + if (!m4b) + argi(m, i++, "siz", siz, gfxd_Siz); + argi(m, i++, "width", width, gfxd_Dim); + argi(m, i++, "height", 0, gfxd_Dim); + argu(m, i++, "uls", uls, gfxd_Coordi); + argu(m, i++, "ult", ult, gfxd_Coordi); + argu(m, i++, "lrs", lrs, gfxd_Coordi); + argu(m, i++, "lrt", lrt, gfxd_Coordi); + argi(m, i++, "pal", pal, gfxd_Pal); + argu(m, i++, "cms", cms, gfxd_Cm); + argu(m, i++, "cmt", cmt, gfxd_Cm); + argi(m, i++, "masks", masks, gfxd_Tm); + argi(m, i++, "maskt", maskt, gfxd_Tm); + argi(m, i++, "shifts", shifts, gfxd_Ts); + argi(m, i++, "shiftt", shiftt, gfxd_Ts); + return 0; +} + +UCFUNC int c_DPLoadMultiTileYuv(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd_DPLoadMultiTileYuv, 1, 1, 1, 0); +} + +UCFUNC int c_DPLoadMultiTile_4b(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd_DPLoadMultiTile_4b, 1, 1, 0, 1); +} + +UCFUNC int c_DPLoadMultiTile(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd_DPLoadMultiTile, 1, 1, 0, 0); +} + +UCFUNC int c__DPLoadTextureTileYuv(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd__DPLoadTextureTileYuv, 1, 0, 1, 0); +} + +UCFUNC int c__DPLoadTextureTile_4b(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd__DPLoadTextureTile_4b, 1, 0, 0, 1); +} + +UCFUNC int c__DPLoadTextureTile(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd__DPLoadTextureTile, 1, 0, 0, 0); +} + +UCFUNC int c_DPLoadTextureTileYuv(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd_DPLoadTextureTileYuv, 0, 0, 1, 0); +} + +UCFUNC int c_DPLoadTextureTile_4b(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd_DPLoadTextureTile_4b, 0, 0, 0, 1); +} + +UCFUNC int c_DPLoadTextureTile(gfxd_macro_t *m, int n_macro) +{ + return c_ltt(m, n_macro, gfxd_DPLoadTextureTile, 0, 0, 0, 0); +} + +UCFUNC int d_DPLoadBlock(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPLoadBlock; + argi(m, 0, "tile", getfield(lo, 3, 24), gfxd_Tile); + argu(m, 1, "uls", getfield(hi, 12, 12), gfxd_Coordi); + argu(m, 2, "ult", getfield(hi, 12, 0), gfxd_Coordi); + argu(m, 3, "lrs", getfield(lo, 12, 12), gfxd_Coordi); + argu(m, 4, "dxt", getfield(lo, 12, 0), gfxd_Dxt); + if (argvu(m, 3) > G_TX_LDBLK_MAX_TXL) { + badarg(m, 3); + return -1; + } + else { + return 0; + } +} + +UCFUNC int d_DPNoOp(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPNoOp; + return 0; +} + +UCFUNC int d_DPNoOpTag(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + if (lo == 0) + return d_DPNoOp(m, hi, lo); + else + { + m->id = gfxd_DPNoOpTag; + argu(m, 0, "tag", lo, gfxd_Tag); + return 0; + } +} + +UCFUNC int d_DPPipelineMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPPipelineMode; + argu(m, 0, "mode", lo, gfxd_Pm); + return 0; +} + +UCFUNC int d_DPSetBlendColor(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetBlendColor; + argu(m, 0, "r", getfield(lo, 8, 24), gfxd_Colorpart); + argu(m, 1, "g", getfield(lo, 8, 16), gfxd_Colorpart); + argu(m, 2, "b", getfield(lo, 8, 8), gfxd_Colorpart); + argu(m, 3, "a", getfield(lo, 8, 0), gfxd_Colorpart); + return 0; +} + +UCFUNC int d_DPSetEnvColor(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetEnvColor; + argu(m, 0, "r", getfield(lo, 8, 24), gfxd_Colorpart); + argu(m, 1, "g", getfield(lo, 8, 16), gfxd_Colorpart); + argu(m, 2, "b", getfield(lo, 8, 8), gfxd_Colorpart); + argu(m, 3, "a", getfield(lo, 8, 0), gfxd_Colorpart); + return 0; +} + +UCFUNC int d_DPSetFillColor(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetFillColor; + argu(m, 0, "c", lo, gfxd_Color); + return 0; +} + +UCFUNC int d_DPSetFogColor(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetFogColor; + argu(m, 0, "r", getfield(lo, 8, 24), gfxd_Colorpart); + argu(m, 1, "g", getfield(lo, 8, 16), gfxd_Colorpart); + argu(m, 2, "b", getfield(lo, 8, 8), gfxd_Colorpart); + argu(m, 3, "a", getfield(lo, 8, 0), gfxd_Colorpart); + return 0; +} + +UCFUNC int d_DPSetPrimColor(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetPrimColor; + argu(m, 0, "m", getfield(hi, 8, 8), gfxd_Lodfrac); + argu(m, 1, "l", getfield(hi, 8, 0), gfxd_Lodfrac); + argu(m, 2, "r", getfield(lo, 8, 24), gfxd_Colorpart); + argu(m, 3, "g", getfield(lo, 8, 16), gfxd_Colorpart); + argu(m, 4, "b", getfield(lo, 8, 8), gfxd_Colorpart); + argu(m, 5, "a", getfield(lo, 8, 0), gfxd_Colorpart); + return 0; +} + +UCFUNC int d_DPSetColorImage(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetColorImage; + argi(m, 0, "fmt", getfield(hi, 3, 21), gfxd_Fmt); + argi(m, 1, "siz", getfield(hi, 2, 19), gfxd_Siz); + argi(m, 2, "width", getfield(hi, 12, 0) + 1, gfxd_Dim); + argu(m, 3, "cimg", lo, gfxd_Cimg); + return 0; +} + +UCFUNC int d_DPSetDepthImage(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetDepthImage; + argu(m, 0, "zimg", lo, gfxd_Zimg); + return 0; +} + +UCFUNC int d_DPSetTextureImage(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTextureImage; + argi(m, 0, "fmt", getfield(hi, 3, 21), gfxd_Fmt); + argi(m, 1, "siz", getfield(hi, 2, 19), gfxd_Siz); + argi(m, 2, "width", getfield(hi, 12, 0) + 1, gfxd_Dim); + argu(m, 3, "timg", lo, gfxd_Timg); + return 0; +} + +UCFUNC int d_DPSetAlphaCompare(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetAlphaCompare; + argu(m, 0, "mode", lo, gfxd_Ac); + return 0; +} + +UCFUNC int d_DPSetAlphaDither(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetAlphaDither; + argu(m, 0, "mode", lo, gfxd_Ad); + return 0; +} + +UCFUNC int d_DPSetColorDither(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetColorDither; + argu(m, 0, "mode", lo, gfxd_Cd); + return 0; +} + +UCFUNC void cc_unpack(struct cc_mode *m0, struct cc_mode *m1, uint32_t hi, + uint32_t lo) +{ + m0->a = getfield(hi, 4, 20); + m0->b = getfield(lo, 4, 28); + m0->c = getfield(hi, 5, 15); + m0->d = getfield(lo, 3, 15); + m0->Aa = getfield(hi, 3, 12); + m0->Ab = getfield(lo, 3, 12); + m0->Ac = getfield(hi, 3, 9); + m0->Ad = getfield(lo, 3, 9); + m1->a = getfield(hi, 4, 5); + m1->b = getfield(lo, 4, 24); + m1->c = getfield(hi, 5, 0); + m1->d = getfield(lo, 3, 6); + m1->Aa = getfield(lo, 3, 21); + m1->Ab = getfield(lo, 3, 3); + m1->Ac = getfield(lo, 3, 18); + m1->Ad = getfield(lo, 3, 0); +} + +UCFUNC int cc_lookup(const struct cc_mode *m) +{ + struct cc_mode m_norm = *m; + if (m_norm.a > 0x7) m_norm.a = G_CCMUX_0; + if (m_norm.b > 0x7) m_norm.b = G_CCMUX_0; + if (m_norm.c > 0xF) m_norm.c = G_CCMUX_0; + if (m_norm.d > 0x6) m_norm.d = G_CCMUX_0; + m = &m_norm; + int n_presets = sizeof(cc_presets) / sizeof(*cc_presets); + for (int i = 0; i < n_presets; i++) + { + const struct cc_mode *p = &cc_presets[i].mode; + if (m->a == p->a + && m->b == p->b + && m->c == p->c + && m->d == p->d + && m->Aa == p->Aa + && m->Ab == p->Ab + && m->Ac == p->Ac + && m->Ad == p->Ad) + { + return i; + } + } + return -1; +} + +UCFUNC int d_DPSetCombineMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetCombineMode; + struct cc_mode m0; + struct cc_mode m1; + cc_unpack(&m0, &m1, hi, lo); + int p0 = cc_lookup(&m0); + int p1 = cc_lookup(&m1); + argi(m, 0, "mode1", p0, gfxd_Ccpre); + argi(m, 1, "mode2", p1, gfxd_Ccpre); + int ret = 0; + if (p0 == -1) + { + badarg(m, 0); + ret = -1; + } + if (p1 == -1) + { + badarg(m, 1); + ret = -1; + } + return ret; +} + +UCFUNC int d_DPSetCombineLERP(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + struct cc_mode m0; + struct cc_mode m1; + cc_unpack(&m0, &m1, hi, lo); + int p0 = cc_lookup(&m0); + int p1 = cc_lookup(&m1); + if (p0 != -1 && p1 != -1) + return d_DPSetCombineMode(m, hi, lo); + else + { + m->id = gfxd_DPSetCombineLERP; + argi(m, 0, "a0", m0.a, gfxd_Ccmuxa); + argi(m, 1, "b0", m0.b, gfxd_Ccmuxb); + argi(m, 2, "c0", m0.c, gfxd_Ccmuxc); + argi(m, 3, "d0", m0.d, gfxd_Ccmuxd); + argi(m, 4, "Aa0", m0.Aa, gfxd_Acmuxabd); + argi(m, 5, "Ab0", m0.Ab, gfxd_Acmuxabd); + argi(m, 6, "Ac0", m0.Ac, gfxd_Acmuxc); + argi(m, 7, "Ad0", m0.Ad, gfxd_Acmuxabd); + argi(m, 8, "a1", m1.a, gfxd_Ccmuxa); + argi(m, 9, "b1", m1.b, gfxd_Ccmuxb); + argi(m, 10, "c1", m1.c, gfxd_Ccmuxc); + argi(m, 11, "d1", m1.d, gfxd_Ccmuxd); + argi(m, 12, "Aa1", m1.Aa, gfxd_Acmuxabd); + argi(m, 13, "Ab1", m1.Ab, gfxd_Acmuxabd); + argi(m, 14, "Ac1", m1.Ac, gfxd_Acmuxc); + argi(m, 15, "Ad1", m1.Ad, gfxd_Acmuxabd); + return 0; + } +} + +UCFUNC int d_DPSetConvert(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetConvert; + argi(m, 0, "k0", sx(getfield(hi, 9, 13), 9), gfxd_Cv); + argi(m, 1, "k1", sx(getfield(hi, 9, 4), 9), gfxd_Cv); + argi(m, 2, "k2", sx((getfield(hi, 4, 0) << 5) | getfield(lo, 5, 27), 9), + gfxd_Cv); + argi(m, 3, "k3", sx(getfield(lo, 9, 18), 9), gfxd_Cv); + argi(m, 4, "k4", sx(getfield(lo, 9, 9), 9), gfxd_Cv); + argi(m, 5, "k5", sx(getfield(lo, 9, 0), 9), gfxd_Cv); + return 0; +} + +UCFUNC int d_DPSetTextureConvert(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTextureConvert; + argu(m, 0, "mode", lo, gfxd_Tc); + return 0; +} + +UCFUNC int d_DPSetCycleType(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetCycleType; + argu(m, 0, "mode", lo, gfxd_Cyc); + return 0; +} + +UCFUNC int d_DPSetDepthSource(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetDepthSource; + argu(m, 0, "mode", lo, gfxd_Zs); + return 0; +} + +UCFUNC int d_DPSetCombineKey(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetCombineKey; + argu(m, 0, "mode", lo, gfxd_Ck); + return 0; +} + +UCFUNC int d_DPSetKeyGB(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetKeyGB; + argu(m, 0, "cG", getfield(lo, 8, 24), gfxd_Color); + argu(m, 1, "sG", getfield(lo, 8, 16), gfxd_Keyscale); + argi(m, 2, "wG", sx(getfield(hi, 12, 12), 12), gfxd_Keywidth); + argu(m, 3, "cB", getfield(lo, 8, 8), gfxd_Color); + argu(m, 4, "sB", getfield(lo, 8, 0), gfxd_Keyscale); + argi(m, 5, "wB", sx(getfield(hi, 12, 0), 12), gfxd_Keywidth); + return 0; +} + +UCFUNC int d_DPSetKeyR(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetKeyR; + argu(m, 0, "cR", getfield(lo, 8, 8), gfxd_Color); + argu(m, 1, "sR", getfield(lo, 8, 0), gfxd_Keyscale); + argi(m, 2, "wR", sx(getfield(lo, 12, 16), 12), gfxd_Keywidth); + return 0; +} + +UCFUNC int d_DPSetPrimDepth(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetPrimDepth; + argi(m, 0, "z", sx(getfield(lo, 16, 16), 16), gfxd_Zi); + argi(m, 1, "dz", sx(getfield(lo, 16, 0), 16), gfxd_Zi); + return 0; +} + +UCFUNC int d_DPSetRenderMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetRenderMode; + argu(m, 0, "mode1", lo, gfxd_Rm1); + argu(m, 1, "mode2", lo, gfxd_Rm2); + return 0; +} + +UCFUNC int d_DPSetScissor(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetScissor; + argi(m, 0, "mode", getfield(lo, 2, 24), gfxd_Sc); + argu(m, 1, "ulx", getfield(hi, 10, 14), gfxd_Coordi); + argu(m, 2, "uly", getfield(hi, 10, 2), gfxd_Coordi); + argu(m, 3, "lrx", getfield(lo, 10, 14), gfxd_Coordi); + argu(m, 4, "lry", getfield(lo, 10, 2), gfxd_Coordi); + return 0; +} + +UCFUNC int d_DPSetScissorFrac(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + qu102_t ulx = getfield(hi, 12, 12); + qu102_t uly = getfield(hi, 12, 0); + qu102_t lrx = getfield(lo, 12, 12); + qu102_t lry = getfield(lo, 12, 0); + if ((ulx & 3) || (uly & 3) || (lrx & 3) || (lry & 3)) + { + m->id = gfxd_DPSetScissorFrac; + argi(m, 0, "mode", getfield(lo, 2, 24), gfxd_Sc); + argu(m, 1, "ulx", ulx, gfxd_Coordq); + argu(m, 2, "uly", uly, gfxd_Coordq); + argu(m, 3, "lrx", lrx, gfxd_Coordq); + argu(m, 4, "lry", lry, gfxd_Coordq); + return 0; + } + else + return d_DPSetScissor(m, hi, lo); +} + +UCFUNC int d_DPSetTextureDetail(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTextureDetail; + argu(m, 0, "mode", lo, gfxd_Td); + return 0; +} + +UCFUNC int d_DPSetTextureFilter(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTextureFilter; + argu(m, 0, "mode", lo, gfxd_Tf); + return 0; +} + +UCFUNC int d_DPSetTextureLOD(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTextureLOD; + argu(m, 0, "mode", lo, gfxd_Tl); + return 0; +} + +UCFUNC int d_DPSetTextureLUT(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTextureLUT; + argu(m, 0, "mode", lo, gfxd_Tt); + return 0; +} + +UCFUNC int d_DPSetTexturePersp(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTexturePersp; + argu(m, 0, "mode", lo, gfxd_Tp); + return 0; +} + +UCFUNC int d_DPSetTile(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTile; + argi(m, 0, "fmt", getfield(hi, 3, 21), gfxd_Fmt); + argi(m, 1, "siz", getfield(hi, 2, 19), gfxd_Siz); + argi(m, 2, "line", getfield(hi, 9, 9), gfxd_Line); + argu(m, 3, "tmem", getfield(hi, 9, 0), gfxd_Tmem); + argi(m, 4, "tile", getfield(lo, 3, 24), gfxd_Tile); + argi(m, 5, "pal", getfield(lo, 4, 20), gfxd_Pal); + argu(m, 6, "cmt", getfield(lo, 2, 18), gfxd_Cm); + argi(m, 7, "maskt", getfield(lo, 4, 14), gfxd_Tm); + argi(m, 8, "shiftt", getfield(lo, 4, 10), gfxd_Ts); + argu(m, 9, "cms", getfield(lo, 2, 8), gfxd_Cm); + argi(m, 10, "masks", getfield(lo, 4, 4), gfxd_Tm); + argi(m, 11, "shifts", getfield(lo, 4, 0), gfxd_Ts); + return 0; +} + +UCFUNC int d_DPSetTileSize(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetTileSize; + argi(m, 0, "tile", getfield(lo, 3, 24), gfxd_Tile); + argu(m, 1, "uls", getfield(hi, 12, 12), gfxd_Coordq); + argu(m, 2, "ult", getfield(hi, 12, 0), gfxd_Coordq); + argu(m, 3, "lrs", getfield(lo, 12, 12), gfxd_Coordq); + argu(m, 4, "lrt", getfield(lo, 12, 0), gfxd_Coordq); + return 0; +} + +#if defined(F3D_GBI) +UCFUNC int d_SP1Triangle(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SP1Triangle; + int n0 = getfield(lo, 8, 16); + int n1 = getfield(lo, 8, 8); + int n2 = getfield(lo, 8, 0); + argi(m, 0, "v0", n0 / 10, gfxd_Vtx); + argi(m, 1, "v1", n1 / 10, gfxd_Vtx); + argi(m, 2, "v2", n2 / 10, gfxd_Vtx); + argi(m, 3, "flag", getfield(lo, 8, 24), gfxd_Vtxflag); + int ret = 0; + if (n0 % 10 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 10 != 0) + { + badarg(m, 1); + ret = -1; + } + if (n2 % 10 != 0) + { + badarg(m, 2); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI) +UCFUNC int d_SP1Triangle(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SP1Triangle; + int n0 = getfield(lo, 8, 16); + int n1 = getfield(lo, 8, 8); + int n2 = getfield(lo, 8, 0); + argi(m, 0, "v0", n0 / 2, gfxd_Vtx); + argi(m, 1, "v1", n1 / 2, gfxd_Vtx); + argi(m, 2, "v2", n2 / 2, gfxd_Vtx); + argi(m, 3, "flag", 0, gfxd_Vtxflag); + int ret = 0; + if (n0 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + if (n2 % 2 != 0) + { + badarg(m, 2); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_SP1Triangle(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SP1Triangle; + int n0 = getfield(hi, 8, 16); + int n1 = getfield(hi, 8, 8); + int n2 = getfield(hi, 8, 0); + argi(m, 0, "v0", n0 / 2, gfxd_Vtx); + argi(m, 1, "v1", n1 / 2, gfxd_Vtx); + argi(m, 2, "v2", n2 / 2, gfxd_Vtx); + argi(m, 3, "flag", 0, gfxd_Vtxflag); + int ret = 0; + if (n0 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + if (n2 % 2 != 0) + { + badarg(m, 2); + ret = -1; + } + return ret; +} +#endif + +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int d_SP1Quadrangle(gfxd_macro_t *m, uint32_t hi, uint32_t lo); +UCFUNC int d_SP2Triangles(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int n00 = getfield(hi, 8, 16); + int n01 = getfield(hi, 8, 8); + int n02 = getfield(hi, 8, 0); + int n10 = getfield(lo, 8, 16); + int n11 = getfield(lo, 8, 8); + int n12 = getfield(lo, 8, 0); +#if defined(F3DEX_GBI) + if (n00 == n10 && n02 == n11) + return d_SP1Quadrangle(m, hi, lo); +#endif + m->id = gfxd_SP2Triangles; + argi(m, 0, "v00", n00 / 2, gfxd_Vtx); + argi(m, 1, "v01", n01 / 2, gfxd_Vtx); + argi(m, 2, "v02", n02 / 2, gfxd_Vtx); + argi(m, 3, "flag0", 0, gfxd_Vtxflag); + argi(m, 4, "v10", n10 / 2, gfxd_Vtx); + argi(m, 5, "v11", n11 / 2, gfxd_Vtx); + argi(m, 6, "v12", n12 / 2, gfxd_Vtx); + argi(m, 7, "flag1", 0, gfxd_Vtxflag); + int ret = 0; + if (n00 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n01 % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + if (n02 % 2 != 0) + { + badarg(m, 2); + ret = -1; + } + if (n10 % 2 != 0) + { + badarg(m, 4); + ret = -1; + } + if (n11 % 2 != 0) + { + badarg(m, 5); + ret = -1; + } + if (n12 % 2 != 0) + { + badarg(m, 6); + ret = -1; + } + return ret; +} + +UCFUNC int d_SP1Quadrangle(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SP1Quadrangle; + int n00 = getfield(hi, 8, 16); + int n01 = getfield(hi, 8, 8); + int n02 = getfield(hi, 8, 0); + int n10 = getfield(lo, 8, 16); + int n11 = getfield(lo, 8, 8); + int n12 = getfield(lo, 8, 0); + int v00 = n00 / 2; + int v01 = n01 / 2; + int v02 = n02 / 2; + int v10 = n10 / 2; + int v11 = n11 / 2; + int v12 = n12 / 2; + argi(m, 0, "v0", v00, gfxd_Vtx); + argi(m, 1, "v1", v01, gfxd_Vtx); + argi(m, 2, "v2", v11, gfxd_Vtx); + argi(m, 3, "v3", v12, gfxd_Vtx); + argi(m, 4, "flag", 0, gfxd_Vtxflag); + int ret = 0; + if (v00 != v10 || n00 % 2 != 0 || n10 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n01 % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + if (v02 != v11 || n02 % 2 != 0 || n11 % 2 != 0) + { + badarg(m, 2); + ret = -1; + } + if (n12 % 2 != 0) + { + badarg(m, 3); + ret = -1; + } + return ret; +} + +UCFUNC int c_SPBranchLessZraw(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_DPHalf1) + return -1; + uint32_t branchdl = argvu(&m[0], 0); + if (m[1].id != gfxd_BranchZ) + return -1; + int32_t vtx = argvi(&m[1], 0); + int32_t zval = argvi(&m[1], 1); + m->id = gfxd_SPBranchLessZraw; + argu(m, 0, "dl", branchdl, gfxd_Dl); + argi(m, 1, "vtx", vtx, gfxd_Vtx); + argi(m, 2, "zval", zval, gfxd_Zraw); + return 0; +} +#endif + +UCFUNC int d_SPBranchList(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPBranchList; + argu(m, 0, "dl", lo, gfxd_Dl); + return 0; +} + +UCFUNC int c_SPClipRatio(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 4) + return -1; + if (m[0].id != gfxd_MoveWd + || argvi(&m[0], 0) != G_MW_CLIP + || argvu(&m[0], 1) != G_MWO_CLIP_RNX) + { + return -1; + } + uint32_t r = argvu(&m[0], 2); + if (m[1].id != gfxd_MoveWd + || argvi(&m[1], 0) != G_MW_CLIP + || argvu(&m[1], 1) != G_MWO_CLIP_RNY + || argvu(&m[1], 2) != r) + { + return -1; + } + if (m[2].id != gfxd_MoveWd + || argvi(&m[2], 0) != G_MW_CLIP + || argvu(&m[2], 1) != G_MWO_CLIP_RPX + || ((uint32_t)1 << 16) - argvu(&m[2], 2) != r) + { + return -1; + } + if (m[3].id != gfxd_MoveWd + || argvi(&m[3], 0) != G_MW_CLIP + || argvu(&m[3], 1) != G_MWO_CLIP_RPY + || ((uint32_t)1 << 16) - argvu(&m[3], 2) != r) + { + return -1; + } + m->id = gfxd_SPClipRatio; + argi(m, 0, "r", r, gfxd_Cr); + return 0; +} + +#if defined(F3D_GBI) +UCFUNC int d_SPCullDisplayList(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPCullDisplayList; + int n0 = getfield(hi, 24, 0); + int nn = getfield(lo, 16, 0); + argi(m, 0, "v0", n0 / 40, gfxd_Vtx); + argi(m, 1, "vn", nn / 40 - 1, gfxd_Num); + int ret = 0; + if (n0 % 40 != 0) + { + badarg(m, 0); + ret = -1; + } + if (nn % 40 != 0) + { + badarg(m, 1); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int d_SPCullDisplayList(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPCullDisplayList; + int n0 = getfield(hi, 16, 0); + int nn = getfield(lo, 16, 0); + argi(m, 0, "v0", n0 / 2, gfxd_Vtx); + argi(m, 1, "vn", nn / 2, gfxd_Num); + int ret = 0; + if (n0 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (nn % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + return ret; +} +#endif + +UCFUNC int d_SPDisplayList(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPDisplayList; + argu(m, 0, "dl", lo, gfxd_Dl); + return 0; +} + +UCFUNC int d_SPEndDisplayList(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPEndDisplayList; + return 0; +} + +UCFUNC int d_SPFogFactor(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPFogFactor; + argi(m, 0, "fm", sx(getfield(lo, 16, 16), 16), gfxd_Fogz); + argi(m, 1, "fo", sx(getfield(lo, 16, 0), 16), gfxd_Fogz); + return 0; +} + +UCFUNC int d_SPFogPosition(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int x = sx(getfield(lo, 16, 16), 16); + int y = sx(getfield(lo, 16, 0), 16); + if (x == 0) + return d_SPFogFactor(m, hi, lo); + else + { + int d = 128000 / x; + int yd = y * d; + if (yd > 0) + yd += 255; + else if (yd < 0) + yd -= 255; + int min = 500 - yd / 256; + int max = d + min; + + if (min >= 0 && min <= 1000 && max >= 0 && max <= 1000) + { + m->id = gfxd_SPFogPosition; + argi(m, 0, "min", min, gfxd_Fogp); + argi(m, 1, "max", max, gfxd_Fogp); + return 0; + } + else + return d_SPFogFactor(m, hi, lo); + } +} + +#if defined(F3D_GBI) || defined(F3DEX_GBI) +UCFUNC int c_SPForceMatrix(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 4) + return -1; + for (int i = 0; i < 4; i++) + if (m[i].id != gfxd_MoveMem + || argvu(&m[i], 0) != 16 + || argvu(&m[i], 2) != argvu(&m[0], 2) + i * 16) + { + return -1; + } + if (argvi(&m[0], 1) != G_MV_MATRIX_1 + || argvi(&m[1], 1) != G_MV_MATRIX_2 + || argvi(&m[2], 1) != G_MV_MATRIX_3 + || argvi(&m[3], 1) != G_MV_MATRIX_4) + { + return -1; + } + uint32_t mptr = argvu(&m[0], 2); + m->id = gfxd_SPForceMatrix; + argu(m, 0, "mptr", mptr, gfxd_Mtxptr); + return 0; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int c_SPForceMatrix(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_MoveMem + || argvu(&m[0], 0) != sizeof(Mtx) + || argvi(&m[0], 1) != G_MV_MATRIX + || argvu(&m[0], 2) != 0) + { + return -1; + } + uint32_t mptr = argvu(&m[0], 3); + if (m[1].id != gfxd_MoveWd + || argvi(&m[1], 0) != G_MW_FORCEMTX + || argvu(&m[1], 1) != 0 + || argvu(&m[1], 2) != 0x10000) + { + return -1; + } + m->id = gfxd_SPForceMatrix; + argu(m, 0, "mptr", mptr, gfxd_Mtxptr); + return 0; +} +#endif + +UCFUNC int d_SPSetGeometryMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPSetGeometryMode; + argu(m, 0, "mode", lo, gfxd_Gm); + return 0; +} + +UCFUNC int d_SPClearGeometryMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPClearGeometryMode; +#if defined(F3D_GBI) || defined(F3DEX_GBI) + argu(m, 0, "mode", lo, gfxd_Gm); +#elif defined(F3DEX_GBI_2) + argu(m, 0, "mode", getfield(~hi, 24, 0), gfxd_Gm); +#endif + return 0; +} + +#if defined(F3D_GBI) || defined(F3DEX_GBI) +UCFUNC int c_SPLoadGeometryMode(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_SPClearGeometryMode + || argvu(&m[0], 0) != 0xFFFFFFFF + || m[1].id != gfxd_SPSetGeometryMode) + { + return -1; + } + uint32_t mode = argvu(&m[1], 0); + m->id = gfxd_SPLoadGeometryMode; + argu(m, 0, "mode", mode, gfxd_Gm); + return 0; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_SPLoadGeometryMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPLoadGeometryMode; + argu(m, 0, "mode", lo, gfxd_Gm); + return 0; +} +#endif + +#if defined(F3D_GBI) || defined(F3DEX_GBI) +UCFUNC int d_SPInsertMatrix(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPInsertMatrix; + argu(m, 0, "where", getfield(hi, 16, 8), gfxd_Mwo_matrix); + argu(m, 1, "val", lo, gfxd_Word); + return 0; +} +#endif + +#if defined(F3D_GBI) +UCFUNC int d_SPLine3D(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPLine3D; + int n0 = getfield(lo, 8, 16); + int n1 = getfield(lo, 8, 8); + argi(m, 0, "v0", n0 / 10, gfxd_Vtx); + argi(m, 1, "v1", n1 / 10, gfxd_Vtx); + argi(m, 2, "flag", getfield(lo, 8, 24), gfxd_Vtxflag); + int ret = 0; + if (n0 % 10 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 10 != 0) + { + badarg(m, 1); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI) +UCFUNC int d_SPLine3D(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPLine3D; + int n0 = getfield(lo, 8, 16); + int n1 = getfield(lo, 8, 8); + argi(m, 0, "v0", n0 / 2, gfxd_Vtx); + argi(m, 1, "v1", n1 / 2, gfxd_Vtx); + argi(m, 2, "flag", 0, gfxd_Vtxflag); + int ret = 0; + if (n0 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_SPLine3D(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPLine3D; + int n0 = getfield(hi, 8, 16); + int n1 = getfield(hi, 8, 8); + argi(m, 0, "v0", n0 / 2, gfxd_Vtx); + argi(m, 1, "v1", n1 / 2, gfxd_Vtx); + argi(m, 2, "flag", 0, gfxd_Vtxflag); + int ret = 0; + if (n0 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + return ret; +} +#endif + +#if defined(F3D_GBI) +UCFUNC int d_SPLineW3D(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int wd = getfield(lo, 8, 0); + if (wd == 0) + return d_SPLine3D(m, hi, lo); + else + { + m->id = gfxd_SPLineW3D; + int n0 = getfield(lo, 8, 16); + int n1 = getfield(lo, 8, 8); + argi(m, 0, "v0", n0 / 10, gfxd_Vtx); + argi(m, 1, "v1", n1 / 10, gfxd_Vtx); + argi(m, 2, "wd", wd, gfxd_Linewd); + argi(m, 3, "flag", getfield(lo, 8, 24), gfxd_Vtxflag); + int ret = 0; + if (n0 % 10 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 10 != 0) + { + badarg(m, 1); + ret = -1; + } + return ret; + } +} +#elif defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int d_SPLineW3D(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int wd = getfield(hi, 8, 0); + if (wd == 0) + return d_SPLine3D(m, hi, lo); + else + { + m->id = gfxd_SPLineW3D; + int n0 = getfield(hi, 8, 16); + int n1 = getfield(hi, 8, 8); + argi(m, 0, "v0", n0 / 2, gfxd_Vtx); + argi(m, 1, "v1", n1 / 2, gfxd_Vtx); + argi(m, 2, "wd", wd, gfxd_Linewd); + argi(m, 3, "flag", 0, gfxd_Vtxflag); + int ret = 0; + if (n0 % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + if (n1 % 2 != 0) + { + badarg(m, 1); + ret = -1; + } + return ret; + } +} +#endif + +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int c_SPLoadUcode(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_DPHalf1) + return -1; + uint32_t uc_dstart = argvu(&m[0], 0); + if (m[1].id != gfxd_LoadUcode) + return -1; + uint32_t uc_start = argvu(&m[1], 0); + uint32_t uc_dsize = argvu(&m[1], 1); + if (uc_dsize != 0x800) + return -1; + m->id = gfxd_SPLoadUcode; + argu(m, 0, "uc_start", uc_start, gfxd_Uctext); + argu(m, 1, "uc_dstart", uc_dstart, gfxd_Ucdata); + return 0; +} +#endif + +UCFUNC int d_SPLookAtX(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPLookAtX; + argu(m, 0, "l", lo, gfxd_Lookatptr); + return 0; +} + +UCFUNC int d_SPLookAtY(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPLookAtY; + argu(m, 0, "l", lo, gfxd_Lookatptr); + return 0; +} + +UCFUNC int c_SPLookAt(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_SPLookAtX) + return -1; + uint32_t l = argvu(&m[0], 0); + if (m[1].id != gfxd_SPLookAtY || argvu(&m[1], 0) != l + 0x10) + return -1; + m->id = gfxd_SPLookAt; + argu(m, 0, "l", l, gfxd_Lookatptr); + return 0; +} + +#if defined(F3D_GBI) || defined(F3DEX_GBI) +UCFUNC int d_SPMatrix(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPMatrix; + int x = getfield(hi, 16, 0); + argu(m, 0, "matrix", lo, gfxd_Mtxptr); + argi(m, 1, "param", getfield(hi, 8, 16), gfxd_Mtxparam); + if (x != sizeof(Mtx)) + return -1; + else + return 0; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_SPMatrix(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPMatrix; + int x = getfield(hi, 5, 19); + argu(m, 0, "matrix", lo, gfxd_Mtxptr); + argi(m, 1, "param", getfield(hi, 8, 0) ^ G_MTX_PUSH, gfxd_Mtxparam); + if (x != (sizeof(Mtx) - 1) / 8) + return -1; + else + return 0; +} +#endif + +#if defined(F3D_GBI) || (defined(F3D_BETA) && defined(F3DEX_GBI)) +UCFUNC int d_SPModifyVertex(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPModifyVertex; + int offset = getfield(hi, 16, 8); + argi(m, 0, "vtx", offset / 40, gfxd_Vtx); + argu(m, 1, "where", offset % 40, gfxd_Mwo_point); + argu(m, 2, "val", lo, gfxd_Word); + return 0; +} +#elif defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int d_SPModifyVertex(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPModifyVertex; + int vtx = getfield(hi, 16, 0); + argi(m, 0, "vtx", vtx / 2, gfxd_Vtx); + argu(m, 1, "where", getfield(hi, 8, 16), gfxd_Mwo_point); + argu(m, 2, "val", lo, gfxd_Word); + int ret = 0; + if (vtx % 2 != 0) + { + badarg(m, 0); + ret = -1; + } + return ret; +} +#endif + +UCFUNC int d_SPPerspNormalize(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPPerspNormalize; + argu(m, 0, "scale", getfield(lo, 16, 0), gfxd_Wscale); + return 0; +} + +UCFUNC int d_SPPopMatrix(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPPopMatrix; +#if defined(F3D_GBI) || defined(F3DEX_GBI) + argi(m, 0, "param", lo, gfxd_Mtxstack); +#elif defined(F3DEX_GBI_2) + argi(m, 0, "param", G_MTX_MODELVIEW, gfxd_Mtxstack); +#endif + return 0; +} + +#if defined(F3DEX_GBI_2) +UCFUNC int d_SPPopMatrixN(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int len = (getfield(hi, 5, 19) + 1) * 8; + int ofs = getfield(hi, 8, 8) * 8; + int idx = getfield(hi, 8, 0); + int n = lo / sizeof(Mtx); + if (lo % sizeof(Mtx) == 0 + && len == sizeof(Mtx) + && ofs == 0 + && idx == 2 + && n == 1) + { + return d_SPPopMatrix(m, hi, lo); + } + m->id = gfxd_SPPopMatrixN; + argi(m, 0, "param", G_MTX_MODELVIEW, gfxd_Mtxstack); + argi(m, 1, "num", n, gfxd_Num); + int ret = 0; + if (lo % sizeof(Mtx) != 0) + { + badarg(m, 1); + ret = -1; + } + if (len != sizeof(Mtx) || ofs != 0 || idx != 2) + ret = -1; + return ret; +} +#endif + +UCFUNC int d_SPSegment(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPSegment; +#if defined(F3D_GBI) || defined(F3DEX_GBI) + int offset = getfield(hi, 16, 8); +#elif defined(F3DEX_GBI_2) + int offset = getfield(hi, 16, 0); +#endif + argu(m, 0, "seg", offset / 4, gfxd_Seg); + argu(m, 1, "base", lo, gfxd_Segptr); + int ret = 0; + if (offset % 4 != 0) + { + badarg(m, 0); + ret = -1; + } + return ret; +} + +UCFUNC int c_SPSetLightsN(gfxd_macro_t *m, int n_macro, int id, int numlights) +{ + if (n_macro < 2 + numlights) + return -1; + if (m[0].id != gfxd_SPNumLights || argvi(&m[0], 0) != numlights) + return -1; + int a = 1 + numlights; + if (m[a].id != gfxd_SPLight || argvi(&m[a], 1) != a) + return -1; + uint32_t l = argvu(&m[a], 0); + for (int i = 1; i <= numlights; i++) + { + int offset = sizeof(Ambient) + sizeof(Light) * (i - 1); + if (m[i].id != gfxd_SPLight + || argvu(&m[i], 0) != l + offset + || argvi(&m[i], 1) != i) + { + return -1; + } + } + m->id = id; + argu(m, 0, "l", l, gfxd_Lightsn); + return 0; +} + +UCFUNC int c_SPSetLights1(gfxd_macro_t *m, int n_macro) +{ + return c_SPSetLightsN(m, n_macro, gfxd_SPSetLights1, NUMLIGHTS_1); +} + +UCFUNC int c_SPSetLights2(gfxd_macro_t *m, int n_macro) +{ + return c_SPSetLightsN(m, n_macro, gfxd_SPSetLights2, NUMLIGHTS_2); +} + +UCFUNC int c_SPSetLights3(gfxd_macro_t *m, int n_macro) +{ + return c_SPSetLightsN(m, n_macro, gfxd_SPSetLights3, NUMLIGHTS_3); +} + +UCFUNC int c_SPSetLights4(gfxd_macro_t *m, int n_macro) +{ + return c_SPSetLightsN(m, n_macro, gfxd_SPSetLights4, NUMLIGHTS_4); +} + +UCFUNC int c_SPSetLights5(gfxd_macro_t *m, int n_macro) +{ + return c_SPSetLightsN(m, n_macro, gfxd_SPSetLights5, NUMLIGHTS_5); +} + +UCFUNC int c_SPSetLights6(gfxd_macro_t *m, int n_macro) +{ + return c_SPSetLightsN(m, n_macro, gfxd_SPSetLights6, NUMLIGHTS_6); +} + +UCFUNC int c_SPSetLights7(gfxd_macro_t *m, int n_macro) +{ + return c_SPSetLightsN(m, n_macro, gfxd_SPSetLights7, NUMLIGHTS_7); +} + +#if defined(F3D_GBI) || defined(F3DEX_GBI) +UCFUNC int d_SPNumLights(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPNumLights; + argi(m, 0, "n", (lo - 0x80000000) / 32 - 1, gfxd_Numlights); + int ret = 0; + if (lo < 0x80000040 || lo % 32 != 0) + { + badarg(m, 0); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_SPNumLights(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPNumLights; + argi(m, 0, "n", lo / 24, gfxd_Numlights); + int ret = 0; + if (lo < 24 || lo % 24 != 0) + { + badarg(m, 0); + ret = -1; + } + return ret; +} +#endif + +#if defined(F3D_GBI) || defined(F3DEX_GBI) +UCFUNC int d_SPLight(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int n = (getfield(hi, 8, 16) - G_MV_L0) / 2 + 1; + m->id = gfxd_SPLight; + argu(m, 0, "l", lo, gfxd_Lightptr); + argi(m, 1, "n", n, gfxd_Num); + return 0; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_SPLight(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int n = (getfield(hi, 8, 8) * 8 / 24) - 1; + m->id = gfxd_SPLight; + argu(m, 0, "l", lo, gfxd_Lightptr); + argi(m, 1, "n", n, gfxd_Num); + return 0; +} +#endif + +UCFUNC int c_SPLightColor(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_MoveWd + || argvi(&m[0], 0) != G_MW_LIGHTCOL + || argvu(&m[0], 1) % 0x18 != 0 + || argvu(&m[0], 1) > G_MWO_aLIGHT_8) + { + return -1; + } + uint32_t offset = argvu(&m[0], 1); + uint32_t packedcolor = argvu(&m[0], 2); + if (m[1].id != gfxd_MoveWd + || argvi(&m[1], 0) != G_MW_LIGHTCOL + || argvu(&m[1], 1) != offset + 4 + || argvu(&m[1], 2) != packedcolor) + { + return -1; + } + m->id = gfxd_SPLightColor; + argi(m, 0, "n", offset / 0x18 + 1, gfxd_Lightnum); + argu(m, 1, "c", packedcolor, gfxd_Color); + return 0; +} + +UCFUNC int d_SPTexture(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPTexture; + argu(m, 0, "sc", getfield(lo, 16, 16), gfxd_Tcscale); + argu(m, 1, "tc", getfield(lo, 16, 0), gfxd_Tcscale); + argi(m, 2, "level", getfield(hi, 3, 11), gfxd_Num); + argi(m, 3, "tile", getfield(hi, 3, 8), gfxd_Tile); +#if defined(F3D_GBI) || defined(F3DEX_GBI) + argi(m, 4, "on", getfield(hi, 8, 0), gfxd_Switch); +#elif defined(F3DEX_GBI_2) + argi(m, 4, "on", getfield(hi, 7, 1), gfxd_Switch); +#endif + return 0; +} + +UCFUNC int c_SPTextureRectangle(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 3) + return -1; + if (m[0].id != gfxd_TexRect) + return -1; + qu102_t ulx = argvu(&m[0], 0); + qu102_t uly = argvu(&m[0], 1); + qu102_t lrx = argvu(&m[0], 2); + qu102_t lry = argvu(&m[0], 3); + int tile = argvi(&m[0], 4); + if (m[1].id != gfxd_DPHalf1) + return -1; + qs105_t s = sx(getfield(argvu(&m[1], 0), 16, 16), 16); + qs105_t t = sx(getfield(argvu(&m[1], 0), 16, 0), 16); + if (m[2].id != gfxd_DPHalf2) + return -1; + qs510_t dsdx = sx(getfield(argvu(&m[2], 0), 16, 16), 16); + qs510_t dtdy = sx(getfield(argvu(&m[2], 0), 16, 0), 16); + m->id = gfxd_SPTextureRectangle; + argu(m, 0, "ulx", ulx, gfxd_Coordq); + argu(m, 1, "uly", uly, gfxd_Coordq); + argu(m, 2, "lrx", lrx, gfxd_Coordq); + argu(m, 3, "lry", lry, gfxd_Coordq); + argi(m, 4, "tile", tile, gfxd_Tile); + argi(m, 5, "s", s, gfxd_St); + argi(m, 6, "t", t, gfxd_St); + argi(m, 7, "dsdx", dsdx, gfxd_Stdelta); + argi(m, 8, "dtdy", dtdy, gfxd_Stdelta); + return 0; +} + +UCFUNC int c_SPTextureRectangleFlip(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 3) + return -1; + if (m[0].id != gfxd_TexRectFlip) + return -1; + qu102_t ulx = argvu(&m[0], 0); + qu102_t uly = argvu(&m[0], 1); + qu102_t lrx = argvu(&m[0], 2); + qu102_t lry = argvu(&m[0], 3); + int tile = argvi(&m[0], 4); + if (m[1].id != gfxd_DPHalf1) + return -1; + qs105_t s = sx(getfield(argvu(&m[1], 0), 16, 16), 16); + qs105_t t = sx(getfield(argvu(&m[1], 0), 16, 0), 16); + if (m[2].id != gfxd_DPHalf2) + return -1; + qs510_t dsdx = sx(getfield(argvu(&m[2], 0), 16, 16), 16); + qs510_t dtdy = sx(getfield(argvu(&m[2], 0), 16, 0), 16); + m->id = gfxd_SPTextureRectangleFlip; + argu(m, 0, "ulx", ulx, gfxd_Coordq); + argu(m, 1, "uly", uly, gfxd_Coordq); + argu(m, 2, "lrx", lrx, gfxd_Coordq); + argu(m, 3, "lry", lry, gfxd_Coordq); + argi(m, 4, "tile", tile, gfxd_Tile); + argi(m, 5, "s", s, gfxd_St); + argi(m, 6, "t", t, gfxd_St); + argi(m, 7, "dsdx", dsdx, gfxd_Stdelta); + argi(m, 8, "dtdy", dtdy, gfxd_Stdelta); + return 0; +} + +#if defined(F3D_GBI) +UCFUNC int d_SPVertex(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPVertex; + int n = getfield(hi, 4, 20) + 1; + int v0 = getfield(hi, 4, 16); + int size = getfield(hi, 16, 0); + argu(m, 0, "v", lo, gfxd_Vtxptr); + argi(m, 1, "n", n, gfxd_Num); + argi(m, 2, "v0", v0, gfxd_Vtx); + int ret = 0; + if (size != sizeof(Vtx) * n) + { + badarg(m, 1); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI) +UCFUNC int d_SPVertex(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPVertex; + int n = getfield(hi, 6, 10); + int v0 = getfield(hi, 8, 16); + int size = getfield(hi, 10, 0); + argu(m, 0, "v", lo, gfxd_Vtxptr); + argi(m, 1, "n", n, gfxd_Num); + argi(m, 2, "v0", v0 / 2, gfxd_Vtx); + int ret = 0; + if (size != sizeof(Vtx) * n - 1) + { + badarg(m, 1); + ret = -1; + } + if (v0 % 2 != 0) + { + badarg(m, 2); + ret = -1; + } + return ret; +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_SPVertex(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPVertex; + int n = getfield(hi, 8, 12); + int v0 = getfield(hi, 7, 1) - n; + argu(m, 0, "v", lo, gfxd_Vtxptr); + argi(m, 1, "n", n, gfxd_Num); + argi(m, 2, "v0", v0, gfxd_Vtx); + return 0; +} +#endif + +UCFUNC int d_SPViewport(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPViewport; + argu(m, 0, "v", lo, gfxd_Vpptr); + return 0; +} + +UCFUNC int d_DPLoadTLUTCmd(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPLoadTLUTCmd; + argi(m, 0, "tile", getfield(lo, 3, 24), gfxd_Tile); + argi(m, 1, "count", getfield(lo, 10, 14), gfxd_Num); + return 0; +} + +UCFUNC int c_DPLoadTLUT(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 6) + return -1; + if (m[0].id != gfxd_DPSetTextureImage + || argvi(&m[0], 0) != G_IM_FMT_RGBA + || argvi(&m[0], 1) != G_IM_SIZ_16b + || argvi(&m[0], 2) != 1) + { + return -1; + } + uint32_t dram = argvu(&m[0], 3); + if (m[1].id != gfxd_DPTileSync) + return -1; + if (m[2].id != gfxd_DPSetTile + || argvi(&m[2], 0) != 0 + || argvi(&m[2], 1) != 0 + || argvi(&m[2], 2) != 0 + || argvi(&m[2], 4) != G_TX_LOADTILE + || argvi(&m[2], 5) != 0 + || argvu(&m[2], 6) != 0 + || argvi(&m[2], 7) != 0 + || argvi(&m[2], 8) != 0 + || argvu(&m[2], 9) != 0 + || argvi(&m[2], 10) != 0 + || argvi(&m[2], 11) != 0) + { + return -1; + } + uint32_t tmem = argvu(&m[2], 3); + if (m[3].id != gfxd_DPLoadSync) + return -1; + if (m[4].id != gfxd_DPLoadTLUTCmd || argvi(&m[4], 0) != G_TX_LOADTILE) + return -1; + int count = argvi(&m[4], 1) + 1; + if (m[5].id != gfxd_DPPipeSync) + return -1; + m->id = gfxd_DPLoadTLUT; + argi(m, 0, "count", count, gfxd_Num); + argu(m, 1, "tmem", tmem, gfxd_Tmem); + argu(m, 2, "dram", dram, gfxd_Tlut); + return 0; +} + +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int d_BranchZ(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_BranchZ; + int na = getfield(hi, 12, 12); + int nb = getfield(hi, 12, 0); + int32_t zval; + if (lo > 0x7FFFFFFF) + zval = INT32_MIN + (int32_t)(lo & 0x7FFFFFFF); + else + zval = lo; + argi(m, 0, "vtx", nb / 2, gfxd_Vtx); + argi(m, 1, "zval", zval, gfxd_Zraw); + int ret = 0; + if (nb % 2 != 0 || na / 5 != nb / 2 || na % 5 != 0) + { + badarg(m, 0); + ret = -1; + } + return ret; +} +#endif + +UCFUNC int d_DisplayList(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int flag = getfield(hi, 8, 16); + if (flag == 0) + return d_SPDisplayList(m, hi, lo); + else if (flag == 1) + return d_SPBranchList(m, hi, lo); + else + { + m->id = gfxd_DisplayList; + argu(m, 0, "dl", lo, gfxd_Dl); + argi(m, 1, "flag", flag, gfxd_Dlflag); + return 0; + } +} + +UCFUNC int d_DPHalf1(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPHalf1; + argu(m, 0, "hi", lo, gfxd_Word); + return 0; +} + +UCFUNC int d_DPHalf2(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPHalf2; + argu(m, 0, "lo", lo, gfxd_Word); + return 0; +} + +UCFUNC int c_DPWord(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_DPHalf1 || m[1].id != gfxd_DPHalf2) + return -1; + uint32_t wordhi = argvu(&m[0], 0); + uint32_t wordlo = argvu(&m[1], 0); + m->id = gfxd_DPWord; + argu(m, 0, "wordhi", wordhi, gfxd_Word); + argu(m, 1, "wordlo", wordlo, gfxd_Word); + return 0; +} + +UCFUNC int d_DPLoadTile(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPLoadTile; + argi(m, 0, "tile", getfield(lo, 3, 24), gfxd_Tile); + argu(m, 1, "uls", getfield(hi, 12, 12), gfxd_Coordq); + argu(m, 2, "ult", getfield(hi, 12, 0), gfxd_Coordq); + argu(m, 3, "lrs", getfield(lo, 12, 12), gfxd_Coordq); + argu(m, 4, "lrt", getfield(lo, 12, 0), gfxd_Coordq); + return 0; +} + +#if defined(F3DEX_GBI_2) +UCFUNC int d_SPGeometryMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + uint32_t clearbits = getfield(~hi, 24, 0); + uint32_t setbits = lo; + if (clearbits == 0 && setbits != 0) + return d_SPSetGeometryMode(m, hi, lo); + else if (clearbits != 0 && setbits == 0) + return d_SPClearGeometryMode(m, hi, lo); + else if (clearbits == 0x00FFFFFF) + return d_SPLoadGeometryMode(m, hi, lo); + else + { + m->id = gfxd_SPGeometryMode; + argu(m, 0, "c", clearbits, gfxd_Gm); + argu(m, 1, "s", setbits, gfxd_Gm); + return 0; + } +} +#endif + +UCFUNC int d_SPSetOtherMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPSetOtherMode; + int opc = getfield(hi, 8, 24); +#if defined(F3D_GBI) || defined(F3DEX_GBI) + int length = getfield(hi, 8, 0); + int shift = getfield(hi, 8, 8); +#elif defined(F3DEX_GBI_2) + int length = getfield(hi, 8, 0) + 1; + int shift = 32 - (getfield(hi, 8, 8) + length); +#endif + argi(m, 0, "opc", opc, gfxd_Opcode); + argi(m, 1, "sft", shift, gfxd_Sftlo); + argi(m, 2, "len", length, gfxd_Num); + if (opc == G_SETOTHERMODE_H) + argu(m, 3, "mode", lo, gfxd_Othermodehi); + else if (opc == G_SETOTHERMODE_L) + argu(m, 3, "mode", lo, gfxd_Othermodelo); + else + argu(m, 3, "mode", lo, gfxd_Word); + return 0; +} + +UCFUNC int d_SPSetOtherModeLo(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ +#if defined(F3D_GBI) || defined(F3DEX_GBI) + int length = getfield(hi, 8, 0); + int shift = getfield(hi, 8, 8); +#elif defined(F3DEX_GBI_2) + int length = getfield(hi, 8, 0) + 1; + int shift = 32 - (getfield(hi, 8, 8) + length); +#endif + if (shift == G_MDSFT_ALPHACOMPARE && length == G_MDSIZ_ALPHACOMPARE) + return d_DPSetAlphaCompare(m, hi, lo); + else if (shift == G_MDSFT_ZSRCSEL && length == G_MDSIZ_ZSRCSEL) + return d_DPSetDepthSource(m, hi, lo); + else if (shift == G_MDSFT_RENDERMODE && length == G_MDSIZ_RENDERMODE) + return d_DPSetRenderMode(m, hi, lo); + else if (config.emit_ext_macro) + { + m->id = gfxd_SPSetOtherModeLo; + argi(m, 0, "sft", shift, gfxd_Sftlo); + argi(m, 1, "len", length, gfxd_Num); + argu(m, 2, "mode", lo, gfxd_Othermodelo); + return 0; + } + else + return d_SPSetOtherMode(m, hi, lo); +} + +UCFUNC int d_SPSetOtherModeHi(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ +#if defined(F3D_GBI) || defined(F3DEX_GBI) + int length = getfield(hi, 8, 0); + int shift = getfield(hi, 8, 8); +#elif defined(F3DEX_GBI_2) + int length = getfield(hi, 8, 0) + 1; + int shift = 32 - (getfield(hi, 8, 8) + length); +#endif + if (shift == G_MDSFT_ALPHADITHER && length == G_MDSIZ_ALPHADITHER) + return d_DPSetAlphaDither(m, hi, lo); + else if (shift == G_MDSFT_RGBDITHER && length == G_MDSIZ_RGBDITHER) + return d_DPSetColorDither(m, hi, lo); + else if (shift == G_MDSFT_COMBKEY && length == G_MDSIZ_COMBKEY) + return d_DPSetCombineKey(m, hi, lo); + else if (shift == G_MDSFT_TEXTCONV && length == G_MDSIZ_TEXTCONV) + return d_DPSetTextureConvert(m, hi, lo); + else if (shift == G_MDSFT_TEXTFILT && length == G_MDSIZ_TEXTFILT) + return d_DPSetTextureFilter(m, hi, lo); + else if (shift == G_MDSFT_TEXTLUT && length == G_MDSIZ_TEXTLUT) + return d_DPSetTextureLUT(m, hi, lo); + else if (shift == G_MDSFT_TEXTLOD && length == G_MDSIZ_TEXTLOD) + return d_DPSetTextureLOD(m, hi, lo); + else if (shift == G_MDSFT_TEXTDETAIL && length == G_MDSIZ_TEXTDETAIL) + return d_DPSetTextureDetail(m, hi, lo); + else if (shift == G_MDSFT_TEXTPERSP && length == G_MDSIZ_TEXTPERSP) + return d_DPSetTexturePersp(m, hi, lo); + else if (shift == G_MDSFT_CYCLETYPE && length == G_MDSIZ_CYCLETYPE) + return d_DPSetCycleType(m, hi, lo); + else if (shift == G_MDSFT_PIPELINE && length == G_MDSIZ_PIPELINE) + return d_DPPipelineMode(m, hi, lo); + else if (config.emit_ext_macro) + { + m->id = gfxd_SPSetOtherModeHi; + argi(m, 0, "sft", shift, gfxd_Sfthi); + argi(m, 1, "len", length, gfxd_Num); + argu(m, 2, "mode", lo, gfxd_Othermodehi); + return 0; + } + else + return d_SPSetOtherMode(m, hi, lo); +} + +UCFUNC int d_DPSetOtherMode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_DPSetOtherMode; + argu(m, 0, "hi", getfield(hi, 24, 0), gfxd_Othermodehi); + argu(m, 1, "lo", lo, gfxd_Othermodelo); + return 0; +} + +UCFUNC int d_MoveWd(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ +#if defined(F3D_GBI) || defined(F3DEX_GBI) + int index = getfield(hi, 8, 0); + int offset = getfield(hi, 16, 8); +#elif defined(F3DEX_GBI_2) + int index = getfield(hi, 8, 16); + int offset = getfield(hi, 16, 0); +#endif + if (index == G_MW_FOG && offset == G_MWO_FOG) + return d_SPFogPosition(m, hi, lo); +#if !(defined(F3D_BETA) && (defined(F3D_GBI) || defined(F3DEX_GBI))) + else if (index == G_MW_PERSPNORM && offset == 0) + return d_SPPerspNormalize(m, hi, lo); +#endif + else if (index == G_MW_SEGMENT) + return d_SPSegment(m, hi, lo); + else if (index == G_MW_NUMLIGHT && offset == G_MWO_NUMLIGHT) + return d_SPNumLights(m, hi, lo); +#if defined(F3D_GBI) || (defined(F3D_BETA) && defined(F3DEX_GBI)) + else if (index == G_MW_POINTS) + return d_SPModifyVertex(m, hi, lo); +#endif +#if defined(F3D_GBI) || defined(F3DEX_GBI) + else if (index == G_MW_MATRIX) + return d_SPInsertMatrix(m, hi, lo); +#endif + else + { + m->id = gfxd_MoveWd; + argi(m, 0, "index", index, gfxd_Mw); + if (index == G_MW_MATRIX) + argu(m, 1, "offset", offset, gfxd_Mwo_matrix); + else if (index == G_MW_CLIP) + argu(m, 1, "offset", offset, gfxd_Mwo_clip); + else if (index == G_MW_LIGHTCOL) + argu(m, 1, "offset", offset, gfxd_Mwo_lightcol); + else + argu(m, 1, "offset", offset, gfxd_Mwo); + argu(m, 2, "value", lo, gfxd_Word); + } + return 0; +} + +#if defined(F3D_GBI) || defined(F3DEX_GBI) +UCFUNC int d_MoveMem(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int size = getfield(hi, 16, 0); + int index = getfield(hi, 8, 16); + if (size == sizeof(Light) + && index >= G_MV_L0 + && index <= G_MV_L7 + && index % 2 == 0) + { + return d_SPLight(m, hi, lo); + } + else if (size == sizeof(Light) && index == G_MV_LOOKATX) + return d_SPLookAtX(m, hi, lo); + else if (size == sizeof(Light) && index == G_MV_LOOKATY) + return d_SPLookAtY(m, hi, lo); + else if (size == sizeof(Vp) && index == G_MV_VIEWPORT) + return d_SPViewport(m, hi, lo); + else + { + m->id = gfxd_MoveMem; + argu(m, 0, "size", size, gfxd_Size); + argi(m, 1, "index", index, gfxd_Mv); + argu(m, 2, "dram", lo, gfxd_Dram); + return 0; + } +} +#elif defined(F3DEX_GBI_2) +UCFUNC int d_MoveMem(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int size = (getfield(hi, 5, 19) + 1) * 8; + int index = getfield(hi, 8, 0); + int offset = getfield(hi, 8, 8) * 8; + if (size == sizeof(Light) + && index == G_MV_LIGHT + && offset >= G_MVO_L0 + && offset <= G_MVO_L7 + && offset % 0x18 == 0) + { + return d_SPLight(m, hi, lo); + } + else if (size == sizeof(Light) + && index == G_MV_LIGHT + && offset == G_MVO_LOOKATX) + { + return d_SPLookAtX(m, hi, lo); + } + else if (size == sizeof(Light) + && index == G_MV_LIGHT + && offset == G_MVO_LOOKATY) + { + return d_SPLookAtY(m, hi, lo); + } + else if (size == sizeof(Vp) + && index == G_MV_VIEWPORT + && offset == 0) + { + return d_SPViewport(m, hi, lo); + } + else + { + m->id = gfxd_MoveMem; + argu(m, 0, "size", size, gfxd_Size); + argi(m, 1, "index", index, gfxd_Mv); + argu(m, 2, "offset", offset, gfxd_Size); + argu(m, 3, "dram", lo, gfxd_Dram); + return 0; + } +} +#endif + +#if defined(F3DEX_GBI_2) +UCFUNC int d_SPDmaRead(gfxd_macro_t *m, uint32_t hi, uint32_t lo); +UCFUNC int d_SPDmaWrite(gfxd_macro_t *m, uint32_t hi, uint32_t lo); +UCFUNC int d_SPDma_io(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + int flag = getfield(hi, 1, 23); + if (flag == 0) + return d_SPDmaRead(m, hi, lo); + else if (flag == 1) + return d_SPDmaWrite(m, hi, lo); + else + { + m->id = gfxd_SPDma_io; + argi(m, 0, "flag", flag, gfxd_Dmaflag); + argu(m, 1, "dmem", getfield(hi, 10, 13) * 8, gfxd_Dmem); + argu(m, 2, "dram", lo, gfxd_Dram); + argu(m, 3, "size", getfield(hi, 12, 10) + 1, gfxd_Size); + return 0; + } +} + +UCFUNC int d_SPDmaRead(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPDmaRead; + argu(m, 0, "dmem", getfield(hi, 10, 13) * 8, gfxd_Dmem); + argu(m, 1, "dram", lo, gfxd_Dram); + argu(m, 2, "size", getfield(hi, 12, 10) + 1, gfxd_Size); + return 0; +} + +UCFUNC int d_SPDmaWrite(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPDmaWrite; + argu(m, 0, "dmem", getfield(hi, 10, 13) * 8, gfxd_Dmem); + argu(m, 1, "dram", lo, gfxd_Dram); + argu(m, 2, "size", getfield(hi, 12, 10) + 1, gfxd_Size); + return 0; +} +#endif + +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) +UCFUNC int d_LoadUcode(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_LoadUcode; + argu(m, 0, "uc_start", lo, gfxd_Uctext); + argu(m, 1, "uc_dsize", getfield(hi, 16, 0) + 1, gfxd_Size); + return 0; +} + +UCFUNC int c_SPLoadUcodeEx(gfxd_macro_t *m, int n_macro) +{ + if (n_macro < 2) + return -1; + if (m[0].id != gfxd_DPHalf1) + return -1; + uint32_t uc_dstart = argvu(&m[0], 0); + if (m[1].id != gfxd_LoadUcode) + return -1; + uint32_t uc_start = argvu(&m[1], 0); + uint32_t uc_dsize = argvu(&m[1], 1); + m->id = gfxd_SPLoadUcodeEx; + argu(m, 0, "uc_start", uc_start, gfxd_Uctext); + argu(m, 1, "uc_dstart", uc_dstart, gfxd_Ucdata); + argu(m, 2, "uc_dsize", uc_dsize, gfxd_Size); + return 0; +} +#endif + +UCFUNC int d_TexRect(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_TexRect; + argu(m, 0, "ulx", getfield(lo, 12, 12), gfxd_Coordq); + argu(m, 1, "uly", getfield(lo, 12, 0), gfxd_Coordq); + argu(m, 2, "lrx", getfield(hi, 12, 12), gfxd_Coordq); + argu(m, 3, "lry", getfield(hi, 12, 0), gfxd_Coordq); + argi(m, 4, "tile", getfield(lo, 3, 24), gfxd_Tile); + return 0; +} + +UCFUNC int d_TexRectFlip(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_TexRectFlip; + argu(m, 0, "ulx", getfield(lo, 12, 12), gfxd_Coordq); + argu(m, 1, "uly", getfield(lo, 12, 0), gfxd_Coordq); + argu(m, 2, "lrx", getfield(hi, 12, 12), gfxd_Coordq); + argu(m, 3, "lry", getfield(hi, 12, 0), gfxd_Coordq); + argi(m, 4, "tile", getfield(lo, 3, 24), gfxd_Tile); + return 0; +} + +UCFUNC int d_SPNoOp(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_SPNoOp; + return 0; +} + +#if defined(F3DEX_GBI_2) +UCFUNC int d_Special3(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_Special3; + argu(m, 0, "hi", getfield(hi, 24, 0), gfxd_Word); + argu(m, 1, "lo", lo, gfxd_Word); + return 0; +} + +UCFUNC int d_Special2(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_Special2; + argu(m, 0, "hi", getfield(hi, 24, 0), gfxd_Word); + argu(m, 1, "lo", lo, gfxd_Word); + return 0; +} + +UCFUNC int d_Special1(gfxd_macro_t *m, uint32_t hi, uint32_t lo) +{ + m->id = gfxd_Special1; + argu(m, 0, "hi", getfield(hi, 24, 0), gfxd_Word); + argu(m, 1, "lo", lo, gfxd_Word); + return 0; +} +#endif diff --git a/ZAPDTR/lib/libgfxd/uc_macrotbl.c b/ZAPDTR/lib/libgfxd/uc_macrotbl.c new file mode 100644 index 000000000..a8939cede --- /dev/null +++ b/ZAPDTR/lib/libgfxd/uc_macrotbl.c @@ -0,0 +1,1397 @@ +static const gfxd_macro_type_t macro_tbl[] = +{ + [gfxd_Invalid] = + { + .prefix = NULL, + .suffix = NULL, + .opcode = -1, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_Invalid, + }, + [gfxd_DPFillRectangle] = + { + .prefix = NULL, + .suffix = "DPFillRectangle", + .opcode = G_FILLRECT, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_DPFillRectangle, + }, + [gfxd_DPFullSync] = + { + .prefix = NULL, + .suffix = "DPFullSync", + .opcode = G_RDPFULLSYNC, + .n_gfx = 1, + .n_arg = 0, + .disas_fn = d_DPFullSync, + }, + [gfxd_DPLoadSync] = + { + .prefix = NULL, + .suffix = "DPLoadSync", + .opcode = G_RDPLOADSYNC, + .n_gfx = 1, + .n_arg = 0, + .disas_fn = d_DPLoadSync, + }, + [gfxd_DPTileSync] = + { + .prefix = NULL, + .suffix = "DPTileSync", + .opcode = G_RDPTILESYNC, + .n_gfx = 1, + .n_arg = 0, + .disas_fn = d_DPTileSync, + }, + [gfxd_DPPipeSync] = + { + .prefix = NULL, + .suffix = "DPPipeSync", + .opcode = G_RDPPIPESYNC, + .n_gfx = 1, + .n_arg = 0, + .disas_fn = d_DPPipeSync, + }, + [gfxd_DPLoadTLUT_pal16] = + { + .prefix = NULL, + .suffix = "DPLoadTLUT_pal16", + .opcode = G_SETTIMG, + .n_gfx = 6, + .n_arg = 2, + .combine_fn = c_DPLoadTLUT_pal16, + }, + [gfxd_DPLoadTLUT_pal256] = + { + .prefix = NULL, + .suffix = "DPLoadTLUT_pal256", + .opcode = G_SETTIMG, + .n_gfx = 6, + .n_arg = 1, + .combine_fn = c_DPLoadTLUT_pal256, + }, + [gfxd_DPLoadMultiBlockYuvS] = + { + .prefix = NULL, + .suffix = "DPLoadMultiBlockYuvS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 14, + .combine_fn = c_DPLoadMultiBlockYuvS, + .ext = 1, + }, + [gfxd_DPLoadMultiBlockYuv] = + { + .prefix = NULL, + .suffix = "DPLoadMultiBlockYuv", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 14, + .combine_fn = c_DPLoadMultiBlockYuv, + .ext = 1, + }, + [gfxd_DPLoadMultiBlock_4bS] = + { + .prefix = NULL, + .suffix = "DPLoadMultiBlock_4bS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 13, + .combine_fn = c_DPLoadMultiBlock_4bS, + }, + [gfxd_DPLoadMultiBlock_4b] = + { + .prefix = NULL, + .suffix = "DPLoadMultiBlock_4b", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 13, + .combine_fn = c_DPLoadMultiBlock_4b, + }, + [gfxd_DPLoadMultiBlockS] = + { + .prefix = NULL, + .suffix = "DPLoadMultiBlockS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 14, + .combine_fn = c_DPLoadMultiBlockS, + }, + [gfxd_DPLoadMultiBlock] = + { + .prefix = NULL, + .suffix = "DPLoadMultiBlock", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 14, + .combine_fn = c_DPLoadMultiBlock, + }, + [gfxd__DPLoadTextureBlockYuvS] = + { + .prefix = "_", + .suffix = "DPLoadTextureBlockYuvS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 13, + .combine_fn = c__DPLoadTextureBlockYuvS, + .ext = 1, + }, + [gfxd__DPLoadTextureBlockYuv] = + { + .prefix = "_", + .suffix = "DPLoadTextureBlockYuv", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 13, + .combine_fn = c__DPLoadTextureBlockYuv, + .ext = 1, + }, + [gfxd__DPLoadTextureBlock_4bS] = + { + .prefix = "_", + .suffix = "DPLoadTextureBlock_4bS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 12, + .combine_fn = c__DPLoadTextureBlock_4bS, + .ext = 1, + }, + [gfxd__DPLoadTextureBlock_4b] = + { + .prefix = "_", + .suffix = "DPLoadTextureBlock_4b", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 12, + .combine_fn = c__DPLoadTextureBlock_4b, + }, + [gfxd__DPLoadTextureBlockS] = + { + .prefix = "_", + .suffix = "DPLoadTextureBlockS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 13, + .combine_fn = c__DPLoadTextureBlockS, + .ext = 1, + }, + [gfxd__DPLoadTextureBlock] = + { + .prefix = "_", + .suffix = "DPLoadTextureBlock", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 13, + .combine_fn = c__DPLoadTextureBlock, + }, + [gfxd_DPLoadTextureBlockYuvS] = + { + .prefix = NULL, + .suffix = "DPLoadTextureBlockYuvS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 12, + .combine_fn = c_DPLoadTextureBlockYuvS, + .ext = 1, + }, + [gfxd_DPLoadTextureBlockYuv] = + { + .prefix = NULL, + .suffix = "DPLoadTextureBlockYuv", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 12, + .combine_fn = c_DPLoadTextureBlockYuv, + .ext = 1, + }, + [gfxd_DPLoadTextureBlock_4bS] = + { + .prefix = NULL, + .suffix = "DPLoadTextureBlock_4bS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 11, + .combine_fn = c_DPLoadTextureBlock_4bS, + }, + [gfxd_DPLoadTextureBlock_4b] = + { + .prefix = NULL, + .suffix = "DPLoadTextureBlock_4b", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 11, + .combine_fn = c_DPLoadTextureBlock_4b, + }, + [gfxd_DPLoadTextureBlockS] = + { + .prefix = NULL, + .suffix = "DPLoadTextureBlockS", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 12, + .combine_fn = c_DPLoadTextureBlockS, + }, + [gfxd_DPLoadTextureBlock] = + { + .prefix = NULL, + .suffix = "DPLoadTextureBlock", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 12, + .combine_fn = c_DPLoadTextureBlock, + }, + [gfxd_DPLoadMultiTileYuv] = + { + .prefix = NULL, + .suffix = "DPLoadMultiTileYuv", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 18, + .combine_fn = c_DPLoadMultiTileYuv, + .ext = 1, + }, + [gfxd_DPLoadMultiTile_4b] = + { + .prefix = NULL, + .suffix = "DPLoadMultiTile_4b", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 17, + .combine_fn = c_DPLoadMultiTile_4b, + }, + [gfxd_DPLoadMultiTile] = + { + .prefix = NULL, + .suffix = "DPLoadMultiTile", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 18, + .combine_fn = c_DPLoadMultiTile, + }, + [gfxd__DPLoadTextureTileYuv] = + { + .prefix = "_", + .suffix = "DPLoadTextureTileYuv", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 17, + .combine_fn = c__DPLoadTextureTileYuv, + .ext = 1, + }, + [gfxd__DPLoadTextureTile_4b] = + { + .prefix = "_", + .suffix = "DPLoadTextureTile_4b", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 16, + .combine_fn = c__DPLoadTextureTile_4b, + .ext = 1, + }, + [gfxd__DPLoadTextureTile] = + { + .prefix = "_", + .suffix = "DPLoadTextureTile", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 17, + .combine_fn = c__DPLoadTextureTile, + .ext = 1, + }, + [gfxd_DPLoadTextureTileYuv] = + { + .prefix = NULL, + .suffix = "DPLoadTextureTileYuv", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 16, + .combine_fn = c_DPLoadTextureTileYuv, + .ext = 1, + }, + [gfxd_DPLoadTextureTile_4b] = + { + .prefix = NULL, + .suffix = "DPLoadTextureTile_4b", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 15, + .combine_fn = c_DPLoadTextureTile_4b, + }, + [gfxd_DPLoadTextureTile] = + { + .prefix = NULL, + .suffix = "DPLoadTextureTile", + .opcode = G_SETTIMG, + .n_gfx = 7, + .n_arg = 16, + .combine_fn = c_DPLoadTextureTile, + }, + [gfxd_DPLoadBlock] = + { + .prefix = NULL, + .suffix = "DPLoadBlock", + .opcode = G_LOADBLOCK, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_DPLoadBlock, + }, + [gfxd_DPNoOp] = + { + .prefix = NULL, + .suffix = "DPNoOp", + .opcode = G_NOOP, + .n_gfx = 1, + .n_arg = 0, + .alias = gfxd_DPNoOpTag, + }, + [gfxd_DPNoOpTag] = + { + .prefix = NULL, + .suffix = "DPNoOpTag", + .opcode = G_NOOP, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_DPNoOpTag, + }, + [gfxd_DPPipelineMode] = + { + .prefix = NULL, + .suffix = "DPPipelineMode", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetBlendColor] = + { + .prefix = NULL, + .suffix = "DPSetBlendColor", + .opcode = G_SETBLENDCOLOR, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_DPSetBlendColor, + }, + [gfxd_DPSetEnvColor] = + { + .prefix = NULL, + .suffix = "DPSetEnvColor", + .opcode = G_SETENVCOLOR, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_DPSetEnvColor, + }, + [gfxd_DPSetFillColor] = + { + .prefix = NULL, + .suffix = "DPSetFillColor", + .opcode = G_SETFILLCOLOR, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_DPSetFillColor, + }, + [gfxd_DPSetFogColor] = + { + .prefix = NULL, + .suffix = "DPSetFogColor", + .opcode = G_SETFOGCOLOR, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_DPSetFogColor, + }, + [gfxd_DPSetPrimColor] = + { + .prefix = NULL, + .suffix = "DPSetPrimColor", + .opcode = G_SETPRIMCOLOR, + .n_gfx = 1, + .n_arg = 6, + .disas_fn = d_DPSetPrimColor, + }, + [gfxd_DPSetColorImage] = + { + .prefix = NULL, + .suffix = "DPSetColorImage", + .opcode = G_SETCIMG, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_DPSetColorImage, + }, + [gfxd_DPSetDepthImage] = + { + .prefix = NULL, + .suffix = "DPSetDepthImage", + .opcode = G_SETZIMG, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_DPSetDepthImage, + }, + [gfxd_DPSetTextureImage] = + { + .prefix = NULL, + .suffix = "DPSetTextureImage", + .opcode = G_SETTIMG, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_DPSetTextureImage, + }, + [gfxd_DPSetAlphaCompare] = + { + .prefix = NULL, + .suffix = "DPSetAlphaCompare", + .opcode = G_SETOTHERMODE_L, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeLo, + }, + [gfxd_DPSetAlphaDither] = + { + .prefix = NULL, + .suffix = "DPSetAlphaDither", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetColorDither] = + { + .prefix = NULL, + .suffix = "DPSetColorDither", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetCombineMode] = + { + .prefix = NULL, + .suffix = "DPSetCombineMode", + .opcode = G_SETCOMBINE, + .n_gfx = 1, + .n_arg = 2, + .alias = gfxd_DPSetCombineLERP, + }, + [gfxd_DPSetCombineLERP] = + { + .prefix = NULL, + .suffix = "DPSetCombineLERP", + .opcode = G_SETCOMBINE, + .n_gfx = 1, + .n_arg = 16, + .disas_fn = d_DPSetCombineLERP, + }, + [gfxd_DPSetConvert] = + { + .prefix = NULL, + .suffix = "DPSetConvert", + .opcode = G_SETCONVERT, + .n_gfx = 1, + .n_arg = 6, + .disas_fn = d_DPSetConvert, + }, + [gfxd_DPSetTextureConvert] = + { + .prefix = NULL, + .suffix = "DPSetTextureConvert", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetCycleType] = + { + .prefix = NULL, + .suffix = "DPSetCycleType", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetDepthSource] = + { + .prefix = NULL, + .suffix = "DPSetDepthSource", + .opcode = G_SETOTHERMODE_L, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeLo, + }, + [gfxd_DPSetCombineKey] = + { + .prefix = NULL, + .suffix = "DPSetCombineKey", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetKeyGB] = + { + .prefix = NULL, + .suffix = "DPSetKeyGB", + .opcode = G_SETKEYGB, + .n_gfx = 1, + .n_arg = 6, + .disas_fn = d_DPSetKeyGB, + }, + [gfxd_DPSetKeyR] = + { + .prefix = NULL, + .suffix = "DPSetKeyR", + .opcode = G_SETKEYR, + .n_gfx = 1, + .n_arg = 3, + .disas_fn = d_DPSetKeyR, + }, + [gfxd_DPSetPrimDepth] = + { + .prefix = NULL, + .suffix = "DPSetPrimDepth", + .opcode = G_SETPRIMDEPTH, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_DPSetPrimDepth, + }, + [gfxd_DPSetRenderMode] = + { + .prefix = NULL, + .suffix = "DPSetRenderMode", + .opcode = G_SETOTHERMODE_L, + .n_gfx = 1, + .n_arg = 2, + .alias = gfxd_SPSetOtherModeLo, + }, + [gfxd_DPSetScissor] = + { + .prefix = NULL, + .suffix = "DPSetScissor", + .opcode = G_SETSCISSOR, + .n_gfx = 1, + .n_arg = 5, + .alias = gfxd_DPSetScissorFrac, + }, + [gfxd_DPSetScissorFrac] = + { + .prefix = NULL, + .suffix = "DPSetScissorFrac", + .opcode = G_SETSCISSOR, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_DPSetScissorFrac, + }, + [gfxd_DPSetTextureDetail] = + { + .prefix = NULL, + .suffix = "DPSetTextureDetail", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetTextureFilter] = + { + .prefix = NULL, + .suffix = "DPSetTextureFilter", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetTextureLOD] = + { + .prefix = NULL, + .suffix = "DPSetTextureLOD", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetTextureLUT] = + { + .prefix = NULL, + .suffix = "DPSetTextureLUT", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetTexturePersp] = + { + .prefix = NULL, + .suffix = "DPSetTexturePersp", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPSetOtherModeHi, + }, + [gfxd_DPSetTile] = + { + .prefix = NULL, + .suffix = "DPSetTile", + .opcode = G_SETTILE, + .n_gfx = 1, + .n_arg = 12, + .disas_fn = d_DPSetTile, + }, + [gfxd_DPSetTileSize] = + { + .prefix = NULL, + .suffix = "DPSetTileSize", + .opcode = G_SETTILESIZE, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_DPSetTileSize, + }, + [gfxd_SP1Triangle] = + { + .prefix = NULL, + .suffix = "SP1Triangle", + .opcode = G_TRI1, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_SP1Triangle, + }, +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + [gfxd_SP2Triangles] = + { + .prefix = NULL, + .suffix = "SP2Triangles", + .opcode = G_TRI2, + .n_gfx = 1, + .n_arg = 8, + .disas_fn = d_SP2Triangles, + }, +#endif +#if defined(F3DEX_GBI) + [gfxd_SP1Quadrangle] = + { + .prefix = NULL, + .suffix = "SP1Quadrangle", + .opcode = G_TRI2, + .n_gfx = 1, + .n_arg = 5, + .alias = gfxd_SP2Triangles, + }, +#elif defined(F3DEX_GBI_2) + [gfxd_SP1Quadrangle] = + { + .prefix = NULL, + .suffix = "SP1Quadrangle", + .opcode = G_QUAD, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_SP1Quadrangle, + }, +#endif +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + [gfxd_SPBranchLessZraw] = + { + .prefix = NULL, + .suffix = "SPBranchLessZraw", + .opcode = G_RDPHALF_1, + .n_gfx = 2, + .n_arg = 3, + .combine_fn = c_SPBranchLessZraw, + }, +#endif + [gfxd_SPBranchList] = + { + .prefix = NULL, + .suffix = "SPBranchList", + .opcode = G_DL, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_DisplayList, + }, + [gfxd_SPClipRatio] = + { + .prefix = NULL, + .suffix = "SPClipRatio", + .opcode = G_MOVEWORD, + .n_gfx = 4, + .n_arg = 1, + .combine_fn = c_SPClipRatio, + }, + [gfxd_SPCullDisplayList] = + { + .prefix = NULL, + .suffix = "SPCullDisplayList", + .opcode = G_CULLDL, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_SPCullDisplayList, + }, + [gfxd_SPDisplayList] = + { + .prefix = NULL, + .suffix = "SPDisplayList", + .opcode = G_DL, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_DisplayList, + }, + [gfxd_SPEndDisplayList] = + { + .prefix = NULL, + .suffix = "SPEndDisplayList", + .opcode = G_ENDDL, + .n_gfx = 1, + .n_arg = 0, + .disas_fn = d_SPEndDisplayList, + }, + [gfxd_SPFogFactor] = + { + .prefix = NULL, + .suffix = "SPFogFactor", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 2, + .alias = gfxd_SPFogPosition, + }, + [gfxd_SPFogPosition] = + { + .prefix = NULL, + .suffix = "SPFogPosition", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 2, + .alias = gfxd_MoveWd, + }, +#if defined(F3D_GBI) || defined(F3DEX_GBI) + [gfxd_SPForceMatrix] = + { + .prefix = NULL, + .suffix = "SPForceMatrix", + .opcode = G_MOVEMEM, + .n_gfx = 4, + .n_arg = 1, + .combine_fn = c_SPForceMatrix, + }, + [gfxd_SPSetGeometryMode] = + { + .prefix = NULL, + .suffix = "SPSetGeometryMode", + .opcode = G_SETGEOMETRYMODE, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_SPSetGeometryMode, + }, + [gfxd_SPClearGeometryMode] = + { + .prefix = NULL, + .suffix = "SPClearGeometryMode", + .opcode = G_CLEARGEOMETRYMODE, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_SPClearGeometryMode, + }, + [gfxd_SPLoadGeometryMode] = + { + .prefix = NULL, + .suffix = "SPLoadGeometryMode", + .opcode = G_CLEARGEOMETRYMODE, + .n_gfx = 2, + .n_arg = 1, + .combine_fn = c_SPLoadGeometryMode, + }, +#elif defined(F3DEX_GBI_2) + [gfxd_SPForceMatrix] = + { + .prefix = NULL, + .suffix = "SPForceMatrix", + .opcode = G_MOVEMEM, + .n_gfx = 2, + .n_arg = 1, + .combine_fn = c_SPForceMatrix, + }, + [gfxd_SPSetGeometryMode] = + { + .prefix = NULL, + .suffix = "SPSetGeometryMode", + .opcode = G_GEOMETRYMODE, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPGeometryMode, + }, + [gfxd_SPClearGeometryMode] = + { + .prefix = NULL, + .suffix = "SPClearGeometryMode", + .opcode = G_GEOMETRYMODE, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPGeometryMode, + }, + [gfxd_SPLoadGeometryMode] = + { + .prefix = NULL, + .suffix = "SPLoadGeometryMode", + .opcode = G_GEOMETRYMODE, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPGeometryMode, + }, +#endif +#if defined(F3D_GBI) || defined(F3DEX_GBI) + [gfxd_SPInsertMatrix] = + { + .prefix = NULL, + .suffix = "SPInsertMatrix", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 2, + .alias = gfxd_MoveWd, + }, +#endif + [gfxd_SPLine3D] = + { + .prefix = NULL, + .suffix = "SPLine3D", + .opcode = G_LINE3D, + .n_gfx = 1, + .n_arg = 3, + .alias = gfxd_SPLineW3D, + }, + [gfxd_SPLineW3D] = + { + .prefix = NULL, + .suffix = "SPLineW3D", + .opcode = G_LINE3D, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_SPLineW3D, + }, +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + [gfxd_SPLoadUcode] = + { + .prefix = NULL, + .suffix = "SPLoadUcode", + .opcode = G_RDPHALF_1, + .n_gfx = 2, + .n_arg = 2, + .combine_fn = c_SPLoadUcode, + }, +#endif + [gfxd_SPLookAtX] = + { + .prefix = NULL, + .suffix = "SPLookAtX", + .opcode = G_MOVEMEM, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_MoveMem, + }, + [gfxd_SPLookAtY] = + { + .prefix = NULL, + .suffix = "SPLookAtY", + .opcode = G_MOVEMEM, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_MoveMem, + }, + [gfxd_SPLookAt] = + { + .prefix = NULL, + .suffix = "SPLookAt", + .opcode = G_MOVEMEM, + .n_gfx = 2, + .n_arg = 1, + .combine_fn = c_SPLookAt, + }, + [gfxd_SPMatrix] = + { + .prefix = NULL, + .suffix = "SPMatrix", + .opcode = G_MTX, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_SPMatrix, + }, +#if defined(F3D_GBI) || (defined(F3D_BETA) && defined(F3DEX_GBI)) + [gfxd_SPModifyVertex] = + { + .prefix = NULL, + .suffix = "SPModifyVertex", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 3, + .alias = gfxd_MoveWd, + }, +#elif defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + [gfxd_SPModifyVertex] = + { + .prefix = NULL, + .suffix = "SPModifyVertex", + .opcode = G_MODIFYVTX, + .n_gfx = 1, + .n_arg = 3, + .disas_fn = d_SPModifyVertex, + }, +#endif +#if defined(F3D_BETA) && (defined(F3D_GBI) || defined(F3DEX_GBI)) + [gfxd_SPPerspNormalize] = + { + .prefix = NULL, + .suffix = "SPPerspNormalize", + .opcode = G_PERSPNORM, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_SPPerspNormalize, + }, +#else + [gfxd_SPPerspNormalize] = + { + .prefix = NULL, + .suffix = "SPPerspNormalize", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_MoveWd, + }, +#endif +#if defined(F3D_GBI) || defined(F3DEX_GBI) + [gfxd_SPPopMatrix] = + { + .prefix = NULL, + .suffix = "SPPopMatrix", + .opcode = G_POPMTX, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_SPPopMatrix, + }, +#elif defined(F3DEX_GBI_2) + [gfxd_SPPopMatrix] = + { + .prefix = NULL, + .suffix = "SPPopMatrix", + .opcode = G_POPMTX, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_SPPopMatrixN, + }, + [gfxd_SPPopMatrixN] = + { + .prefix = NULL, + .suffix = "SPPopMatrixN", + .opcode = G_POPMTX, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_SPPopMatrixN, + }, +#endif + [gfxd_SPSegment] = + { + .prefix = NULL, + .suffix = "SPSegment", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 2, + .alias = gfxd_MoveWd, + }, + [gfxd_SPSetLights1] = + { + .prefix = NULL, + .suffix = "SPSetLights1", + .opcode = G_MOVEWORD, + .n_gfx = 3, + .n_arg = 1, + .combine_fn = c_SPSetLights1, + }, + [gfxd_SPSetLights2] = + { + .prefix = NULL, + .suffix = "SPSetLights2", + .opcode = G_MOVEWORD, + .n_gfx = 4, + .n_arg = 1, + .combine_fn = c_SPSetLights2, + }, + [gfxd_SPSetLights3] = + { + .prefix = NULL, + .suffix = "SPSetLights3", + .opcode = G_MOVEWORD, + .n_gfx = 5, + .n_arg = 1, + .combine_fn = c_SPSetLights3, + }, + [gfxd_SPSetLights4] = + { + .prefix = NULL, + .suffix = "SPSetLights4", + .opcode = G_MOVEWORD, + .n_gfx = 6, + .n_arg = 1, + .combine_fn = c_SPSetLights4, + }, + [gfxd_SPSetLights5] = + { + .prefix = NULL, + .suffix = "SPSetLights5", + .opcode = G_MOVEWORD, + .n_gfx = 7, + .n_arg = 1, + .combine_fn = c_SPSetLights5, + }, + [gfxd_SPSetLights6] = + { + .prefix = NULL, + .suffix = "SPSetLights6", + .opcode = G_MOVEWORD, + .n_gfx = 8, + .n_arg = 1, + .combine_fn = c_SPSetLights6, + }, + [gfxd_SPSetLights7] = + { + .prefix = NULL, + .suffix = "SPSetLights7", + .opcode = G_MOVEWORD, + .n_gfx = 9, + .n_arg = 1, + .combine_fn = c_SPSetLights7, + }, + [gfxd_SPNumLights] = + { + .prefix = NULL, + .suffix = "SPNumLights", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_MoveWd, + }, + [gfxd_SPLight] = + { + .prefix = NULL, + .suffix = "SPLight", + .opcode = G_MOVEMEM, + .n_gfx = 1, + .n_arg = 2, + .alias = gfxd_MoveMem, + }, + [gfxd_SPLightColor] = + { + .prefix = NULL, + .suffix = "SPLightColor", + .opcode = G_MOVEWORD, + .n_gfx = 2, + .n_arg = 2, + .combine_fn = c_SPLightColor, + }, + [gfxd_SPTexture] = + { + .prefix = NULL, + .suffix = "SPTexture", + .opcode = G_TEXTURE, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_SPTexture, + }, + [gfxd_SPTextureRectangle] = + { + .prefix = NULL, + .suffix = "SPTextureRectangle", + .opcode = G_TEXRECT, + .n_gfx = 3, + .n_arg = 9, + .combine_fn = c_SPTextureRectangle, + }, + [gfxd_SPTextureRectangleFlip] = + { + .prefix = NULL, + .suffix = "SPTextureRectangleFlip", + .opcode = G_TEXRECTFLIP, + .n_gfx = 3, + .n_arg = 9, + .combine_fn = c_SPTextureRectangleFlip, + }, + [gfxd_SPVertex] = + { + .prefix = NULL, + .suffix = "SPVertex", + .opcode = G_VTX, + .n_gfx = 1, + .n_arg = 3, + .disas_fn = d_SPVertex, + }, + [gfxd_SPViewport] = + { + .prefix = NULL, + .suffix = "SPViewport", + .opcode = G_MOVEMEM, + .n_gfx = 1, + .n_arg = 1, + .alias = gfxd_MoveMem, + }, + [gfxd_DPLoadTLUTCmd] = + { + .prefix = NULL, + .suffix = "DPLoadTLUTCmd", + .opcode = G_LOADTLUT, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_DPLoadTLUTCmd, + }, + [gfxd_DPLoadTLUT] = + { + .prefix = NULL, + .suffix = "DPLoadTLUT", + .opcode = G_SETTIMG, + .n_gfx = 6, + .n_arg = 3, + .combine_fn = c_DPLoadTLUT, + }, +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + [gfxd_BranchZ] = + { + .prefix = NULL, + .suffix = "BranchZ", + .opcode = G_BRANCH_Z, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_BranchZ, + .ext = 1, + }, +#endif + [gfxd_DisplayList] = + { + .prefix = NULL, + .suffix = "DisplayList", + .opcode = G_DL, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_DisplayList, + .ext = 1, + }, + [gfxd_DPHalf1] = + { + .prefix = NULL, + .suffix = "DPHalf1", + .opcode = G_RDPHALF_1, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_DPHalf1, + .ext = 1, + }, + [gfxd_DPHalf2] = + { + .prefix = NULL, + .suffix = "DPHalf2", + .opcode = G_RDPHALF_2, + .n_gfx = 1, + .n_arg = 1, + .disas_fn = d_DPHalf2, + .ext = 1, + }, + [gfxd_DPWord] = + { + .prefix = NULL, + .suffix = "DPWord", + .opcode = G_RDPHALF_1, + .n_gfx = 2, + .n_arg = 2, + .combine_fn = c_DPWord, + }, + [gfxd_DPLoadTile] = + { + .prefix = NULL, + .suffix = "DPLoadTile", + .opcode = G_LOADTILE, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_DPLoadTile, + }, +#if defined(F3DEX_GBI_2) + [gfxd_SPGeometryMode] = + { + .prefix = NULL, + .suffix = "SPGeometryMode", + .opcode = G_GEOMETRYMODE, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_SPGeometryMode, + }, +#endif + [gfxd_SPSetOtherMode] = + { + .prefix = NULL, + .suffix = "SPSetOtherMode", + .opcode = -1, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_SPSetOtherMode, + }, + [gfxd_SPSetOtherModeLo] = + { + .prefix = NULL, + .suffix = "SPSetOtherModeLo", + .opcode = G_SETOTHERMODE_L, + .n_gfx = 1, + .n_arg = 3, + .disas_fn = d_SPSetOtherModeLo, + .ext = 1, + }, + [gfxd_SPSetOtherModeHi] = + { + .prefix = NULL, + .suffix = "SPSetOtherModeHi", + .opcode = G_SETOTHERMODE_H, + .n_gfx = 1, + .n_arg = 3, + .disas_fn = d_SPSetOtherModeHi, + .ext = 1, + }, + [gfxd_DPSetOtherMode] = + { + .prefix = NULL, + .suffix = "DPSetOtherMode", + .opcode = G_RDPSETOTHERMODE, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_DPSetOtherMode, + }, + [gfxd_MoveWd] = + { + .prefix = NULL, + .suffix = "MoveWd", + .opcode = G_MOVEWORD, + .n_gfx = 1, + .n_arg = 3, + .disas_fn = d_MoveWd, + }, +#if defined(F3D_GBI) || defined(F3DEX_GBI) + [gfxd_MoveMem] = + { + .prefix = NULL, + .suffix = "MoveMem", + .opcode = G_MOVEMEM, + .n_gfx = 1, + .n_arg = 3, + .disas_fn = d_MoveMem, + .ext = 1, + }, +#elif defined(F3DEX_GBI_2) + [gfxd_MoveMem] = + { + .prefix = NULL, + .suffix = "MoveMem", + .opcode = G_MOVEMEM, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_MoveMem, + .ext = 1, + }, +#endif +#if defined(F3DEX_GBI_2) + [gfxd_SPDma_io] = + { + .prefix = NULL, + .suffix = "SPDma_io", + .opcode = G_DMA_IO, + .n_gfx = 1, + .n_arg = 4, + .disas_fn = d_SPDma_io, + }, + [gfxd_SPDmaRead] = + { + .prefix = NULL, + .suffix = "SPDmaRead", + .opcode = G_DMA_IO, + .n_gfx = 1, + .n_arg = 3, + .alias = gfxd_SPDma_io, + }, + [gfxd_SPDmaWrite] = + { + .prefix = NULL, + .suffix = "SPDmaWrite", + .opcode = G_DMA_IO, + .n_gfx = 1, + .n_arg = 3, + .alias = gfxd_SPDma_io, + }, +#endif +#if defined(F3DEX_GBI) || defined(F3DEX_GBI_2) + [gfxd_LoadUcode] = + { + .prefix = NULL, + .suffix = "LoadUcode", + .opcode = G_LOAD_UCODE, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_LoadUcode, + }, + [gfxd_SPLoadUcodeEx] = + { + .prefix = NULL, + .suffix = "SPLoadUcodeEx", + .opcode = G_RDPHALF_1, + .n_gfx = 2, + .n_arg = 3, + .combine_fn = c_SPLoadUcodeEx, + }, +#endif + [gfxd_TexRect] = + { + .prefix = NULL, + .suffix = "TexRect", + .opcode = G_TEXRECT, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_TexRect, + .ext = 1, + }, + [gfxd_TexRectFlip] = + { + .prefix = NULL, + .suffix = "TexRectFlip", + .opcode = G_TEXRECTFLIP, + .n_gfx = 1, + .n_arg = 5, + .disas_fn = d_TexRectFlip, + .ext = 1, + }, + [gfxd_SPNoOp] = + { + .prefix = NULL, + .suffix = "SPNoOp", + .opcode = G_SPNOOP, + .n_gfx = 1, + .n_arg = 0, + .disas_fn = d_SPNoOp, + }, +#if defined(F3DEX_GBI_2) + [gfxd_Special3] = + { + .prefix = NULL, + .suffix = "Special3", + .opcode = G_SPECIAL_3, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_Special3, + .ext = 1, + }, + [gfxd_Special2] = + { + .prefix = NULL, + .suffix = "Special2", + .opcode = G_SPECIAL_2, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_Special2, + .ext = 1, + }, + [gfxd_Special1] = + { + .prefix = NULL, + .suffix = "Special1", + .opcode = G_SPECIAL_1, + .n_gfx = 1, + .n_arg = 2, + .disas_fn = d_Special1, + .ext = 1, + }, +#endif +}; diff --git a/ZAPDTR/lib/tinyxml2/tinyxml2.cpp b/ZAPDTR/lib/tinyxml2/tinyxml2.cpp new file mode 100644 index 000000000..23a3a6ce4 --- /dev/null +++ b/ZAPDTR/lib/tinyxml2/tinyxml2.cpp @@ -0,0 +1,2837 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml2.h" + +#include // yes, this one new style header, is in the Android SDK. +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +#else +# include +# include +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + // Microsoft Visual Studio, version 2005 and higher. Not WinCE. + /*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... + );*/ + static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) + { + va_list va; + va_start( va, format ); + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; + } + + static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) + { + int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + return result; + } + + #define TIXML_VSCPRINTF _vscprintf + #define TIXML_SSCANF sscanf_s +#elif defined _MSC_VER + // Microsoft Visual Studio 2003 and earlier or WinCE + #define TIXML_SNPRINTF _snprintf + #define TIXML_VSNPRINTF _vsnprintf + #define TIXML_SSCANF sscanf + #if (_MSC_VER < 1400 ) && (!defined WINCE) + // Microsoft Visual Studio 2003 and not WinCE. + #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. + #else + // Microsoft Visual Studio 2003 and earlier or WinCE. + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = 512; + for (;;) { + len = len*2; + char* str = new char[len](); + const int required = _vsnprintf(str, len, format, va); + delete[] str; + if ( required != -1 ) { + TIXMLASSERT( required >= 0 ); + len = required; + break; + } + } + TIXMLASSERT( len >= 0 ); + return len; + } + #endif +#else + // GCC version 3 and higher + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_VSNPRINTF vsnprintf + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = vsnprintf( 0, 0, format, va ); + TIXMLASSERT( len >= 0 ); + return len; + } + #define TIXML_SSCANF sscanf +#endif + + +static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF +static const char LF = LINE_FEED; +static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out +static const char CR = CARRIAGE_RETURN; +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '\"'; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// ef bb bf (Microsoft "lead bytes") - designates UTF-8 + +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +namespace tinyxml2 +{ + +struct Entity { + const char* pattern; + int length; + char value; +}; + +static const int NUM_ENTITIES = 5; +static const Entity entities[NUM_ENTITIES] = { + { "quot", 4, DOUBLE_QUOTE }, + { "amp", 3, '&' }, + { "apos", 4, SINGLE_QUOTE }, + { "lt", 2, '<' }, + { "gt", 2, '>' } +}; + + +StrPair::~StrPair() +{ + Reset(); +} + + +void StrPair::TransferTo( StrPair* other ) +{ + if ( this == other ) { + return; + } + // This in effect implements the assignment operator by "moving" + // ownership (as in auto_ptr). + + TIXMLASSERT( other != 0 ); + TIXMLASSERT( other->_flags == 0 ); + TIXMLASSERT( other->_start == 0 ); + TIXMLASSERT( other->_end == 0 ); + + other->Reset(); + + other->_flags = _flags; + other->_start = _start; + other->_end = _end; + + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::Reset() +{ + if ( _flags & NEEDS_DELETE ) { + delete [] _start; + } + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::SetStr( const char* str, int flags ) +{ + TIXMLASSERT( str ); + Reset(); + size_t len = strlen( str ); + TIXMLASSERT( _start == 0 ); + _start = new char[ len+1 ]; + memcpy( _start, str, len+1 ); + _end = _start + len; + _flags = flags | NEEDS_DELETE; +} + + +char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( endTag && *endTag ); + TIXMLASSERT(curLineNumPtr); + + char* start = p; + char endChar = *endTag; + size_t length = strlen( endTag ); + + // Inner loop of text parsing. + while ( *p ) { + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { + Set( start, p, strFlags ); + return p + length; + } else if (*p == '\n') { + ++(*curLineNumPtr); + } + ++p; + TIXMLASSERT( p ); + } + return 0; +} + + +char* StrPair::ParseName( char* p ) +{ + if ( !p || !(*p) ) { + return 0; + } + if ( !XMLUtil::IsNameStartChar( *p ) ) { + return 0; + } + + char* const start = p; + ++p; + while ( *p && XMLUtil::IsNameChar( *p ) ) { + ++p; + } + + Set( start, p, 0 ); + return p; +} + + +void StrPair::CollapseWhitespace() +{ + // Adjusting _start would cause undefined behavior on delete[] + TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); + // Trim leading space. + _start = XMLUtil::SkipWhiteSpace( _start, 0 ); + + if ( *_start ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( *p ) { + if ( XMLUtil::IsWhiteSpace( *p )) { + p = XMLUtil::SkipWhiteSpace( p, 0 ); + if ( *p == 0 ) { + break; // don't write to q; this trims the trailing space. + } + *q = ' '; + ++q; + } + *q = *p; + ++q; + ++p; + } + *q = 0; + } +} + + +const char* StrPair::GetStr() +{ + TIXMLASSERT( _start ); + TIXMLASSERT( _end ); + if ( _flags & NEEDS_FLUSH ) { + *_end = 0; + _flags ^= NEEDS_FLUSH; + + if ( _flags ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( p < _end ) { + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { + // CR-LF pair becomes LF + // CR alone becomes LF + // LF-CR becomes LF + if ( *(p+1) == LF ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { + if ( *(p+1) == CR ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { + // Entities handled by tinyXML2: + // - special entities in the entity table [in/out] + // - numeric character reference [in] + // 中 or 中 + + if ( *(p+1) == '#' ) { + const int buflen = 10; + char buf[buflen] = { 0 }; + int len = 0; + char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); + if ( adjusted == 0 ) { + *q = *p; + ++p; + ++q; + } + else { + TIXMLASSERT( 0 <= len && len <= buflen ); + TIXMLASSERT( q + len <= adjusted ); + p = adjusted; + memcpy( q, buf, len ); + q += len; + } + } + else { + bool entityFound = false; + for( int i = 0; i < NUM_ENTITIES; ++i ) { + const Entity& entity = entities[i]; + if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 + && *( p + entity.length + 1 ) == ';' ) { + // Found an entity - convert. + *q = entity.value; + ++q; + p += entity.length + 2; + entityFound = true; + break; + } + } + if ( !entityFound ) { + // fixme: treat as error? + ++p; + ++q; + } + } + } + else { + *q = *p; + ++p; + ++q; + } + } + *q = 0; + } + // The loop below has plenty going on, and this + // is a less useful mode. Break it out. + if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { + CollapseWhitespace(); + } + _flags = (_flags & NEEDS_DELETE); + } + TIXMLASSERT( _start ); + return _start; +} + + + + +// --------- XMLUtil ----------- // + +const char* XMLUtil::writeBoolTrue = "true"; +const char* XMLUtil::writeBoolFalse = "false"; + +void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse) +{ + static const char* defTrue = "true"; + static const char* defFalse = "false"; + + writeBoolTrue = (writeTrue) ? writeTrue : defTrue; + writeBoolFalse = (writeFalse) ? writeFalse : defFalse; +} + + +const char* XMLUtil::ReadBOM( const char* p, bool* bom ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( bom ); + *bom = false; + const unsigned char* pu = reinterpret_cast(p); + // Check for BOM: + if ( *(pu+0) == TIXML_UTF_LEAD_0 + && *(pu+1) == TIXML_UTF_LEAD_1 + && *(pu+2) == TIXML_UTF_LEAD_2 ) { + *bom = true; + p += 3; + } + TIXMLASSERT( p ); + return p; +} + + +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) { + *length = 1; + } + else if ( input < 0x800 ) { + *length = 2; + } + else if ( input < 0x10000 ) { + *length = 3; + } + else if ( input < 0x200000 ) { + *length = 4; + } + else { + *length = 0; // This code won't convert this correctly anyway. + return; + } + + output += *length; + + // Scary scary fall throughs are annotated with carefully designed comments + // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc + switch (*length) { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + break; + default: + TIXMLASSERT( false ); + } +} + + +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) +{ + // Presume an entity, and pull it out. + *length = 0; + + if ( *(p+1) == '#' && *(p+2) ) { + unsigned long ucs = 0; + TIXMLASSERT( sizeof( ucs ) >= 4 ); + ptrdiff_t delta = 0; + unsigned mult = 1; + static const char SEMICOLON = ';'; + + if ( *(p+2) == 'x' ) { + // Hexadecimal. + const char* q = p+3; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != 'x' ) { + unsigned int digit = 0; + + if ( *q >= '0' && *q <= '9' ) { + digit = *q - '0'; + } + else if ( *q >= 'a' && *q <= 'f' ) { + digit = *q - 'a' + 10; + } + else if ( *q >= 'A' && *q <= 'F' ) { + digit = *q - 'A' + 10; + } + else { + return 0; + } + TIXMLASSERT( digit < 16 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + TIXMLASSERT( mult <= UINT_MAX / 16 ); + mult *= 16; + --q; + } + } + else { + // Decimal. + const char* q = p+2; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != '#' ) { + if ( *q >= '0' && *q <= '9' ) { + const unsigned int digit = *q - '0'; + TIXMLASSERT( digit < 10 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + } + else { + return 0; + } + TIXMLASSERT( mult <= UINT_MAX / 10 ); + mult *= 10; + --q; + } + } + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + return p + delta + 1; + } + return p+1; +} + + +void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); +} + + +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); +} + + +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse); +} + +/* + ToStr() of a number is a very tricky topic. + https://github.com/leethomason/tinyxml2/issues/106 +*/ +void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); +} + + +void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); +} + + +void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) +{ + // horrible syntax trick to make the compiler happy about %lld + TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); +} + + +bool XMLUtil::ToInt( const char* str, int* value ) +{ + if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) +{ + if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { + return true; + } + return false; +} + +bool XMLUtil::ToBool( const char* str, bool* value ) +{ + int ival = 0; + if ( ToInt( str, &ival )) { + *value = (ival==0) ? false : true; + return true; + } + if ( StringEqual( str, "true" ) ) { + *value = true; + return true; + } + else if ( StringEqual( str, "false" ) ) { + *value = false; + return true; + } + return false; +} + + +bool XMLUtil::ToFloat( const char* str, float* value ) +{ + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToDouble( const char* str, double* value ) +{ + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToInt64(const char* str, int64_t* value) +{ + long long v = 0; // horrible syntax trick to make the compiler happy about %lld + if (TIXML_SSCANF(str, "%lld", &v) == 1) { + *value = (int64_t)v; + return true; + } + return false; +} + + +char* XMLDocument::Identify( char* p, XMLNode** node ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( p ); + char* const start = p; + int const startLine = _parseCurLineNum; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + if( !*p ) { + *node = 0; + TIXMLASSERT( p ); + return p; + } + + // These strings define the matching patterns: + static const char* xmlHeader = { "( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += xmlHeaderLen; + } + else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += commentHeaderLen; + } + else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { + XMLText* text = CreateUnlinkedNode( _textPool ); + returnNode = text; + returnNode->_parseLineNum = _parseCurLineNum; + p += cdataHeaderLen; + text->SetCData( true ); + } + else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += dtdHeaderLen; + } + else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _elementPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += elementHeaderLen; + } + else { + returnNode = CreateUnlinkedNode( _textPool ); + returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character + p = start; // Back it up, all the text counts. + _parseCurLineNum = startLine; + } + + TIXMLASSERT( returnNode ); + TIXMLASSERT( p ); + *node = returnNode; + return p; +} + + +bool XMLDocument::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLNode ----------- // + +XMLNode::XMLNode( XMLDocument* doc ) : + _document( doc ), + _parent( 0 ), + _value(), + _parseLineNum( 0 ), + _firstChild( 0 ), _lastChild( 0 ), + _prev( 0 ), _next( 0 ), + _userData( 0 ), + _memPool( 0 ) +{ +} + + +XMLNode::~XMLNode() +{ + DeleteChildren(); + if ( _parent ) { + _parent->Unlink( this ); + } +} + +const char* XMLNode::Value() const +{ + // Edge case: XMLDocuments don't have a Value. Return null. + if ( this->ToDocument() ) + return 0; + return _value.GetStr(); +} + +void XMLNode::SetValue( const char* str, bool staticMem ) +{ + if ( staticMem ) { + _value.SetInternedStr( str ); + } + else { + _value.SetStr( str ); + } +} + +XMLNode* XMLNode::DeepClone(XMLDocument* target) const +{ + XMLNode* clone = this->ShallowClone(target); + if (!clone) return 0; + + for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { + XMLNode* childClone = child->DeepClone(target); + TIXMLASSERT(childClone); + clone->InsertEndChild(childClone); + } + return clone; +} + +void XMLNode::DeleteChildren() +{ + while( _firstChild ) { + TIXMLASSERT( _lastChild ); + DeleteChild( _firstChild ); + } + _firstChild = _lastChild = 0; +} + + +void XMLNode::Unlink( XMLNode* child ) +{ + TIXMLASSERT( child ); + TIXMLASSERT( child->_document == _document ); + TIXMLASSERT( child->_parent == this ); + if ( child == _firstChild ) { + _firstChild = _firstChild->_next; + } + if ( child == _lastChild ) { + _lastChild = _lastChild->_prev; + } + + if ( child->_prev ) { + child->_prev->_next = child->_next; + } + if ( child->_next ) { + child->_next->_prev = child->_prev; + } + child->_next = 0; + child->_prev = 0; + child->_parent = 0; +} + + +void XMLNode::DeleteChild( XMLNode* node ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( node->_document == _document ); + TIXMLASSERT( node->_parent == this ); + Unlink( node ); + TIXMLASSERT(node->_prev == 0); + TIXMLASSERT(node->_next == 0); + TIXMLASSERT(node->_parent == 0); + DeleteNode( node ); +} + + +XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _lastChild ) { + TIXMLASSERT( _firstChild ); + TIXMLASSERT( _lastChild->_next == 0 ); + _lastChild->_next = addThis; + addThis->_prev = _lastChild; + _lastChild = addThis; + + addThis->_next = 0; + } + else { + TIXMLASSERT( _firstChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _firstChild ) { + TIXMLASSERT( _lastChild ); + TIXMLASSERT( _firstChild->_prev == 0 ); + + _firstChild->_prev = addThis; + addThis->_next = _firstChild; + _firstChild = addThis; + + addThis->_prev = 0; + } + else { + TIXMLASSERT( _lastChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + + TIXMLASSERT( afterThis ); + + if ( afterThis->_parent != this ) { + TIXMLASSERT( false ); + return 0; + } + if ( afterThis == addThis ) { + // Current state: BeforeThis -> AddThis -> OneAfterAddThis + // Now AddThis must disappear from it's location and then + // reappear between BeforeThis and OneAfterAddThis. + // So just leave it where it is. + return addThis; + } + + if ( afterThis->_next == 0 ) { + // The last node or the only node. + return InsertEndChild( addThis ); + } + InsertChildPreamble( addThis ); + addThis->_prev = afterThis; + addThis->_next = afterThis->_next; + afterThis->_next->_prev = addThis; + afterThis->_next = addThis; + addThis->_parent = this; + return addThis; +} + + + + +const XMLElement* XMLNode::FirstChildElement( const char* name ) const +{ + for( const XMLNode* node = _firstChild; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::LastChildElement( const char* name ) const +{ + for( const XMLNode* node = _lastChild; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::NextSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _next; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _prev; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) +{ + // This is a recursive method, but thinking about it "at the current level" + // it is a pretty simple flat list: + // + // + // + // With a special case: + // + // + // + // + // Where the closing element (/foo) *must* be the next thing after the opening + // element, and the names must match. BUT the tricky bit is that the closing + // element will be read by the child. + // + // 'endTag' is the end tag for this node, it is returned by a call to a child. + // 'parentEnd' is the end tag for the parent, which is filled in and returned. + + XMLDocument::DepthTracker tracker(_document); + if (_document->Error()) + return 0; + + while( p && *p ) { + XMLNode* node = 0; + + p = _document->Identify( p, &node ); + TIXMLASSERT( p ); + if ( node == 0 ) { + break; + } + + int initialLineNum = node->_parseLineNum; + + StrPair endTag; + p = node->ParseDeep( p, &endTag, curLineNumPtr ); + if ( !p ) { + DeleteNode( node ); + if ( !_document->Error() ) { + _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); + } + break; + } + + XMLDeclaration* decl = node->ToDeclaration(); + if ( decl ) { + // Declarations are only allowed at document level + // + // Multiple declarations are allowed but all declarations + // must occur before anything else. + // + // Optimized due to a security test case. If the first node is + // a declaration, and the last node is a declaration, then only + // declarations have so far been addded. + bool wellLocated = false; + + if (ToDocument()) { + if (FirstChild()) { + wellLocated = + FirstChild() && + FirstChild()->ToDeclaration() && + LastChild() && + LastChild()->ToDeclaration(); + } + else { + wellLocated = true; + } + } + if ( !wellLocated ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value()); + DeleteNode( node ); + break; + } + } + + XMLElement* ele = node->ToElement(); + if ( ele ) { + // We read the end tag. Return it to the parent. + if ( ele->ClosingType() == XMLElement::CLOSING ) { + if ( parentEndTag ) { + ele->_value.TransferTo( parentEndTag ); + } + node->_memPool->SetTracked(); // created and then immediately deleted. + DeleteNode( node ); + return p; + } + + // Handle an end tag returned to this level. + // And handle a bunch of annoying errors. + bool mismatch = false; + if ( endTag.Empty() ) { + if ( ele->ClosingType() == XMLElement::OPEN ) { + mismatch = true; + } + } + else { + if ( ele->ClosingType() != XMLElement::OPEN ) { + mismatch = true; + } + else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { + mismatch = true; + } + } + if ( mismatch ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name()); + DeleteNode( node ); + break; + } + } + InsertEndChild( node ); + } + return 0; +} + +/*static*/ void XMLNode::DeleteNode( XMLNode* node ) +{ + if ( node == 0 ) { + return; + } + TIXMLASSERT(node->_document); + if (!node->ToDocument()) { + node->_document->MarkInUse(node); + } + + MemPool* pool = node->_memPool; + node->~XMLNode(); + pool->Free( node ); +} + +void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const +{ + TIXMLASSERT( insertThis ); + TIXMLASSERT( insertThis->_document == _document ); + + if (insertThis->_parent) { + insertThis->_parent->Unlink( insertThis ); + } + else { + insertThis->_document->MarkInUse(insertThis); + insertThis->_memPool->SetTracked(); + } +} + +const XMLElement* XMLNode::ToElementWithName( const char* name ) const +{ + const XMLElement* element = this->ToElement(); + if ( element == 0 ) { + return 0; + } + if ( name == 0 ) { + return element; + } + if ( XMLUtil::StringEqual( element->Name(), name ) ) { + return element; + } + return 0; +} + +// --------- XMLText ---------- // +char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + if ( this->CData() ) { + p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 ); + } + return p; + } + else { + int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; + if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { + flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; + } + + p = _value.ParseText( p, "<", flags, curLineNumPtr ); + if ( p && *p ) { + return p-1; + } + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 ); + } + } + return 0; +} + + +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? + text->SetCData( this->CData() ); + return text; +} + + +bool XMLText::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLText* text = compare->ToText(); + return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); +} + + +bool XMLText::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLComment ---------- // + +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLComment::~XMLComment() +{ +} + + +char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Comment parses as text. + p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? + return comment; +} + + +bool XMLComment::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLComment* comment = compare->ToComment(); + return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); +} + + +bool XMLComment::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLDeclaration ---------- // + +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLDeclaration::~XMLDeclaration() +{ + //printf( "~XMLDeclaration\n" ); +} + + +char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Declaration parses as text. + p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? + return dec; +} + + +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLDeclaration* declaration = compare->ToDeclaration(); + return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); +} + + + +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLUnknown ---------- // + +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLUnknown::~XMLUnknown() +{ +} + + +char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Unknown parses as text. + p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? + return text; +} + + +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLUnknown* unknown = compare->ToUnknown(); + return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); +} + + +bool XMLUnknown::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLAttribute ---------- // + +const char* XMLAttribute::Name() const +{ + return _name.GetStr(); +} + +const char* XMLAttribute::Value() const +{ + return _value.GetStr(); +} + +char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) +{ + // Parse using the name rules: bug fix, was using ParseText before + p = _name.ParseName( p ); + if ( !p || !*p ) { + return 0; + } + + // Skip white space before = + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '=' ) { + return 0; + } + + ++p; // move up to opening quote + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '\"' && *p != '\'' ) { + return 0; + } + + char endTag[2] = { *p, 0 }; + ++p; // move past opening quote + + p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); + return p; +} + + +void XMLAttribute::SetName( const char* n ) +{ + _name.SetStr( n ); +} + + +XMLError XMLAttribute::QueryIntValue( int* value ) const +{ + if ( XMLUtil::ToInt( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const +{ + if ( XMLUtil::ToUnsigned( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryInt64Value(int64_t* value) const +{ + if (XMLUtil::ToInt64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryBoolValue( bool* value ) const +{ + if ( XMLUtil::ToBool( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryFloatValue( float* value ) const +{ + if ( XMLUtil::ToFloat( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryDoubleValue( double* value ) const +{ + if ( XMLUtil::ToDouble( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +void XMLAttribute::SetAttribute( const char* v ) +{ + _value.SetStr( v ); +} + + +void XMLAttribute::SetAttribute( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} + + + +void XMLAttribute::SetAttribute( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +// --------- XMLElement ---------- // +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), + _closingType( OPEN ), + _rootAttribute( 0 ) +{ +} + + +XMLElement::~XMLElement() +{ + while( _rootAttribute ) { + XMLAttribute* next = _rootAttribute->_next; + DeleteAttribute( _rootAttribute ); + _rootAttribute = next; + } +} + + +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const +{ + for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const char* XMLElement::Attribute( const char* name, const char* value ) const +{ + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return 0; + } + if ( !value || XMLUtil::StringEqual( a->Value(), value )) { + return a->Value(); + } + return 0; +} + +int XMLElement::IntAttribute(const char* name, int defaultValue) const +{ + int i = defaultValue; + QueryIntAttribute(name, &i); + return i; +} + +unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedAttribute(name, &i); + return i; +} + +int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Attribute(name, &i); + return i; +} + +bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolAttribute(name, &b); + return b; +} + +double XMLElement::DoubleAttribute(const char* name, double defaultValue) const +{ + double d = defaultValue; + QueryDoubleAttribute(name, &d); + return d; +} + +float XMLElement::FloatAttribute(const char* name, float defaultValue) const +{ + float f = defaultValue; + QueryFloatAttribute(name, &f); + return f; +} + +const char* XMLElement::GetText() const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + return FirstChild()->Value(); + } + return 0; +} + + +void XMLElement::SetText( const char* inText ) +{ + if ( FirstChild() && FirstChild()->ToText() ) + FirstChild()->SetValue( inText ); + else { + XMLText* theText = GetDocument()->NewText( inText ); + InsertFirstChild( theText ); + } +} + + +void XMLElement::SetText( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + + +void XMLElement::SetText( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +XMLError XMLElement::QueryIntText( int* ival ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToInt( t, ival ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToUnsigned( t, uval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryInt64Text(int64_t* ival) const +{ + if (FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if (XMLUtil::ToInt64(t, ival)) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryBoolText( bool* bval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToBool( t, bval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryDoubleText( double* dval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToDouble( t, dval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryFloatText( float* fval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToFloat( t, fval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + +int XMLElement::IntText(int defaultValue) const +{ + int i = defaultValue; + QueryIntText(&i); + return i; +} + +unsigned XMLElement::UnsignedText(unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedText(&i); + return i; +} + +int64_t XMLElement::Int64Text(int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Text(&i); + return i; +} + +bool XMLElement::BoolText(bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolText(&b); + return b; +} + +double XMLElement::DoubleText(double defaultValue) const +{ + double d = defaultValue; + QueryDoubleText(&d); + return d; +} + +float XMLElement::FloatText(float defaultValue) const +{ + float f = defaultValue; + QueryFloatText(&f); + return f; +} + + +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) +{ + XMLAttribute* last = 0; + XMLAttribute* attrib = 0; + for( attrib = _rootAttribute; + attrib; + last = attrib, attrib = attrib->_next ) { + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { + break; + } + } + if ( !attrib ) { + attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + if ( last ) { + TIXMLASSERT( last->_next == 0 ); + last->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + attrib->SetName( name ); + } + return attrib; +} + + +void XMLElement::DeleteAttribute( const char* name ) +{ + XMLAttribute* prev = 0; + for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { + if ( XMLUtil::StringEqual( name, a->Name() ) ) { + if ( prev ) { + prev->_next = a->_next; + } + else { + _rootAttribute = a->_next; + } + DeleteAttribute( a ); + break; + } + prev = a; + } +} + + +char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) +{ + XMLAttribute* prevAttribute = 0; + + // Read the attributes. + while( p ) { + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( !(*p) ) { + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() ); + return 0; + } + + // attribute. + if (XMLUtil::IsNameStartChar( *p ) ) { + XMLAttribute* attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + attrib->_parseLineNum = _document->_parseCurLineNum; + + int attrLineNum = attrib->_parseLineNum; + + p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); + if ( !p || Attribute( attrib->Name() ) ) { + DeleteAttribute( attrib ); + _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() ); + return 0; + } + // There is a minor bug here: if the attribute in the source xml + // document is duplicated, it will not be detected and the + // attribute will be doubly added. However, tracking the 'prevAttribute' + // avoids re-scanning the attribute list. Preferring performance for + // now, may reconsider in the future. + if ( prevAttribute ) { + TIXMLASSERT( prevAttribute->_next == 0 ); + prevAttribute->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + prevAttribute = attrib; + } + // end of the tag + else if ( *p == '>' ) { + ++p; + break; + } + // end of the tag + else if ( *p == '/' && *(p+1) == '>' ) { + _closingType = CLOSED; + return p+2; // done; sealed element. + } + else { + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 ); + return 0; + } + } + return p; +} + +void XMLElement::DeleteAttribute( XMLAttribute* attribute ) +{ + if ( attribute == 0 ) { + return; + } + MemPool* pool = attribute->_memPool; + attribute->~XMLAttribute(); + pool->Free( attribute ); +} + +XMLAttribute* XMLElement::CreateAttribute() +{ + TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); + XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + TIXMLASSERT( attrib ); + attrib->_memPool = &_document->_attributePool; + attrib->_memPool->SetTracked(); + return attrib; +} + +// +// +// foobar +// +char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) +{ + // Read the element name. + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + + // The closing element is the form. It is + // parsed just like a regular element then deleted from + // the DOM. + if ( *p == '/' ) { + _closingType = CLOSING; + ++p; + } + + p = _value.ParseName( p ); + if ( _value.Empty() ) { + return 0; + } + + p = ParseAttributes( p, curLineNumPtr ); + if ( !p || !*p || _closingType != OPEN ) { + return p; + } + + p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); + return p; +} + + + +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? + for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { + element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? + } + return element; +} + + +bool XMLElement::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLElement* other = compare->ToElement(); + if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { + + const XMLAttribute* a=FirstAttribute(); + const XMLAttribute* b=other->FirstAttribute(); + + while ( a && b ) { + if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { + return false; + } + a = a->Next(); + b = b->Next(); + } + if ( a || b ) { + // different count + return false; + } + return true; + } + return false; +} + + +bool XMLElement::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this, _rootAttribute ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLDocument ----------- // + +// Warning: List must match 'enum XMLError' +const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { + "XML_SUCCESS", + "XML_NO_ATTRIBUTE", + "XML_WRONG_ATTRIBUTE_TYPE", + "XML_ERROR_FILE_NOT_FOUND", + "XML_ERROR_FILE_COULD_NOT_BE_OPENED", + "XML_ERROR_FILE_READ_ERROR", + "XML_ERROR_PARSING_ELEMENT", + "XML_ERROR_PARSING_ATTRIBUTE", + "XML_ERROR_PARSING_TEXT", + "XML_ERROR_PARSING_CDATA", + "XML_ERROR_PARSING_COMMENT", + "XML_ERROR_PARSING_DECLARATION", + "XML_ERROR_PARSING_UNKNOWN", + "XML_ERROR_EMPTY_DOCUMENT", + "XML_ERROR_MISMATCHED_ELEMENT", + "XML_ERROR_PARSING", + "XML_CAN_NOT_CONVERT_TEXT", + "XML_NO_TEXT_NODE", + "XML_ELEMENT_DEPTH_EXCEEDED" +}; + + +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : + XMLNode( 0 ), + _writeBOM( false ), + _processEntities( processEntities ), + _errorID(XML_SUCCESS), + _whitespaceMode( whitespaceMode ), + _errorStr(), + _errorLineNum( 0 ), + _charBuffer( 0 ), + _parseCurLineNum( 0 ), + _parsingDepth(0), + _unlinked(), + _elementPool(), + _attributePool(), + _textPool(), + _commentPool() +{ + // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) + _document = this; +} + + +XMLDocument::~XMLDocument() +{ + Clear(); +} + + +void XMLDocument::MarkInUse(XMLNode* node) +{ + TIXMLASSERT(node); + TIXMLASSERT(node->_parent == 0); + + for (int i = 0; i < _unlinked.Size(); ++i) { + if (node == _unlinked[i]) { + _unlinked.SwapRemove(i); + break; + } + } +} + +void XMLDocument::Clear() +{ + DeleteChildren(); + while( _unlinked.Size()) { + DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. + } + +#ifdef TINYXML2_DEBUG + const bool hadError = Error(); +#endif + ClearError(); + + delete [] _charBuffer; + _charBuffer = 0; + _parsingDepth = 0; + +#if 0 + _textPool.Trace( "text" ); + _elementPool.Trace( "element" ); + _commentPool.Trace( "comment" ); + _attributePool.Trace( "attribute" ); +#endif + +#ifdef TINYXML2_DEBUG + if ( !hadError ) { + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); + } +#endif +} + + +void XMLDocument::DeepCopy(XMLDocument* target) const +{ + TIXMLASSERT(target); + if (target == this) { + return; // technically success - a no-op. + } + + target->Clear(); + for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { + target->InsertEndChild(node->DeepClone(target)); + } +} + +XMLElement* XMLDocument::NewElement( const char* name ) +{ + XMLElement* ele = CreateUnlinkedNode( _elementPool ); + ele->SetName( name ); + return ele; +} + + +XMLComment* XMLDocument::NewComment( const char* str ) +{ + XMLComment* comment = CreateUnlinkedNode( _commentPool ); + comment->SetValue( str ); + return comment; +} + + +XMLText* XMLDocument::NewText( const char* str ) +{ + XMLText* text = CreateUnlinkedNode( _textPool ); + text->SetValue( str ); + return text; +} + + +XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) +{ + XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); + return dec; +} + + +XMLUnknown* XMLDocument::NewUnknown( const char* str ) +{ + XMLUnknown* unk = CreateUnlinkedNode( _commentPool ); + unk->SetValue( str ); + return unk; +} + +static FILE* callfopen( const char* filepath, const char* mode ) +{ + TIXMLASSERT( filepath ); + TIXMLASSERT( mode ); +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filepath, mode ); + if ( err ) { + return 0; + } +#else + FILE* fp = fopen( filepath, mode ); +#endif + return fp; +} + +void XMLDocument::DeleteNode( XMLNode* node ) { + TIXMLASSERT( node ); + TIXMLASSERT(node->_document == this ); + if (node->_parent) { + node->_parent->DeleteChild( node ); + } + else { + // Isn't in the tree. + // Use the parent delete. + // Also, we need to mark it tracked: we 'know' + // it was never used. + node->_memPool->SetTracked(); + // Call the static XMLNode version: + XMLNode::DeleteNode(node); + } +} + + +XMLError XMLDocument::LoadFile( const char* filename ) +{ + if ( !filename ) { + TIXMLASSERT( false ); + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); + return _errorID; + } + + Clear(); + FILE* fp = callfopen( filename, "rb" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ); + return _errorID; + } + LoadFile( fp ); + fclose( fp ); + return _errorID; +} + +// This is likely overengineered template art to have a check that unsigned long value incremented +// by one still fits into size_t. If size_t type is larger than unsigned long type +// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit +// -Wtype-limits warning. This piece makes the compiler select code with a check when a check +// is useful and code with no check when a check is redundant depending on how size_t and unsigned long +// types sizes relate to each other. +template += sizeof(size_t))> +struct LongFitsIntoSizeTMinusOne { + static bool Fits( unsigned long value ) + { + return value < (size_t)-1; + } +}; + +template <> +struct LongFitsIntoSizeTMinusOne { + static bool Fits( unsigned long ) + { + return true; + } +}; + +XMLError XMLDocument::LoadFile( FILE* fp ) +{ + Clear(); + + fseek( fp, 0, SEEK_SET ); + if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + fseek( fp, 0, SEEK_END ); + const long filelength = ftell( fp ); + fseek( fp, 0, SEEK_SET ); + if ( filelength == -1L ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + TIXMLASSERT( filelength >= 0 ); + + if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { + // Cannot handle files which won't fit in buffer together with null terminator + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + if ( filelength == 0 ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + const size_t size = filelength; + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[size+1]; + size_t read = fread( _charBuffer, 1, size, fp ); + if ( read != size ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + _charBuffer[size] = 0; + + Parse(); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( const char* filename, bool compact ) +{ + if ( !filename ) { + TIXMLASSERT( false ); + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); + return _errorID; + } + + FILE* fp = callfopen( filename, "w" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ); + return _errorID; + } + SaveFile(fp, compact); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) +{ + // Clear any error from the last save, otherwise it will get reported + // for *this* call. + ClearError(); + XMLPrinter stream( fp, compact ); + Print( &stream ); + return _errorID; +} + + +XMLError XMLDocument::Parse( const char* p, size_t len ) +{ + Clear(); + + if ( len == 0 || !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + if ( len == (size_t)(-1) ) { + len = strlen( p ); + } + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[ len+1 ]; + memcpy( _charBuffer, p, len ); + _charBuffer[len] = 0; + + Parse(); + if ( Error() ) { + // clean up now essentially dangling memory. + // and the parse fail can put objects in the + // pools that are dead and inaccessible. + DeleteChildren(); + _elementPool.Clear(); + _attributePool.Clear(); + _textPool.Clear(); + _commentPool.Clear(); + } + return _errorID; +} + + +void XMLDocument::Print( XMLPrinter* streamer ) const +{ + if ( streamer ) { + Accept( streamer ); + } + else { + XMLPrinter stdoutStreamer( stdout ); + Accept( &stdoutStreamer ); + } +} + + +void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... ) +{ + TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); + _errorID = error; + _errorLineNum = lineNum; + _errorStr.Reset(); + + size_t BUFFER_SIZE = 1000; + char* buffer = new char[BUFFER_SIZE]; + + TIXMLASSERT(sizeof(error) <= sizeof(int)); + TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum); + + if (format) { + size_t len = strlen(buffer); + TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": "); + len = strlen(buffer); + + va_list va; + va_start(va, format); + TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va); + va_end(va); + } + _errorStr.SetStr(buffer); + delete[] buffer; +} + + +/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) +{ + TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); + const char* errorName = _errorNames[errorID]; + TIXMLASSERT( errorName && errorName[0] ); + return errorName; +} + +const char* XMLDocument::ErrorStr() const +{ + return _errorStr.Empty() ? "" : _errorStr.GetStr(); +} + + +void XMLDocument::PrintError() const +{ + printf("%s\n", ErrorStr()); +} + +const char* XMLDocument::ErrorName() const +{ + return ErrorIDToName(_errorID); +} + +void XMLDocument::Parse() +{ + TIXMLASSERT( NoChildren() ); // Clear() must have been called previously + TIXMLASSERT( _charBuffer ); + _parseCurLineNum = 1; + _parseLineNum = 1; + char* p = _charBuffer; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); + if ( !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return; + } + ParseDeep(p, 0, &_parseCurLineNum ); +} + +void XMLDocument::PushDepth() +{ + _parsingDepth++; + if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) { + SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." ); + } +} + +void XMLDocument::PopDepth() +{ + TIXMLASSERT(_parsingDepth > 0); + --_parsingDepth; +} + +XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : + _elementJustOpened( false ), + _stack(), + _firstElement( true ), + _fp( file ), + _depth( depth ), + _textDepth( -1 ), + _processEntities( true ), + _compactMode( compact ), + _buffer() +{ + for( int i=0; i'] = true; // not required, but consistency is nice + _buffer.Push( 0 ); +} + + +void XMLPrinter::Print( const char* format, ... ) +{ + va_list va; + va_start( va, format ); + + if ( _fp ) { + vfprintf( _fp, format, va ); + } + else { + const int len = TIXML_VSCPRINTF( format, va ); + // Close out and re-start the va-args + va_end( va ); + TIXMLASSERT( len >= 0 ); + va_start( va, format ); + TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); + char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. + TIXML_VSNPRINTF( p, len+1, format, va ); + } + va_end( va ); +} + + +void XMLPrinter::Write( const char* data, size_t size ) +{ + if ( _fp ) { + fwrite ( data , sizeof(char), size, _fp); + } + else { + char* p = _buffer.PushArr( static_cast(size) ) - 1; // back up over the null terminator. + memcpy( p, data, size ); + p[size] = 0; + } +} + + +void XMLPrinter::Putc( char ch ) +{ + if ( _fp ) { + fputc ( ch, _fp); + } + else { + char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator. + p[0] = ch; + p[1] = 0; + } +} + + +void XMLPrinter::PrintSpace( int depth ) +{ + for( int i=0; i 0 && *q < ENTITY_RANGE ) { + // Check for entities. If one is found, flush + // the stream up until the entity, write the + // entity, and keep looking. + if ( flag[(unsigned char)(*q)] ) { + while ( p < q ) { + const size_t delta = q - p; + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; + Write( p, toPrint ); + p += toPrint; + } + bool entityPatternPrinted = false; + for( int i=0; i( bom ) ); + } + if ( writeDec ) { + PushDeclaration( "xml version=\"1.0\"" ); + } +} + + +void XMLPrinter::OpenElement( const char* name, bool compactMode ) +{ + SealElementIfJustOpened(); + _stack.Push( name ); + + if ( _textDepth < 0 && !_firstElement && !compactMode ) { + Putc( '\n' ); + } + if ( !compactMode ) { + PrintSpace( _depth ); + } + + Write ( "<" ); + Write ( name ); + + _elementJustOpened = true; + _firstElement = false; + ++_depth; +} + + +void XMLPrinter::PushAttribute( const char* name, const char* value ) +{ + TIXMLASSERT( _elementJustOpened ); + Putc ( ' ' ); + Write( name ); + Write( "=\"" ); + PrintString( value, false ); + Putc ( '\"' ); +} + + +void XMLPrinter::PushAttribute( const char* name, int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute(const char* name, int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + PushAttribute(name, buf); +} + + +void XMLPrinter::PushAttribute( const char* name, bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::CloseElement( bool compactMode ) +{ + --_depth; + const char* name = _stack.Pop(); + + if ( _elementJustOpened ) { + Write( "/>" ); + } + else { + if ( _textDepth < 0 && !compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + Write ( "" ); + } + + if ( _textDepth == _depth ) { + _textDepth = -1; + } + if ( _depth == 0 && !compactMode) { + Putc( '\n' ); + } + _elementJustOpened = false; +} + + +void XMLPrinter::SealElementIfJustOpened() +{ + if ( !_elementJustOpened ) { + return; + } + _elementJustOpened = false; + Putc( '>' ); +} + + +void XMLPrinter::PushText( const char* text, bool cdata ) +{ + _textDepth = _depth-1; + + SealElementIfJustOpened(); + if ( cdata ) { + Write( "" ); + } + else { + PrintString( text, true ); + } +} + +void XMLPrinter::PushText( int64_t value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + +void XMLPrinter::PushText( int value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( unsigned value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( bool value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( float value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( double value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushComment( const char* comment ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + _firstElement = false; + + Write( "" ); +} + + +void XMLPrinter::PushDeclaration( const char* value ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + _firstElement = false; + + Write( "" ); +} + + +void XMLPrinter::PushUnknown( const char* value ) +{ + SealElementIfJustOpened(); + if ( _textDepth < 0 && !_firstElement && !_compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + _firstElement = false; + + Write( "' ); +} + + +bool XMLPrinter::VisitEnter( const XMLDocument& doc ) +{ + _processEntities = doc.ProcessEntities(); + if ( doc.HasBOM() ) { + PushHeader( true, false ); + } + return true; +} + + +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) +{ + const XMLElement* parentElem = 0; + if ( element.Parent() ) { + parentElem = element.Parent()->ToElement(); + } + const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; + OpenElement( element.Name(), compactMode ); + while ( attribute ) { + PushAttribute( attribute->Name(), attribute->Value() ); + attribute = attribute->Next(); + } + return true; +} + + +bool XMLPrinter::VisitExit( const XMLElement& element ) +{ + CloseElement( CompactMode(element) ); + return true; +} + + +bool XMLPrinter::Visit( const XMLText& text ) +{ + PushText( text.Value(), text.CData() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLComment& comment ) +{ + PushComment( comment.Value() ); + return true; +} + +bool XMLPrinter::Visit( const XMLDeclaration& declaration ) +{ + PushDeclaration( declaration.Value() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLUnknown& unknown ) +{ + PushUnknown( unknown.Value() ); + return true; +} + +} // namespace tinyxml2 diff --git a/ZAPDTR/lib/tinyxml2/tinyxml2.h b/ZAPDTR/lib/tinyxml2/tinyxml2.h new file mode 100644 index 000000000..7cd312736 --- /dev/null +++ b/ZAPDTR/lib/tinyxml2/tinyxml2.h @@ -0,0 +1,2309 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef TINYXML2_INCLUDED +#define TINYXML2_INCLUDED + +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +# include +# include +# include +# if defined(__PS3__) +# include +# endif +#else +# include +# include +# include +# include +# include +#endif +#include + +/* + TODO: intern strings instead of allocation. +*/ +/* + gcc: + g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe + + Formatting, Artistic Style: + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h +*/ + +#if defined( _DEBUG ) || defined (__DEBUG__) +# ifndef TINYXML2_DEBUG +# define TINYXML2_DEBUG +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4251) +#endif + +#ifdef _WIN32 +# ifdef TINYXML2_EXPORT +# define TINYXML2_LIB __declspec(dllexport) +# elif defined(TINYXML2_IMPORT) +# define TINYXML2_LIB __declspec(dllimport) +# else +# define TINYXML2_LIB +# endif +#elif __GNUC__ >= 4 +# define TINYXML2_LIB __attribute__((visibility("default"))) +#else +# define TINYXML2_LIB +#endif + + +#if defined(TINYXML2_DEBUG) +# if defined(_MSC_VER) +# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like +# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } +# elif defined (ANDROID_NDK) +# include +# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } +# else +# include +# define TIXMLASSERT assert +# endif +#else +# define TIXMLASSERT( x ) {} +#endif + + +/* Versioning, past 1.0.14: + http://semver.org/ +*/ +static const int TIXML2_MAJOR_VERSION = 7; +static const int TIXML2_MINOR_VERSION = 0; +static const int TIXML2_PATCH_VERSION = 1; + +#define TINYXML2_MAJOR_VERSION 7 +#define TINYXML2_MINOR_VERSION 0 +#define TINYXML2_PATCH_VERSION 1 + +// A fixed element depth limit is problematic. There needs to be a +// limit to avoid a stack overflow. However, that limit varies per +// system, and the capacity of the stack. On the other hand, it's a trivial +// attack that can result from ill, malicious, or even correctly formed XML, +// so there needs to be a limit in place. +static const int TINYXML2_MAX_ELEMENT_DEPTH = 100; + +namespace tinyxml2 +{ +class XMLDocument; +class XMLElement; +class XMLAttribute; +class XMLComment; +class XMLText; +class XMLDeclaration; +class XMLUnknown; +class XMLPrinter; + +/* + A class that wraps strings. Normally stores the start and end + pointers into the XML file itself, and will apply normalization + and entity translation if actually read. Can also store (and memory + manage) a traditional char[] + + Isn't clear why TINYXML2_LIB is needed; but seems to fix #719 +*/ +class TINYXML2_LIB StrPair +{ +public: + enum { + NEEDS_ENTITY_PROCESSING = 0x01, + NEEDS_NEWLINE_NORMALIZATION = 0x02, + NEEDS_WHITESPACE_COLLAPSING = 0x04, + + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_NAME = 0, + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + COMMENT = NEEDS_NEWLINE_NORMALIZATION + }; + + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} + ~StrPair(); + + void Set( char* start, char* end, int flags ) { + TIXMLASSERT( start ); + TIXMLASSERT( end ); + Reset(); + _start = start; + _end = end; + _flags = flags | NEEDS_FLUSH; + } + + const char* GetStr(); + + bool Empty() const { + return _start == _end; + } + + void SetInternedStr( const char* str ) { + Reset(); + _start = const_cast(str); + } + + void SetStr( const char* str, int flags=0 ); + + char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); + char* ParseName( char* in ); + + void TransferTo( StrPair* other ); + void Reset(); + +private: + void CollapseWhitespace(); + + enum { + NEEDS_FLUSH = 0x100, + NEEDS_DELETE = 0x200 + }; + + int _flags; + char* _start; + char* _end; + + StrPair( const StrPair& other ); // not supported + void operator=( const StrPair& other ); // not supported, use TransferTo() +}; + + +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ +template +class DynArray +{ +public: + DynArray() : + _mem( _pool ), + _allocated( INITIAL_SIZE ), + _size( 0 ) + { + } + + ~DynArray() { + if ( _mem != _pool ) { + delete [] _mem; + } + } + + void Clear() { + _size = 0; + } + + void Push( T t ) { + TIXMLASSERT( _size < INT_MAX ); + EnsureCapacity( _size+1 ); + _mem[_size] = t; + ++_size; + } + + T* PushArr( int count ) { + TIXMLASSERT( count >= 0 ); + TIXMLASSERT( _size <= INT_MAX - count ); + EnsureCapacity( _size+count ); + T* ret = &_mem[_size]; + _size += count; + return ret; + } + + T Pop() { + TIXMLASSERT( _size > 0 ); + --_size; + return _mem[_size]; + } + + void PopArr( int count ) { + TIXMLASSERT( _size >= count ); + _size -= count; + } + + bool Empty() const { + return _size == 0; + } + + T& operator[](int i) { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& operator[](int i) const { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& PeekTop() const { + TIXMLASSERT( _size > 0 ); + return _mem[ _size - 1]; + } + + int Size() const { + TIXMLASSERT( _size >= 0 ); + return _size; + } + + int Capacity() const { + TIXMLASSERT( _allocated >= INITIAL_SIZE ); + return _allocated; + } + + void SwapRemove(int i) { + TIXMLASSERT(i >= 0 && i < _size); + TIXMLASSERT(_size > 0); + _mem[i] = _mem[_size - 1]; + --_size; + } + + const T* Mem() const { + TIXMLASSERT( _mem ); + return _mem; + } + + T* Mem() { + TIXMLASSERT( _mem ); + return _mem; + } + +private: + DynArray( const DynArray& ); // not supported + void operator=( const DynArray& ); // not supported + + void EnsureCapacity( int cap ) { + TIXMLASSERT( cap > 0 ); + if ( cap > _allocated ) { + TIXMLASSERT( cap <= INT_MAX / 2 ); + int newAllocated = cap * 2; + T* newMem = new T[newAllocated]; + TIXMLASSERT( newAllocated >= _size ); + memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs + if ( _mem != _pool ) { + delete [] _mem; + } + _mem = newMem; + _allocated = newAllocated; + } + } + + T* _mem; + T _pool[INITIAL_SIZE]; + int _allocated; // objects allocated + int _size; // number objects in use +}; + + +/* + Parent virtual class of a pool for fast allocation + and deallocation of objects. +*/ +class MemPool +{ +public: + MemPool() {} + virtual ~MemPool() {} + + virtual int ItemSize() const = 0; + virtual void* Alloc() = 0; + virtual void Free( void* ) = 0; + virtual void SetTracked() = 0; +}; + + +/* + Template child class to create pools of the correct type. +*/ +template< int ITEM_SIZE > +class MemPoolT : public MemPool +{ +public: + MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} + ~MemPoolT() { + MemPoolT< ITEM_SIZE >::Clear(); + } + + void Clear() { + // Delete the blocks. + while( !_blockPtrs.Empty()) { + Block* lastBlock = _blockPtrs.Pop(); + delete lastBlock; + } + _root = 0; + _currentAllocs = 0; + _nAllocs = 0; + _maxAllocs = 0; + _nUntracked = 0; + } + + virtual int ItemSize() const { + return ITEM_SIZE; + } + int CurrentAllocs() const { + return _currentAllocs; + } + + virtual void* Alloc() { + if ( !_root ) { + // Need a new block. + Block* block = new Block(); + _blockPtrs.Push( block ); + + Item* blockItems = block->items; + for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { + blockItems[i].next = &(blockItems[i + 1]); + } + blockItems[ITEMS_PER_BLOCK - 1].next = 0; + _root = blockItems; + } + Item* const result = _root; + TIXMLASSERT( result != 0 ); + _root = _root->next; + + ++_currentAllocs; + if ( _currentAllocs > _maxAllocs ) { + _maxAllocs = _currentAllocs; + } + ++_nAllocs; + ++_nUntracked; + return result; + } + + virtual void Free( void* mem ) { + if ( !mem ) { + return; + } + --_currentAllocs; + Item* item = static_cast( mem ); +#ifdef TINYXML2_DEBUG + memset( item, 0xfe, sizeof( *item ) ); +#endif + item->next = _root; + _root = item; + } + void Trace( const char* name ) { + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", + name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, + ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); + } + + void SetTracked() { + --_nUntracked; + } + + int Untracked() const { + return _nUntracked; + } + + // This number is perf sensitive. 4k seems like a good tradeoff on my machine. + // The test file is large, 170k. + // Release: VS2010 gcc(no opt) + // 1k: 4000 + // 2k: 4000 + // 4k: 3900 21000 + // 16k: 5200 + // 32k: 4300 + // 64k: 4000 21000 + // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK + // in private part if ITEMS_PER_BLOCK is private + enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; + +private: + MemPoolT( const MemPoolT& ); // not supported + void operator=( const MemPoolT& ); // not supported + + union Item { + Item* next; + char itemData[ITEM_SIZE]; + }; + struct Block { + Item items[ITEMS_PER_BLOCK]; + }; + DynArray< Block*, 10 > _blockPtrs; + Item* _root; + + int _currentAllocs; + int _nAllocs; + int _maxAllocs; + int _nUntracked; +}; + + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a XMLVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its siblings will be visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the XMLDocument, although all nodes support visiting. + + You should never change the document from a callback. + + @sa XMLNode::Accept() +*/ +class TINYXML2_LIB XMLVisitor +{ +public: + virtual ~XMLVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { + return true; + } + /// Visit a document. + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + /// Visit an element. + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { + return true; + } + /// Visit an element. + virtual bool VisitExit( const XMLElement& /*element*/ ) { + return true; + } + + /// Visit a declaration. + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { + return true; + } + /// Visit a text node. + virtual bool Visit( const XMLText& /*text*/ ) { + return true; + } + /// Visit a comment node. + virtual bool Visit( const XMLComment& /*comment*/ ) { + return true; + } + /// Visit an unknown node. + virtual bool Visit( const XMLUnknown& /*unknown*/ ) { + return true; + } +}; + +// WARNING: must match XMLDocument::_errorNames[] +enum XMLError { + XML_SUCCESS = 0, + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE, + XML_ELEMENT_DEPTH_EXCEEDED, + + XML_ERROR_COUNT +}; + + +/* + Utility functionality. +*/ +class TINYXML2_LIB XMLUtil +{ +public: + static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { + TIXMLASSERT( p ); + + while( IsWhiteSpace(*p) ) { + if (curLineNumPtr && *p == '\n') { + ++(*curLineNumPtr); + } + ++p; + } + TIXMLASSERT( p ); + return p; + } + static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) { + return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); + } + + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. + static bool IsWhiteSpace( char p ) { + return !IsUTF8Continuation(p) && isspace( static_cast(p) ); + } + + inline static bool IsNameStartChar( unsigned char ch ) { + if ( ch >= 128 ) { + // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() + return true; + } + if ( isalpha( ch ) ) { + return true; + } + return ch == ':' || ch == '_'; + } + + inline static bool IsNameChar( unsigned char ch ) { + return IsNameStartChar( ch ) + || isdigit( ch ) + || ch == '.' + || ch == '-'; + } + + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { + if ( p == q ) { + return true; + } + TIXMLASSERT( p ); + TIXMLASSERT( q ); + TIXMLASSERT( nChar >= 0 ); + return strncmp( p, q, nChar ) == 0; + } + + inline static bool IsUTF8Continuation( char p ) { + return ( p & 0x80 ) != 0; + } + + static const char* ReadBOM( const char* p, bool* hasBOM ); + // p is the starting location, + // the UTF-8 value of the entity will be placed in value, and length filled in. + static const char* GetCharacterRef( const char* p, char* value, int* length ); + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + + // converts primitive types to strings + static void ToStr( int v, char* buffer, int bufferSize ); + static void ToStr( unsigned v, char* buffer, int bufferSize ); + static void ToStr( bool v, char* buffer, int bufferSize ); + static void ToStr( float v, char* buffer, int bufferSize ); + static void ToStr( double v, char* buffer, int bufferSize ); + static void ToStr(int64_t v, char* buffer, int bufferSize); + + // converts strings to primitive types + static bool ToInt( const char* str, int* value ); + static bool ToUnsigned( const char* str, unsigned* value ); + static bool ToBool( const char* str, bool* value ); + static bool ToFloat( const char* str, float* value ); + static bool ToDouble( const char* str, double* value ); + static bool ToInt64(const char* str, int64_t* value); + + // Changes what is serialized for a boolean value. + // Default to "true" and "false". Shouldn't be changed + // unless you have a special testing or compatibility need. + // Be careful: static, global, & not thread safe. + // Be sure to set static const memory as parameters. + static void SetBoolSerialization(const char* writeTrue, const char* writeFalse); + +private: + static const char* writeBoolTrue; + static const char* writeBoolFalse; +}; + + +/** XMLNode is a base class for every object that is in the + XML Document Object Model (DOM), except XMLAttributes. + Nodes have siblings, a parent, and children which can + be navigated. A node is always in a XMLDocument. + The type of a XMLNode can be queried, and it can + be cast to its more defined type. + + A XMLDocument allocates memory for all its Nodes. + When the XMLDocument gets deleted, all its Nodes + will also be deleted. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + @endverbatim +*/ +class TINYXML2_LIB XMLNode +{ + friend class XMLDocument; + friend class XMLElement; +public: + + /// Get the XMLDocument that owns this XMLNode. + const XMLDocument* GetDocument() const { + TIXMLASSERT( _document ); + return _document; + } + /// Get the XMLDocument that owns this XMLNode. + XMLDocument* GetDocument() { + TIXMLASSERT( _document ); + return _document; + } + + /// Safely cast to an Element, or null. + virtual XMLElement* ToElement() { + return 0; + } + /// Safely cast to Text, or null. + virtual XMLText* ToText() { + return 0; + } + /// Safely cast to a Comment, or null. + virtual XMLComment* ToComment() { + return 0; + } + /// Safely cast to a Document, or null. + virtual XMLDocument* ToDocument() { + return 0; + } + /// Safely cast to a Declaration, or null. + virtual XMLDeclaration* ToDeclaration() { + return 0; + } + /// Safely cast to an Unknown, or null. + virtual XMLUnknown* ToUnknown() { + return 0; + } + + virtual const XMLElement* ToElement() const { + return 0; + } + virtual const XMLText* ToText() const { + return 0; + } + virtual const XMLComment* ToComment() const { + return 0; + } + virtual const XMLDocument* ToDocument() const { + return 0; + } + virtual const XMLDeclaration* ToDeclaration() const { + return 0; + } + virtual const XMLUnknown* ToUnknown() const { + return 0; + } + + /** The meaning of 'value' changes for the specific type. + @verbatim + Document: empty (NULL is returned, not an empty string) + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + const char* Value() const; + + /** Set the Value of an XML node. + @sa Value() + */ + void SetValue( const char* val, bool staticMem=false ); + + /// Gets the line number the node is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// Get the parent of this node on the DOM. + const XMLNode* Parent() const { + return _parent; + } + + XMLNode* Parent() { + return _parent; + } + + /// Returns true if this node has no children. + bool NoChildren() const { + return !_firstChild; + } + + /// Get the first child node, or null if none exists. + const XMLNode* FirstChild() const { + return _firstChild; + } + + XMLNode* FirstChild() { + return _firstChild; + } + + /** Get the first child element, or optionally the first child + element with the specified name. + */ + const XMLElement* FirstChildElement( const char* name = 0 ) const; + + XMLElement* FirstChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->FirstChildElement( name )); + } + + /// Get the last child node, or null if none exists. + const XMLNode* LastChild() const { + return _lastChild; + } + + XMLNode* LastChild() { + return _lastChild; + } + + /** Get the last child element or optionally the last child + element with the specified name. + */ + const XMLElement* LastChildElement( const char* name = 0 ) const; + + XMLElement* LastChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->LastChildElement(name) ); + } + + /// Get the previous (left) sibling node of this node. + const XMLNode* PreviousSibling() const { + return _prev; + } + + XMLNode* PreviousSibling() { + return _prev; + } + + /// Get the previous (left) sibling element of this node, with an optionally supplied name. + const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; + + XMLElement* PreviousSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); + } + + /// Get the next (right) sibling node of this node. + const XMLNode* NextSibling() const { + return _next; + } + + XMLNode* NextSibling() { + return _next; + } + + /// Get the next (right) sibling element of this node, with an optionally supplied name. + const XMLElement* NextSiblingElement( const char* name = 0 ) const; + + XMLElement* NextSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->NextSiblingElement( name ) ); + } + + /** + Add a child node as the last (right) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertEndChild( XMLNode* addThis ); + + XMLNode* LinkEndChild( XMLNode* addThis ) { + return InsertEndChild( addThis ); + } + /** + Add a child node as the first (left) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertFirstChild( XMLNode* addThis ); + /** + Add a node after the specified child node. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the afterThis node + is not a child of this node, or if the node does not + belong to the same document. + */ + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + + /** + Delete all the children of this node. + */ + void DeleteChildren(); + + /** + Delete a child of this node. + */ + void DeleteChild( XMLNode* node ); + + /** + Make a copy of this node, but not its children. + You may pass in a Document pointer that will be + the owner of the new Node. If the 'document' is + null, then the node returned will be allocated + from the current Document. (this->GetDocument()) + + Note: if called on a XMLDocument, this will return null. + */ + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; + + /** + Make a copy of this node and all its children. + + If the 'target' is null, then the nodes will + be allocated in the current document. If 'target' + is specified, the memory will be allocated is the + specified XMLDocument. + + NOTE: This is probably not the correct tool to + copy a document, since XMLDocuments can have multiple + top level XMLNodes. You probably want to use + XMLDocument::DeepCopy() + */ + XMLNode* DeepClone( XMLDocument* target ) const; + + /** + Test if 2 nodes are the same, but don't test children. + The 2 nodes do not need to be in the same Document. + + Note: if called on a XMLDocument, this will return false. + */ + virtual bool ShallowEqual( const XMLNode* compare ) const = 0; + + /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the XMLVisitor interface. + + This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + XMLPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( XMLVisitor* visitor ) const = 0; + + /** + Set user data into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void SetUserData(void* userData) { _userData = userData; } + + /** + Get user data set into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void* GetUserData() const { return _userData; } + +protected: + explicit XMLNode( XMLDocument* ); + virtual ~XMLNode(); + + virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); + + XMLDocument* _document; + XMLNode* _parent; + mutable StrPair _value; + int _parseLineNum; + + XMLNode* _firstChild; + XMLNode* _lastChild; + + XMLNode* _prev; + XMLNode* _next; + + void* _userData; + +private: + MemPool* _memPool; + void Unlink( XMLNode* child ); + static void DeleteNode( XMLNode* node ); + void InsertChildPreamble( XMLNode* insertThis ) const; + const XMLElement* ToElementWithName( const char* name ) const; + + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported +}; + + +/** XML text. + + Note that a text node can have child element nodes, for example: + @verbatim + This is bold + @endverbatim + + A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCData() and query it with CData(). +*/ +class TINYXML2_LIB XMLText : public XMLNode +{ + friend class XMLDocument; +public: + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLText* ToText() { + return this; + } + virtual const XMLText* ToText() const { + return this; + } + + /// Declare whether this should be CDATA or standard text. + void SetCData( bool isCData ) { + _isCData = isCData; + } + /// Returns true if this is a CDATA text element. + bool CData() const { + return _isCData; + } + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + virtual ~XMLText() {} + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + bool _isCData; + + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported +}; + + +/** An XML Comment. */ +class TINYXML2_LIB XMLComment : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLComment* ToComment() { + return this; + } + virtual const XMLComment* ToComment() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLComment( XMLDocument* doc ); + virtual ~XMLComment(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); + +private: + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXML-2 will happily read or write files without a declaration, + however. + + The text of the declaration isn't interpreted. It is parsed + and written as a string. +*/ +class TINYXML2_LIB XMLDeclaration : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLDeclaration* ToDeclaration() { + return this; + } + virtual const XMLDeclaration* ToDeclaration() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLDeclaration( XMLDocument* doc ); + virtual ~XMLDeclaration(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + XMLDeclaration( const XMLDeclaration& ); // not supported + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported +}; + + +/** Any tag that TinyXML-2 doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into XMLUnknowns. +*/ +class TINYXML2_LIB XMLUnknown : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLUnknown* ToUnknown() { + return this; + } + virtual const XMLUnknown* ToUnknown() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLUnknown( XMLDocument* doc ); + virtual ~XMLUnknown(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + XMLUnknown( const XMLUnknown& ); // not supported + XMLUnknown& operator=( const XMLUnknown& ); // not supported +}; + + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not XMLNodes. You may only query the + Next() attribute in a list. +*/ +class TINYXML2_LIB XMLAttribute +{ + friend class XMLElement; +public: + /// The name of the attribute. + const char* Name() const; + + /// The value of the attribute. + const char* Value() const; + + /// Gets the line number the attribute is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// The next attribute in the list. + const XMLAttribute* Next() const { + return _next; + } + + /** IntValue interprets the attribute as an integer, and returns the value. + If the value isn't an integer, 0 will be returned. There is no error checking; + use QueryIntValue() if you need error checking. + */ + int IntValue() const { + int i = 0; + QueryIntValue(&i); + return i; + } + + int64_t Int64Value() const { + int64_t i = 0; + QueryInt64Value(&i); + return i; + } + + /// Query as an unsigned integer. See IntValue() + unsigned UnsignedValue() const { + unsigned i=0; + QueryUnsignedValue( &i ); + return i; + } + /// Query as a boolean. See IntValue() + bool BoolValue() const { + bool b=false; + QueryBoolValue( &b ); + return b; + } + /// Query as a double. See IntValue() + double DoubleValue() const { + double d=0; + QueryDoubleValue( &d ); + return d; + } + /// Query as a float. See IntValue() + float FloatValue() const { + float f=0; + QueryFloatValue( &f ); + return f; + } + + /** QueryIntValue interprets the attribute as an integer, and returns the value + in the provided parameter. The function will return XML_SUCCESS on success, + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. + */ + XMLError QueryIntValue( int* value ) const; + /// See QueryIntValue + XMLError QueryUnsignedValue( unsigned int* value ) const; + /// See QueryIntValue + XMLError QueryInt64Value(int64_t* value) const; + /// See QueryIntValue + XMLError QueryBoolValue( bool* value ) const; + /// See QueryIntValue + XMLError QueryDoubleValue( double* value ) const; + /// See QueryIntValue + XMLError QueryFloatValue( float* value ) const; + + /// Set the attribute to a string value. + void SetAttribute( const char* value ); + /// Set the attribute to value. + void SetAttribute( int value ); + /// Set the attribute to value. + void SetAttribute( unsigned value ); + /// Set the attribute to value. + void SetAttribute(int64_t value); + /// Set the attribute to value. + void SetAttribute( bool value ); + /// Set the attribute to value. + void SetAttribute( double value ); + /// Set the attribute to value. + void SetAttribute( float value ); + +private: + enum { BUF_SIZE = 200 }; + + XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} + virtual ~XMLAttribute() {} + + XMLAttribute( const XMLAttribute& ); // not supported + void operator=( const XMLAttribute& ); // not supported + void SetName( const char* name ); + + char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); + + mutable StrPair _name; + mutable StrPair _value; + int _parseLineNum; + XMLAttribute* _next; + MemPool* _memPool; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TINYXML2_LIB XMLElement : public XMLNode +{ + friend class XMLDocument; +public: + /// Get the name of an element (which is the Value() of the node.) + const char* Name() const { + return Value(); + } + /// Set the name of the element. + void SetName( const char* str, bool staticMem=false ) { + SetValue( str, staticMem ); + } + + virtual XMLElement* ToElement() { + return this; + } + virtual const XMLElement* ToElement() const { + return this; + } + virtual bool Accept( XMLVisitor* visitor ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none + exists. For example: + + @verbatim + const char* value = ele->Attribute( "foo" ); + @endverbatim + + The 'value' parameter is normally null. However, if specified, + the attribute will only be returned if the 'name' and 'value' + match. This allow you to write code: + + @verbatim + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); + @endverbatim + + rather than: + @verbatim + if ( ele->Attribute( "foo" ) ) { + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); + } + @endverbatim + */ + const char* Attribute( const char* name, const char* value=0 ) const; + + /** Given an attribute name, IntAttribute() returns the value + of the attribute interpreted as an integer. The default + value will be returned if the attribute isn't present, + or if there is an error. (For a method with error + checking, see QueryIntAttribute()). + */ + int IntAttribute(const char* name, int defaultValue = 0) const; + /// See IntAttribute() + unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; + /// See IntAttribute() + int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; + /// See IntAttribute() + bool BoolAttribute(const char* name, bool defaultValue = false) const; + /// See IntAttribute() + double DoubleAttribute(const char* name, double defaultValue = 0) const; + /// See IntAttribute() + float FloatAttribute(const char* name, float defaultValue = 0) const; + + /** Given an attribute name, QueryIntAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryIntAttribute( const char* name, int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryIntValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsignedValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryInt64Attribute(const char* name, int64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryInt64Value(value); + } + + /// See QueryIntAttribute() + XMLError QueryBoolAttribute( const char* name, bool* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryBoolValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryDoubleAttribute( const char* name, double* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryDoubleValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryFloatAttribute( const char* name, float* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryFloatValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryStringAttribute(const char* name, const char** value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + *value = a->Value(); + return XML_SUCCESS; + } + + + + /** Given an attribute name, QueryAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. It is overloaded for the primitive types, + and is a generally more convenient replacement of + QueryIntAttribute() and related functions. + + If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryAttribute( const char* name, int* value ) const { + return QueryIntAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, unsigned int* value ) const { + return QueryUnsignedAttribute( name, value ); + } + + XMLError QueryAttribute(const char* name, int64_t* value) const { + return QueryInt64Attribute(name, value); + } + + XMLError QueryAttribute( const char* name, bool* value ) const { + return QueryBoolAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, double* value ) const { + return QueryDoubleAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, float* value ) const { + return QueryFloatAttribute( name, value ); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, const char* value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, int value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, unsigned value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /// Sets the named attribute to value. + void SetAttribute(const char* name, int64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, bool value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, double value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, float value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /** + Delete an attribute. + */ + void DeleteAttribute( const char* name ); + + /// Return the first attribute in the list. + const XMLAttribute* FirstAttribute() const { + return _rootAttribute; + } + /// Query a specific attribute in the list. + const XMLAttribute* FindAttribute( const char* name ) const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the XMLText child + and accessing it directly. + + If the first child of 'this' is a XMLText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + */ + const char* GetText() const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, SetText() is limited compared to creating an XMLText child + and mutating it directly. + + If the first child of 'this' is a XMLText, SetText() sets its value to + the given string, otherwise it will create a first child that is an XMLText. + + This is a convenient method for setting the text of simple contained text: + @verbatim + This is text + fooElement->SetText( "Hullaballoo!" ); + Hullaballoo! + @endverbatim + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then it will not change "This is text", but rather prefix it with a text element: + @verbatim + Hullaballoo!This is text + @endverbatim + + For this XML: + @verbatim + + @endverbatim + SetText() will generate + @verbatim + Hullaballoo! + @endverbatim + */ + void SetText( const char* inText ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( int value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( unsigned value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(int64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( bool value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( double value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( float value ); + + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + XMLError QueryIntText( int* ival ) const; + /// See QueryIntText() + XMLError QueryUnsignedText( unsigned* uval ) const; + /// See QueryIntText() + XMLError QueryInt64Text(int64_t* uval) const; + /// See QueryIntText() + XMLError QueryBoolText( bool* bval ) const; + /// See QueryIntText() + XMLError QueryDoubleText( double* dval ) const; + /// See QueryIntText() + XMLError QueryFloatText( float* fval ) const; + + int IntText(int defaultValue = 0) const; + + /// See QueryIntText() + unsigned UnsignedText(unsigned defaultValue = 0) const; + /// See QueryIntText() + int64_t Int64Text(int64_t defaultValue = 0) const; + /// See QueryIntText() + bool BoolText(bool defaultValue = false) const; + /// See QueryIntText() + double DoubleText(double defaultValue = 0) const; + /// See QueryIntText() + float FloatText(float defaultValue = 0) const; + + // internal: + enum ElementClosingType { + OPEN, // + CLOSED, // + CLOSING // + }; + ElementClosingType ClosingType() const { + return _closingType; + } + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + XMLElement( XMLDocument* doc ); + virtual ~XMLElement(); + XMLElement( const XMLElement& ); // not supported + void operator=( const XMLElement& ); // not supported + + XMLAttribute* FindOrCreateAttribute( const char* name ); + char* ParseAttributes( char* p, int* curLineNumPtr ); + static void DeleteAttribute( XMLAttribute* attribute ); + XMLAttribute* CreateAttribute(); + + enum { BUF_SIZE = 200 }; + ElementClosingType _closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. + XMLAttribute* _rootAttribute; +}; + + +enum Whitespace { + PRESERVE_WHITESPACE, + COLLAPSE_WHITESPACE +}; + + +/** A Document binds together all the functionality. + It can be saved, loaded, and printed to the screen. + All Nodes are connected and allocated to a Document. + If the Document is deleted, all its Nodes are also deleted. +*/ +class TINYXML2_LIB XMLDocument : public XMLNode +{ + friend class XMLElement; + // Gives access to SetError and Push/PopDepth, but over-access for everything else. + // Wishing C++ had "internal" scope. + friend class XMLNode; + friend class XMLText; + friend class XMLComment; + friend class XMLDeclaration; + friend class XMLUnknown; +public: + /// constructor + XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); + ~XMLDocument(); + + virtual XMLDocument* ToDocument() { + TIXMLASSERT( this == _document ); + return this; + } + virtual const XMLDocument* ToDocument() const { + TIXMLASSERT( this == _document ); + return this; + } + + /** + Parse an XML file from a character string. + Returns XML_SUCCESS (0) on success, or + an errorID. + + You may optionally pass in the 'nBytes', which is + the number of bytes which will be parsed. If not + specified, TinyXML-2 will assume 'xml' points to a + null terminated string. + */ + XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); + + /** + Load an XML file from disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( const char* filename ); + + /** + Load an XML file from disk. You are responsible + for providing and closing the FILE*. + + NOTE: The file should be opened as binary ("rb") + not text in order for TinyXML-2 to correctly + do newline normalization. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( FILE* ); + + /** + Save the XML file to disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( const char* filename, bool compact = false ); + + /** + Save the XML file to disk. You are responsible + for providing and closing the FILE*. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( FILE* fp, bool compact = false ); + + bool ProcessEntities() const { + return _processEntities; + } + Whitespace WhitespaceMode() const { + return _whitespaceMode; + } + + /** + Returns true if this document has a leading Byte Order Mark of UTF8. + */ + bool HasBOM() const { + return _writeBOM; + } + /** Sets whether to write the BOM when writing the file. + */ + void SetBOM( bool useBOM ) { + _writeBOM = useBOM; + } + + /** Return the root element of DOM. Equivalent to FirstChildElement(). + To get the first node, use FirstChild(). + */ + XMLElement* RootElement() { + return FirstChildElement(); + } + const XMLElement* RootElement() const { + return FirstChildElement(); + } + + /** Print the Document. If the Printer is not provided, it will + print to stdout. If you provide Printer, this can print to a file: + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Or you can use a printer to print to memory: + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + // printer.CStr() has a const char* to the XML + @endverbatim + */ + void Print( XMLPrinter* streamer=0 ) const; + virtual bool Accept( XMLVisitor* visitor ) const; + + /** + Create a new Element associated with + this Document. The memory for the Element + is managed by the Document. + */ + XMLElement* NewElement( const char* name ); + /** + Create a new Comment associated with + this Document. The memory for the Comment + is managed by the Document. + */ + XMLComment* NewComment( const char* comment ); + /** + Create a new Text associated with + this Document. The memory for the Text + is managed by the Document. + */ + XMLText* NewText( const char* text ); + /** + Create a new Declaration associated with + this Document. The memory for the object + is managed by the Document. + + If the 'text' param is null, the standard + declaration is used.: + @verbatim + + @endverbatim + */ + XMLDeclaration* NewDeclaration( const char* text=0 ); + /** + Create a new Unknown associated with + this Document. The memory for the object + is managed by the Document. + */ + XMLUnknown* NewUnknown( const char* text ); + + /** + Delete a node associated with this document. + It will be unlinked from the DOM. + */ + void DeleteNode( XMLNode* node ); + + void ClearError() { + SetError(XML_SUCCESS, 0, 0); + } + + /// Return true if there was an error parsing the document. + bool Error() const { + return _errorID != XML_SUCCESS; + } + /// Return the errorID. + XMLError ErrorID() const { + return _errorID; + } + const char* ErrorName() const; + static const char* ErrorIDToName(XMLError errorID); + + /** Returns a "long form" error description. A hopefully helpful + diagnostic with location, line number, and/or additional info. + */ + const char* ErrorStr() const; + + /// A (trivial) utility function that prints the ErrorStr() to stdout. + void PrintError() const; + + /// Return the line where the error occurred, or zero if unknown. + int ErrorLineNum() const + { + return _errorLineNum; + } + + /// Clear the document, resetting it to the initial state. + void Clear(); + + /** + Copies this document to a target document. + The target will be completely cleared before the copy. + If you want to copy a sub-tree, see XMLNode::DeepClone(). + + NOTE: that the 'target' must be non-null. + */ + void DeepCopy(XMLDocument* target) const; + + // internal + char* Identify( char* p, XMLNode** node ); + + // internal + void MarkInUse(XMLNode*); + + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + return 0; + } + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + return false; + } + +private: + XMLDocument( const XMLDocument& ); // not supported + void operator=( const XMLDocument& ); // not supported + + bool _writeBOM; + bool _processEntities; + XMLError _errorID; + Whitespace _whitespaceMode; + mutable StrPair _errorStr; + int _errorLineNum; + char* _charBuffer; + int _parseCurLineNum; + int _parsingDepth; + // Memory tracking does add some overhead. + // However, the code assumes that you don't + // have a bunch of unlinked nodes around. + // Therefore it takes less memory to track + // in the document vs. a linked list in the XMLNode, + // and the performance is the same. + DynArray _unlinked; + + MemPoolT< sizeof(XMLElement) > _elementPool; + MemPoolT< sizeof(XMLAttribute) > _attributePool; + MemPoolT< sizeof(XMLText) > _textPool; + MemPoolT< sizeof(XMLComment) > _commentPool; + + static const char* _errorNames[XML_ERROR_COUNT]; + + void Parse(); + + void SetError( XMLError error, int lineNum, const char* format, ... ); + + // Something of an obvious security hole, once it was discovered. + // Either an ill-formed XML or an excessively deep one can overflow + // the stack. Track stack depth, and error out if needed. + class DepthTracker { + public: + explicit DepthTracker(XMLDocument * document) { + this->_document = document; + document->PushDepth(); + } + ~DepthTracker() { + _document->PopDepth(); + } + private: + XMLDocument * _document; + }; + void PushDepth(); + void PopDepth(); + + template + NodeType* CreateUnlinkedNode( MemPoolT& pool ); +}; + +template +inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) +{ + TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); + TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); + NodeType* returnNode = new (pool.Alloc()) NodeType( this ); + TIXMLASSERT( returnNode ); + returnNode->_memPool = &pool; + + _unlinked.Push(returnNode); + return returnNode; +} + +/** + A XMLHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + XMLElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + XMLElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + XMLElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + XMLElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity + of such code. A XMLHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + XMLHandle docHandle( &document ); + XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + XMLHandle handleCopy = handle; + @endverbatim + + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. +*/ +class TINYXML2_LIB XMLHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + explicit XMLHandle( XMLNode* node ) : _node( node ) { + } + /// Create a handle from a node. + explicit XMLHandle( XMLNode& node ) : _node( &node ) { + } + /// Copy constructor + XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { + } + /// Assignment + XMLHandle& operator=( const XMLHandle& ref ) { + _node = ref._node; + return *this; + } + + /// Get the first child of this handle. + XMLHandle FirstChild() { + return XMLHandle( _node ? _node->FirstChild() : 0 ); + } + /// Get the first child element of this handle. + XMLHandle FirstChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + /// Get the last child of this handle. + XMLHandle LastChild() { + return XMLHandle( _node ? _node->LastChild() : 0 ); + } + /// Get the last child element of this handle. + XMLHandle LastChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + /// Get the previous sibling of this handle. + XMLHandle PreviousSibling() { + return XMLHandle( _node ? _node->PreviousSibling() : 0 ); + } + /// Get the previous sibling element of this handle. + XMLHandle PreviousSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + /// Get the next sibling of this handle. + XMLHandle NextSibling() { + return XMLHandle( _node ? _node->NextSibling() : 0 ); + } + /// Get the next sibling element of this handle. + XMLHandle NextSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + /// Safe cast to XMLNode. This can return null. + XMLNode* ToNode() { + return _node; + } + /// Safe cast to XMLElement. This can return null. + XMLElement* ToElement() { + return ( _node ? _node->ToElement() : 0 ); + } + /// Safe cast to XMLText. This can return null. + XMLText* ToText() { + return ( _node ? _node->ToText() : 0 ); + } + /// Safe cast to XMLUnknown. This can return null. + XMLUnknown* ToUnknown() { + return ( _node ? _node->ToUnknown() : 0 ); + } + /// Safe cast to XMLDeclaration. This can return null. + XMLDeclaration* ToDeclaration() { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + XMLNode* _node; +}; + + +/** + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the + same in all regards, except for the 'const' qualifiers. See XMLHandle for API. +*/ +class TINYXML2_LIB XMLConstHandle +{ +public: + explicit XMLConstHandle( const XMLNode* node ) : _node( node ) { + } + explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) { + } + XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { + } + + XMLConstHandle& operator=( const XMLConstHandle& ref ) { + _node = ref._node; + return *this; + } + + const XMLConstHandle FirstChild() const { + return XMLConstHandle( _node ? _node->FirstChild() : 0 ); + } + const XMLConstHandle FirstChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + const XMLConstHandle LastChild() const { + return XMLConstHandle( _node ? _node->LastChild() : 0 ); + } + const XMLConstHandle LastChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + const XMLConstHandle PreviousSibling() const { + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); + } + const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + const XMLConstHandle NextSibling() const { + return XMLConstHandle( _node ? _node->NextSibling() : 0 ); + } + const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + + const XMLNode* ToNode() const { + return _node; + } + const XMLElement* ToElement() const { + return ( _node ? _node->ToElement() : 0 ); + } + const XMLText* ToText() const { + return ( _node ? _node->ToText() : 0 ); + } + const XMLUnknown* ToUnknown() const { + return ( _node ? _node->ToUnknown() : 0 ); + } + const XMLDeclaration* ToDeclaration() const { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + const XMLNode* _node; +}; + + +/** + Printing functionality. The XMLPrinter gives you more + options than the XMLDocument::Print() method. + + It can: + -# Print to memory. + -# Print to a file you provide. + -# Print XML without a XMLDocument. + + Print to Memory + + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + SomeFunction( printer.CStr() ); + @endverbatim + + Print to a File + + You provide the file pointer. + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Print without a XMLDocument + + When loading, an XML parser is very useful. However, sometimes + when saving, it just gets in the way. The code is often set up + for streaming, and constructing the DOM is just overhead. + + The Printer supports the streaming case. The following code + prints out a trivially simple XML file without ever creating + an XML document. + + @verbatim + XMLPrinter printer( fp ); + printer.OpenElement( "foo" ); + printer.PushAttribute( "foo", "bar" ); + printer.CloseElement(); + @endverbatim +*/ +class TINYXML2_LIB XMLPrinter : public XMLVisitor +{ +public: + /** Construct the printer. If the FILE* is specified, + this will print to the FILE. Else it will print + to memory, and the result is available in CStr(). + If 'compact' is set to true, then output is created + with only required whitespace and newlines. + */ + XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); + virtual ~XMLPrinter() {} + + /** If streaming, write the BOM and declaration. */ + void PushHeader( bool writeBOM, bool writeDeclaration ); + /** If streaming, start writing an element. + The element must be closed with CloseElement() + */ + void OpenElement( const char* name, bool compactMode=false ); + /// If streaming, add an attribute to an open element. + void PushAttribute( const char* name, const char* value ); + void PushAttribute( const char* name, int value ); + void PushAttribute( const char* name, unsigned value ); + void PushAttribute(const char* name, int64_t value); + void PushAttribute( const char* name, bool value ); + void PushAttribute( const char* name, double value ); + /// If streaming, close the Element. + virtual void CloseElement( bool compactMode=false ); + + /// Add a text node. + void PushText( const char* text, bool cdata=false ); + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from an unsigned. + void PushText(int64_t value); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment + void PushComment( const char* comment ); + + void PushDeclaration( const char* value ); + void PushUnknown( const char* value ); + + virtual bool VisitEnter( const XMLDocument& /*doc*/ ); + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); + virtual bool VisitExit( const XMLElement& element ); + + virtual bool Visit( const XMLText& text ); + virtual bool Visit( const XMLComment& comment ); + virtual bool Visit( const XMLDeclaration& declaration ); + virtual bool Visit( const XMLUnknown& unknown ); + + /** + If in print to memory mode, return a pointer to + the XML file in memory. + */ + const char* CStr() const { + return _buffer.Mem(); + } + /** + If in print to memory mode, return the size + of the XML file in memory. (Note the size returned + includes the terminating null.) + */ + int CStrSize() const { + return _buffer.Size(); + } + /** + If in print to memory mode, reset the buffer to the + beginning. + */ + void ClearBuffer() { + _buffer.Clear(); + _buffer.Push(0); + _firstElement = true; + } + +protected: + virtual bool CompactMode( const XMLElement& ) { return _compactMode; } + + /** Prints out the space before an element. You may override to change + the space and tabs used. A PrintSpace() override should call Print(). + */ + virtual void PrintSpace( int depth ); + void Print( const char* format, ... ); + void Write( const char* data, size_t size ); + inline void Write( const char* data ) { Write( data, strlen( data ) ); } + void Putc( char ch ); + + void SealElementIfJustOpened(); + bool _elementJustOpened; + DynArray< const char*, 10 > _stack; + +private: + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. + + bool _firstElement; + FILE* _fp; + int _depth; + int _textDepth; + bool _processEntities; + bool _compactMode; + + enum { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; + + DynArray< char, 20 > _buffer; + + // Prohibit cloning, intentionally not implemented + XMLPrinter( const XMLPrinter& ); + XMLPrinter& operator=( const XMLPrinter& ); +}; + + +} // tinyxml2 + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TINYXML2_INCLUDED diff --git a/libultraship/.clang-format b/libultraship/.clang-format new file mode 100644 index 000000000..c7b900f06 --- /dev/null +++ b/libultraship/.clang-format @@ -0,0 +1,23 @@ +IndentWidth: 4 +Language: Cpp +UseTab: Never +ColumnLimit: 120 +PointerAlignment: Left +BreakBeforeBraces: Attach +SpaceAfterCStyleCast: false +Cpp11BracedListStyle: false +IndentCaseLabels: true +BinPackArguments: true +BinPackParameters: true +AlignAfterOpenBracket: Align +AlignOperands: true +BreakBeforeTernaryOperators: true +BreakBeforeBinaryOperators: None +AllowShortBlocksOnASingleLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AlignEscapedNewlines: Left +AlignTrailingComments: true +SortIncludes: false diff --git a/libultraship/.clang-tidy b/libultraship/.clang-tidy new file mode 100644 index 000000000..516584aca --- /dev/null +++ b/libultraship/.clang-tidy @@ -0,0 +1,30 @@ +Checks: '-*,readability-braces-around-statements,readability-inconsistent-declaration-parameter-name,readability-identifier-naming' +WarningsAsErrors: '' +HeaderFilterRegex: '(src|include)\/.*\.h$' +FormatStyle: 'file' +CheckOptions: + - { key: readability-identifier-naming.NamespaceCase, value: CamelCase } + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-naming.PrivateMemberPrefix, value: m } + - { key: readability-identifier-naming.ProtectedMemberPrefix, value: m } + - { key: readability-identifier-naming.PrivateMemberCase, value: CamelCase } + - { key: readability-identifier-naming.ProtectedMemberCase, value: CamelCase } + - { key: readability-identifier-naming.PublicMemberCase, value: CamelCase } + - { key: readability-identifier-naming.MemberCase, value: CamelCase } + - { key: readability-identifier-naming.StructCase, value: CamelCase } + #- { key: readability-identifier-naming.EnumCase, value: CamelCase } + #- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } + - { key: readability-identifier-naming.VariableCase, value: CamelCase } + - { key: readability-identifier-naming.StaticVariableCase, value: CamelCase } + - { key: readability-identifier-naming.StaticVariablePrefix, value: s } + - { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase } + - { key: readability-identifier-naming.GlobalConstantPrefix, value: g } + - { key: readability-identifier-naming.MethodCase, value: CamelCase } + - { key: readability-identifier-naming.FunctionCase, value: CamelCase } + - { key: readability-identifier-naming.VariableCase, value: camelBack } + - { key: readability-identifier-naming.LocalVariableCase, value: camelBack } + - { key: readability-identifier-naming.ParameterCase, value: camelBack } + # Require argument names to match exactly (instead of allowing a name to be a prefix/suffix of another) + # Note: 'true' is expected by clang-tidy 12+ but '1' is used for compatibility with older versions + - key: readability-inconsistent-declaration-parameter-name.Strict + value: 1 diff --git a/libultraship/.github/workflows/apt-deps.txt b/libultraship/.github/workflows/apt-deps.txt new file mode 100644 index 000000000..83ed9b933 --- /dev/null +++ b/libultraship/.github/workflows/apt-deps.txt @@ -0,0 +1 @@ +libsdl2-dev libpng-dev libglew-dev libzip-dev zipcmp zipmerge ziptool ninja-build nlohmann-json3-dev libtinyxml2-dev libspdlog-dev \ No newline at end of file diff --git a/libultraship/.github/workflows/brew-deps.txt b/libultraship/.github/workflows/brew-deps.txt new file mode 100644 index 000000000..6c3248f9b --- /dev/null +++ b/libultraship/.github/workflows/brew-deps.txt @@ -0,0 +1 @@ +sdl2 libpng glew ninja libzip nlohmann-json tinyxml2 spdlog \ No newline at end of file diff --git a/libultraship/.github/workflows/tidy-format-validation.yml b/libultraship/.github/workflows/tidy-format-validation.yml new file mode 100644 index 000000000..035b7ee07 --- /dev/null +++ b/libultraship/.github/workflows/tidy-format-validation.yml @@ -0,0 +1,62 @@ +name: tidy-format-validation +on: [pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + tidy-format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y $(cat .github/workflows/apt-deps.txt) clang-tidy clang-format-14 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ runner.os }}-ccache + - name: Run clang-format + run: | + find src include -name "*.cpp" -o -name "*.h" | sed 's| |\\ |g' | xargs clang-format-14 -i + git diff --exit-code + - name: Install latest SDL + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + wget https://www.libsdl.org/release/SDL2-2.24.1.tar.gz + tar -xzf SDL2-2.24.1.tar.gz + cd SDL2-2.24.1 + ./configure + make -j 10 + sudo make install + - name: Install latest tinyxml2 + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + wget https://github.com/leethomason/tinyxml2/archive/refs/tags/10.0.0.tar.gz + tar -xzf 10.0.0.tar.gz + cd tinyxml2-10.0.0 + mkdir build + cd build + cmake .. + make + sudo make install + - name: Prepare compile_commands.json + run: | + cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + - name: Create results directory + run: | + mkdir clang-tidy-result + - name: Analyze + run: | + git diff -U0 HEAD^ -- 'src' 'include' ':!include/libultraship/color.h' ':!include/ship/utils/color.h' ':!include/libultraship/libultra/*' ':!src/libultraship/libultra/*' ':!src/fast/*' ':!include/fast/*' ':!src/ship/utils/AppleFolderManager.mm' | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml + - name: Save PR metadata + run: | + echo ${{ github.event.number }} > clang-tidy-result/pr-id.txt + echo ${{ github.event.pull_request.head.repo.full_name }} > clang-tidy-result/pr-head-repo.txt + echo ${{ github.event.pull_request.head.ref }} > clang-tidy-result/pr-head-ref.txt + - uses: actions/upload-artifact@v4 + with: + name: clang-tidy-result + path: clang-tidy-result/ diff --git a/libultraship/.github/workflows/tidy-result-publish.yml b/libultraship/.github/workflows/tidy-result-publish.yml new file mode 100644 index 000000000..29226acd5 --- /dev/null +++ b/libultraship/.github/workflows/tidy-result-publish.yml @@ -0,0 +1,74 @@ +name: tidy-result-publish +on: + workflow_run: + workflows: [ tidy-format-validation ] + types: [ completed ] +jobs: + clang-tidy-results: + # Trigger the job only if the previous (insecure) workflow completed successfully + if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-22.04 + steps: + - name: Download analysis results + uses: actions/github-script@v7.0.1 + with: + script: | + let artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + let matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "clang-tidy-result" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: "zip", + }); + let fs = require("fs"); + fs.writeFileSync("${{github.workspace}}/clang-tidy-result.zip", Buffer.from(download.data)); + - name: Set environment variables + run: | + mkdir clang-tidy-result + unzip clang-tidy-result.zip -d clang-tidy-result + echo "pr_id=$(cat clang-tidy-result/pr-id.txt)" >> $GITHUB_ENV + echo "pr_head_repo=$(cat clang-tidy-result/pr-head-repo.txt)" >> $GITHUB_ENV + echo "pr_head_ref=$(cat clang-tidy-result/pr-head-ref.txt)" >> $GITHUB_ENV + - uses: actions/checkout@v4 + with: + repository: ${{ env.pr_head_repo }} + ref: ${{ env.pr_head_ref }} + persist-credentials: false + - name: Redownload analysis results + uses: actions/github-script@v7.0.1 + with: + script: | + let artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + let matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "clang-tidy-result" + })[0]; + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: "zip", + }); + let fs = require("fs"); + fs.writeFileSync("${{github.workspace}}/clang-tidy-result.zip", Buffer.from(download.data)); + - name: Extract analysis results + run: | + mkdir clang-tidy-result + unzip clang-tidy-result.zip -d clang-tidy-result + - name: Run clang-tidy-pr-comments action + uses: platisd/clang-tidy-pr-comments@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + clang_tidy_fixes: clang-tidy-result/fixes.yml + pull_request_id: ${{ env.pr_id }} + request_changes: true diff --git a/libultraship/.gitignore b/libultraship/.gitignore new file mode 100644 index 000000000..de7026606 --- /dev/null +++ b/libultraship/.gitignore @@ -0,0 +1,367 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +!libultraship/Lib/** +libultraship/DebugObj/* +build/ +build-cmake/ +libultraship.a +libultraship.lib +ZAPDUtils.lib +.DS_Store +.vscode/ +.vs/ +.idea/ +cmake-build-** +cmake_install.cmake +*.a +*.o +*.pc +*.vcxproj +*.sln +*.filters +*.stamp +*.depend +*.lib +*.tlog +*.recipe +src/ship/install_config.h +# Removed jsonConfig and jsonConfigVersion +extern/nlohmann-json/*_jsonConfig* +*.pbxproj +*.xcworkspace +*.xcsettings +build* \ No newline at end of file diff --git a/libultraship/CMakeLists.txt b/libultraship/CMakeLists.txt new file mode 100644 index 000000000..b442e5d61 --- /dev/null +++ b/libultraship/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.24.0) + +option(NON_PORTABLE "Build a non-portable version" OFF) + +if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + option(SIGN_LIBRARY "Enable xcode signing" OFF) + option(BUNDLE_ID "Bundle ID for xcode signing" "com.example.libultraship") + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) +endif() + +project(libultraship LANGUAGES C CXX) + +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS") + enable_language(OBJCXX) + set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -fobjc-arc") + set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} -fobjc-arc") +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() + +include(cmake/Utils.cmake) +set(ADDITIONAL_LIB_INCLUDES "") + +# ========= Configuration Options ========= +option(INCLUDE_MPQ_SUPPORT "Enable StormLib and MPQ archive support" OFF) +option(GBI_UCODE "Specify the GBI ucode version" F3DEX_GBI_2) + +# =========== Dependencies ============= +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + include(cmake/dependencies/windows-vcpkg.cmake) +endif() + +include(cmake/dependencies/common.cmake) + +if (CMAKE_SYSTEM_NAME STREQUAL "Android") + include(cmake/dependencies/android.cmake) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + include(cmake/dependencies/mac.cmake) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "iOS") + include(cmake/dependencies/ios.cmake) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + include(cmake/dependencies/linux.cmake) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + include(cmake/dependencies/openbsd.cmake) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + include(cmake/dependencies/windows.cmake) +endif() + +# === Platform Specific Configuration === +if(CMAKE_SYSTEM_NAME STREQUAL "iOS") + include(cmake/ios-toolchain-populate.cmake) +endif() + +# =========== Sources ============= +add_subdirectory("src") diff --git a/libultraship/CODE_OF_CONDUCT.md b/libultraship/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..e893f1ac2 --- /dev/null +++ b/libultraship/CODE_OF_CONDUCT.md @@ -0,0 +1,134 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +kenixwhisperwind@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/libultraship/CONTRIBUTING.md b/libultraship/CONTRIBUTING.md new file mode 100644 index 000000000..fe86d69ac --- /dev/null +++ b/libultraship/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Preface +LUS accepts any and all contributions. You can interact with the project via PRs, issues, email (kenixwhisperwind@gmail.com), or [Discord](https://discord.gg/RQvdvavB). + +# Code of Conduct +Please review and abide by our [code of conduct](https://github.com/Kenix3/libultraship/blob/main/CODE_OF_CONDUCT.md). + +# Building +Please see the [readme](https://github.com/Kenix3/libultraship/blob/main/README.md) for building instructions. + +# Pull Requests +## Procedures +Our CI system will automatically check your PR to ensure that it fits [formatting guidelines](https://github.com/Kenix3/libultraship/blob/main/.clang-format), [linter guidelines](https://github.com/Kenix3/libultraship/blob/main/.clang-tidy), and that the code builds. The submitter of a PR is required to ensure their PR passes all CI checks. The submitter of the PR is encouraged to address all PR review comments in a timely manner to ensure a timely merge of the PR. +### Troubleshooting CI Errors +#### tidy-format-validation +If the tidy-format-validation check fails, then you need to run clang-format. Below is a command that can be used on Linux. Most modern IDEs have a clang-format plugin that can be used. The [.clang-format file can be found here](https://github.com/Kenix3/libultraship/blob/main/.clang-format) +##### Running clang-format +###### From within the LUS directory. +Ensure clang-format-14 is installed and available through your system path. + +Bash: +``` +find src include -name "*.cpp" -o -name "*.h" | sed 's| |\\ |g' | xargs clang-format-14 -i +``` + +Powershell: +``` +Get-ChildItem -Recurse -Path .\src -Include *.cpp, *.h -File | ForEach-Object { clang-format-14.exe -i $_.FullName } +``` \ No newline at end of file diff --git a/libultraship/LICENSE b/libultraship/LICENSE new file mode 100644 index 000000000..90e752ce6 --- /dev/null +++ b/libultraship/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 kenix3 kenixwhisperwind@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libultraship/README.md b/libultraship/README.md new file mode 100644 index 000000000..4ea527612 --- /dev/null +++ b/libultraship/README.md @@ -0,0 +1,56 @@ +# libultraship +libultraship (LUS) is a library meant to provide reimplementations of libultra (n64 sdk) functions that run on modern hardware. + +LUS uses an asset loading system where data is stored separately from the executable in an archive file ending in `.otr` or `.o2r`. `.otr` files are [`.mpq`](http://www.zezula.net/en/mpq/main.html) compatible files. `.o2r` files are `.zip` compatible files. This separation of data from executable follows modern design practices which are more mod friendly. All one needs to do is supply a patch `.otr` or `.o2r` and the system will automatically replace the data. + +## Contributing +LUS accepts any and all contributions. You can interact with the project via PRs, issues, email (kenixwhisperwind@gmail.com), or [Discord](https://discord.gg/shipofharkinian). +Please see [CONTRIBUTING.md](https://github.com/Kenix3/libultraship/blob/main/CONTRIBUTING.md) file for more information. + +## Versioning +We use semantic versioning. We have defined the API as: every C linkage function, variable, struct, class, public class method, or enum included from libultraship.h. + +## Building on Linux/Mac +``` +cmake -H. -Bbuild +cmake --build build +``` + +## Generating a Visual Studio `.sln` on Windows +``` +# Visual Studio 2022 +& 'C:\Program Files\CMake\bin\cmake' -DUSE_AUTO_VCPKG=true -S . -B "build/x64" -G "Visual Studio 17 2022" -T v142 -A x64 +# Visual Studio 2019 +& 'C:\Program Files\CMake\bin\cmake' -DUSE_AUTO_VCPKG=true -S . -B "build/x64" -G "Visual Studio 16 2019" -T v142 -A x64 +``` + +## To build on Windows +``` +& 'C:\Program Files\CMake\bin\cmake' --build .\build\x64 +``` + +## Sponsors +Thank you to JetBrains for providing their IDE [CLion](https://www.jetbrains.com/clion/) to me for free! + +## License +LUS is licensed under the [MIT](https://github.com/Kenix3/libultraship/blob/main/LICENSE) license. + +LUS makes use of the following third party libraries and resources: +- [Fast3D](https://github.com/Kenix3/libultraship/blob/main/src/fast/LICENSE.txt) (MIT) render display lists. +- [prism-processor](https://github.com/KiritoDv/prism-processor/blob/main/LICENSE) (MIT) shader preprocessor +- [ImGui](https://github.com/ocornut/imgui/blob/master/LICENSE.txt) (MIT) display UI. +- [StormLib](https://github.com/ladislav-zezula/StormLib/blob/master/LICENSE) (MIT) create and read `.mpq` compatible archive files. +- [libzip](https://github.com/nih-at/libzip/blob/main/LICENSE) (BSD-3-Clause) create and read `.zip` compatible archives +- [StrHash64](https://github.com/Kenix3/libultraship/blob/main/src/ship/utils/StrHash64.cpp) (MIT, zlib, BSD-3-Clause) provide crc64 implementation. +- [nlohmann-json](https://github.com/nlohmann/json/blob/develop/LICENSE.MIT) (MIT) json parsing and saving. +- [spdlog](https://github.com/gabime/spdlog/blob/v1.x/LICENSE) (MIT) logging +- [stb](https://github.com/nothings/stb/blob/master/LICENSE) (MIT) image conversion +- [thread-pool](https://github.com/bshoshany/thread-pool/blob/master/LICENSE.txt) (MIT) thread pool for the resource manager +- [tinyxml2](https://github.com/leethomason/tinyxml2/blob/master/LICENSE.txt) (zlib) parse XML files for resource loaders +- [zlib](https://github.com/madler/zlib/blob/develop/LICENSE) (zlib) compression used in StormLib +- [bzip2](https://github.com/libarchive/bzip2?tab=License-1-ov-file#readme) (bzip2) compression used in StormLib +- [sdl2](https://github.com/libsdl-org/SDL/blob/main/LICENSE.txt) (zlib) window manager, controllers, and audio player +- [glob_match](https://github.com/torvalds/linux/blob/d1bd5fa07667fcc3e38996ec42aef98761f23039/lib/glob.c) (Dual MIT/GPL) Glob pattern matching. +- [libgfxd](https://github.com/glankk/libgfxd/blob/master/LICENSE) (MIT) display list disassembler. +- [metal-cpp](https://github.com/bkaradzic/metal-cpp/blob/metal-cpp_26/LICENSE.txt) (Apache 2.0) interface to the Apple Metal rendering backend. +- [glew](https://github.com/nigels-com/glew/blob/master/LICENSE.txt) (modified BSD-3-Clause and MIT) OpenGL extension loading library. diff --git a/libultraship/cmake/Default.cmake b/libultraship/cmake/Default.cmake new file mode 100644 index 000000000..70bfa9038 --- /dev/null +++ b/libultraship/cmake/Default.cmake @@ -0,0 +1,65 @@ +################################################################################ +# Command for variable_watch. This command issues error message, if a variable +# is changed. If variable PROPERTY_READER_GUARD_DISABLED is TRUE nothing happens +# variable_watch( property_reader_guard) +################################################################################ +function(property_reader_guard VARIABLE ACCESS VALUE CURRENT_LIST_FILE STACK) + if("${PROPERTY_READER_GUARD_DISABLED}") + return() + endif() + + if("${ACCESS}" STREQUAL "MODIFIED_ACCESS") + message(FATAL_ERROR + " Variable ${VARIABLE} is not supposed to be changed.\n" + " It is used only for reading target property ${VARIABLE}.\n" + " Use\n" + " set_target_properties(\"\" PROPERTIES \"${VARIABLE}\" \"\")\n" + " or\n" + " set_target_properties(\"\" PROPERTIES \"${VARIABLE}_\" \"\")\n" + " instead.\n") + endif() +endfunction() + +################################################################################ +# Create variable with generator expression that expands to value of +# target property _. If property is empty or not set then property +# is used instead. Variable has watcher property_reader_guard that +# doesn't allow to edit it. +# create_property_reader() +# Input: +# name - Name of watched property and output variable +################################################################################ +function(create_property_reader NAME) + set(PROPERTY_READER_GUARD_DISABLED TRUE) + set(CONFIG_VALUE "$>>>") + set(IS_CONFIG_VALUE_EMPTY "$") + set(GENERAL_VALUE "$>") + set("${NAME}" "$" PARENT_SCOPE) + variable_watch("${NAME}" property_reader_guard) +endfunction() + +################################################################################ +# Set property $_${PROPS_CONFIG_U} of ${PROPS_TARGET} to +# set_config_specific_property( ) +# Input: +# name - Prefix of property name +# value - New value +################################################################################ +function(set_config_specific_property NAME VALUE) + set_target_properties("${PROPS_TARGET}" PROPERTIES "${NAME}_${PROPS_CONFIG_U}" "${VALUE}") +endfunction() + +################################################################################ + +create_property_reader("TARGET_NAME") +create_property_reader("OUTPUT_DIRECTORY") + +set_config_specific_property("TARGET_NAME" "${PROPS_TARGET}") +set_config_specific_property("OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("ARCHIVE_OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("LIBRARY_OUTPUT_NAME" "${TARGET_NAME}") +set_config_specific_property("RUNTIME_OUTPUT_NAME" "${TARGET_NAME}") + +set_config_specific_property("ARCHIVE_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") +set_config_specific_property("LIBRARY_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") +set_config_specific_property("RUNTIME_OUTPUT_DIRECTORY" "${OUTPUT_DIRECTORY}") \ No newline at end of file diff --git a/libultraship/cmake/DefaultCXX.cmake b/libultraship/cmake/DefaultCXX.cmake new file mode 100644 index 000000000..0d3984f29 --- /dev/null +++ b/libultraship/cmake/DefaultCXX.cmake @@ -0,0 +1,16 @@ +include("${CMAKE_CURRENT_LIST_DIR}/Default.cmake") + +set_config_specific_property("OUTPUT_DIRECTORY" "${CMAKE_SOURCE_DIR}$<$>:/${CMAKE_VS_PLATFORM_NAME}>/${PROPS_CONFIG}") + +if(MSVC) + create_property_reader("DEFAULT_CXX_EXCEPTION_HANDLING") + create_property_reader("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT") + + # set_target_properties("${PROPS_TARGET}" PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + set_config_specific_property("DEFAULT_CXX_EXCEPTION_HANDLING" "/EHsc") + if (CMAKE_C_COMPILER_LAUNCHER STREQUAL "sccache") + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Z7") + else() + set_config_specific_property("DEFAULT_CXX_DEBUG_INFORMATION_FORMAT" "/Zi") + endif() +endif() \ No newline at end of file diff --git a/libultraship/cmake/HandleCompilerRT.cmake b/libultraship/cmake/HandleCompilerRT.cmake new file mode 100644 index 000000000..dad263487 --- /dev/null +++ b/libultraship/cmake/HandleCompilerRT.cmake @@ -0,0 +1,123 @@ +# Check if the compiler-rt library file path exists. +# If found, cache the path in: +# COMPILER_RT_LIBRARY-- +# If err_flag is true OR path not found, emit a message and set: +# COMPILER_RT_LIBRARY-- to NOTFOUND +function(cache_compiler_rt_library err_flag name target library_file) + if(err_flag OR NOT EXISTS "${library_file}") + message(STATUS "Failed to find compiler-rt ${name} library for ${target}") + set(COMPILER_RT_LIBRARY_${name}_${target} "NOTFOUND" CACHE INTERNAL + "compiler-rt ${name} library for ${target}") + else() + message(STATUS "Found compiler-rt ${name} library: ${library_file}") + set(COMPILER_RT_LIBRARY_${name}_${target} "${library_file}" CACHE INTERNAL + "compiler-rt ${name} library for ${target}") + endif() +endfunction() + +function(get_component_name name variable) + if(APPLE) + if(NOT name MATCHES "builtins.*") + set(component_name "${name}_") + endif() + if (CMAKE_OSX_SYSROOT MATCHES ".+MacOSX.+") + set(component_name "${component_name}osx") + + elseif (CMAKE_OSX_SYSROOT MATCHES ".+iPhoneOS.+") + set(component_name "${component_name}ios") + elseif (CMAKE_OSX_SYSROOT MATCHES ".+iPhoneSimulator.+") + set(component_name "${component_name}iossim") + + elseif (CMAKE_OSX_SYSROOT MATCHES ".+AppleTVOS.+") + set(component_name "${component_name}tvos") + elseif (CMAKE_OSX_SYSROOT MATCHES ".+AppleTVSimulator.+") + set(component_name "${component_name}tvossim") + + elseif (CMAKE_OSX_SYSROOT MATCHES ".+WatchOS.+") + set(component_name "${component_name}watchos") + elseif (CMAKE_OSX_SYSROOT MATCHES ".+WatchSimulator.+") + set(component_name "${component_name}watchossim") + else() + message(WARNING "Unknown Apple SDK ${CMAKE_OSX_SYSROOT}, we don't know which compiler-rt library suffix to use.") + endif() + else() + set(component_name "${name}") + endif() + set(${variable} "${component_name}" PARENT_SCOPE) +endfunction() + +# Find the path to compiler-rt library `name` (e.g. "builtins") for the +# specified `TARGET` (e.g. "x86_64-linux-gnu") and return it in `variable`. +# This calls cache_compiler_rt_library that caches the path to speed up +# repeated invocations with the same `name` and `target`. +function(find_compiler_rt_library name variable) + cmake_parse_arguments(ARG "" "TARGET;FLAGS" "" ${ARGN}) + # While we can use compiler-rt runtimes with other compilers, we need to + # query the compiler for runtime location and thus we require Clang. + if(NOT CMAKE_CXX_COMPILER_ID MATCHES Clang) + set(${variable} "NOTFOUND" PARENT_SCOPE) + return() + endif() + set(target "${ARG_TARGET}") + if(NOT target AND CMAKE_CXX_COMPILER_TARGET) + set(target "${CMAKE_CXX_COMPILER_TARGET}") + endif() + if(NOT DEFINED COMPILER_RT_LIBRARY_builtins_${target}) + # If the cache variable is not defined, invoke Clang and then + # set it with cache_compiler_rt_library. + set(clang_command ${CMAKE_CXX_COMPILER} "${ARG_FLAGS}") + if(target) + list(APPEND clang_command "--target=${target}") + endif() + get_property(cxx_flags CACHE CMAKE_CXX_FLAGS PROPERTY VALUE) + string(REPLACE " " ";" cxx_flags "${cxx_flags}") + list(APPEND clang_command ${cxx_flags}) + set(cmd_prefix "") + if(MSVC AND ${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + set(cmd_prefix "/clang:") + endif() + execute_process( + COMMAND ${clang_command} "${cmd_prefix}--rtlib=compiler-rt" "${cmd_prefix}-print-libgcc-file-name" + RESULT_VARIABLE had_error + OUTPUT_VARIABLE library_file + ) + string(STRIP "${library_file}" library_file) + file(TO_CMAKE_PATH "${library_file}" library_file) + get_filename_component(dirname ${library_file} DIRECTORY) + if(APPLE) + execute_process( + COMMAND ${clang_command} "--print-resource-dir" + RESULT_VARIABLE had_error + OUTPUT_VARIABLE resource_dir + ) + string(STRIP "${resource_dir}" resource_dir) + set(dirname "${resource_dir}/lib/darwin") + endif() + get_filename_component(basename ${library_file} NAME) + if(basename MATCHES ".*clang_rt\.([a-z0-9_\-]+)\.(a|lib)") + set(from_name ${CMAKE_MATCH_1}) + get_component_name(${CMAKE_MATCH_1} to_name) + string(REPLACE "${from_name}" "${to_name}" basename "${basename}") + set(library_file "${dirname}/${basename}") + cache_compiler_rt_library(${had_error} builtins "${target}" "${library_file}") + endif() + endif() + if(NOT COMPILER_RT_LIBRARY_builtins_${target}) + set(${variable} "NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT DEFINED COMPILER_RT_LIBRARY_${name}_${target}) + # Clang gives only the builtins library path. Other library paths are + # obtained by substituting "builtins" with ${name} in the builtins + # path and then checking if the resultant path exists. The result of + # this check is also cached by cache_compiler_rt_library. + set(library_file "${COMPILER_RT_LIBRARY_builtins_${target}}") + if(library_file MATCHES ".*clang_rt\.([a-z0-9_\-]+)\.(a|lib)") + set(from_name ${CMAKE_MATCH_0}) + get_component_name(${name} to_name) + string(REPLACE "${from_name}" "${to_name}" library_file "${library_file}") + cache_compiler_rt_library(FALSE "${name}" "${target}" "${library_file}") + endif() + endif() + set(${variable} "${COMPILER_RT_LIBRARY_${name}_${target}}" PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/libultraship/cmake/Utils.cmake b/libultraship/cmake/Utils.cmake new file mode 100644 index 000000000..c691eefc6 --- /dev/null +++ b/libultraship/cmake/Utils.cmake @@ -0,0 +1,248 @@ +# utils file for projects came from visual studio solution with cmake-converter. + +################################################################################ +# Wrap each token of the command with condition +################################################################################ +cmake_policy(PUSH) +cmake_policy(SET CMP0054 NEW) +macro(prepare_commands) + unset(TOKEN_ROLE) + unset(COMMANDS) + foreach(TOKEN ${ARG_COMMANDS}) + if("${TOKEN}" STREQUAL "COMMAND") + set(TOKEN_ROLE "KEYWORD") + elseif("${TOKEN_ROLE}" STREQUAL "KEYWORD") + set(TOKEN_ROLE "CONDITION") + elseif("${TOKEN_ROLE}" STREQUAL "CONDITION") + set(TOKEN_ROLE "COMMAND") + elseif("${TOKEN_ROLE}" STREQUAL "COMMAND") + set(TOKEN_ROLE "ARG") + endif() + + if("${TOKEN_ROLE}" STREQUAL "KEYWORD") + list(APPEND COMMANDS "${TOKEN}") + elseif("${TOKEN_ROLE}" STREQUAL "CONDITION") + set(CONDITION ${TOKEN}) + elseif("${TOKEN_ROLE}" STREQUAL "COMMAND") + list(APPEND COMMANDS "$<$:${DUMMY}>$<${CONDITION}:${TOKEN}>") + elseif("${TOKEN_ROLE}" STREQUAL "ARG") + list(APPEND COMMANDS "$<${CONDITION}:${TOKEN}>") + endif() + endforeach() +endmacro() +cmake_policy(POP) + +################################################################################ +# Transform all the tokens to absolute paths +################################################################################ +macro(prepare_output) + unset(OUTPUT) + foreach(TOKEN ${ARG_OUTPUT}) + if(IS_ABSOLUTE ${TOKEN}) + list(APPEND OUTPUT "${TOKEN}") + else() + list(APPEND OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/${TOKEN}") + endif() + endforeach() +endmacro() + +################################################################################ +# Parse add_custom_command_if args. +# +# Input: +# PRE_BUILD - Pre build event option +# PRE_LINK - Pre link event option +# POST_BUILD - Post build event option +# TARGET - Target +# OUTPUT - List of output files +# DEPENDS - List of files on which the command depends +# COMMANDS - List of commands(COMMAND condition1 commannd1 args1 COMMAND +# condition2 commannd2 args2 ...) +# Output: +# OUTPUT - Output files +# DEPENDS - Files on which the command depends +# COMMENT - Comment +# PRE_BUILD - TRUE/FALSE +# PRE_LINK - TRUE/FALSE +# POST_BUILD - TRUE/FALSE +# TARGET - Target name +# COMMANDS - Prepared commands(every token is wrapped in CONDITION) +# NAME - Unique name for custom target +# STEP - PRE_BUILD/PRE_LINK/POST_BUILD +################################################################################ +function(add_custom_command_if_parse_arguments) + cmake_parse_arguments("ARG" "PRE_BUILD;PRE_LINK;POST_BUILD" "TARGET;COMMENT" "DEPENDS;OUTPUT;COMMANDS" ${ARGN}) + + if(WIN32) + set(DUMMY "cd.") + elseif(UNIX) + set(DUMMY "true") + endif() + + prepare_commands() + prepare_output() + + set(DEPENDS "${ARG_DEPENDS}") + set(COMMENT "${ARG_COMMENT}") + set(PRE_BUILD "${ARG_PRE_BUILD}") + set(PRE_LINK "${ARG_PRE_LINK}") + set(POST_BUILD "${ARG_POST_BUILD}") + set(TARGET "${ARG_TARGET}") + if(PRE_BUILD) + set(STEP "PRE_BUILD") + elseif(PRE_LINK) + set(STEP "PRE_LINK") + elseif(POST_BUILD) + set(STEP "POST_BUILD") + endif() + set(NAME "${TARGET}_${STEP}") + + set(OUTPUT "${OUTPUT}" PARENT_SCOPE) + set(DEPENDS "${DEPENDS}" PARENT_SCOPE) + set(COMMENT "${COMMENT}" PARENT_SCOPE) + set(PRE_BUILD "${PRE_BUILD}" PARENT_SCOPE) + set(PRE_LINK "${PRE_LINK}" PARENT_SCOPE) + set(POST_BUILD "${POST_BUILD}" PARENT_SCOPE) + set(TARGET "${TARGET}" PARENT_SCOPE) + set(COMMANDS "${COMMANDS}" PARENT_SCOPE) + set(STEP "${STEP}" PARENT_SCOPE) + set(NAME "${NAME}" PARENT_SCOPE) +endfunction() + +################################################################################ +# Add conditional custom command +# +# Generating Files +# The first signature is for adding a custom command to produce an output: +# add_custom_command_if( +# +# +# +# [COMMAND condition command2 [args2...]] +# [DEPENDS [depends...]] +# [COMMENT comment] +# +# Build Events +# add_custom_command_if( +# +# +# +# [COMMAND condition command2 [args2...]] +# [COMMENT comment] +# +# Input: +# output - Output files the command is expected to produce +# condition - Generator expression for wrapping the command +# command - Command-line(s) to execute at build time. +# args - Command`s args +# depends - Files on which the command depends +# comment - Display the given message before the commands are executed at +# build time. +# PRE_BUILD - Run before any other rules are executed within the target +# PRE_LINK - Run after sources have been compiled but before linking the +# binary +# POST_BUILD - Run after all other rules within the target have been +# executed +################################################################################ +function(add_custom_command_if) + add_custom_command_if_parse_arguments(${ARGN}) + + if(OUTPUT AND TARGET) + message(FATAL_ERROR "Wrong syntax. A TARGET and OUTPUT can not both be specified.") + endif() + + if(OUTPUT) + add_custom_command(OUTPUT ${OUTPUT} + ${COMMANDS} + DEPENDS ${DEPENDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + elseif(TARGET) + if(PRE_BUILD AND NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio") + add_custom_target( + ${NAME} + ${COMMANDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + add_dependencies(${TARGET} ${NAME}) + else() + add_custom_command( + TARGET ${TARGET} + ${STEP} + ${COMMANDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT ${COMMENT}) + endif() + else() + message(FATAL_ERROR "Wrong syntax. A TARGET or OUTPUT must be specified.") + endif() +endfunction() + +################################################################################ +# Use props file for a target and configs +# use_props( ) +# Inside there are following variables: +# PROPS_TARGET - +# PROPS_CONFIG - One of +# PROPS_CONFIG_U - Uppercase PROPS_CONFIG +# Input: +# target - Target to apply props file +# configs - Build configurations to apply props file +# props_file - CMake script +################################################################################ +macro(use_props TARGET CONFIGS PROPS_FILE) + set(PROPS_TARGET "${TARGET}") + foreach(PROPS_CONFIG ${CONFIGS}) + string(TOUPPER "${PROPS_CONFIG}" PROPS_CONFIG_U) + + get_filename_component(ABSOLUTE_PROPS_FILE "${PROPS_FILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") + if(EXISTS "${ABSOLUTE_PROPS_FILE}") + include("${ABSOLUTE_PROPS_FILE}") + else() + message(WARNING "Corresponding cmake file from props \"${ABSOLUTE_PROPS_FILE}\" doesn't exist") + endif() + endforeach() +endmacro() + +################################################################################ +# Add compile options to source file +# source_file_compile_options( [compile_options...]) +# Input: +# source_file - Source file +# compile_options - Options to add to COMPILE_FLAGS property +################################################################################ +function(source_file_compile_options SOURCE_FILE) + if("${ARGC}" LESS_EQUAL "1") + return() + endif() + + get_source_file_property(COMPILE_OPTIONS "${SOURCE_FILE}" COMPILE_OPTIONS) + + if(COMPILE_OPTIONS) + list(APPEND COMPILE_OPTIONS ${ARGN}) + else() + set(COMPILE_OPTIONS "${ARGN}") + endif() + + set_source_files_properties("${SOURCE_FILE}" PROPERTIES COMPILE_OPTIONS "${COMPILE_OPTIONS}") +endfunction() + +################################################################################ +# Default properties of visual studio projects +################################################################################ +set(DEFAULT_CXX_PROPS "${CMAKE_CURRENT_LIST_DIR}/DefaultCXX.cmake") + +function(get_linux_lsb_release_information) + find_program(LSB_RELEASE_EXEC lsb_release) + if(NOT LSB_RELEASE_EXEC) + message(FATAL_ERROR "Could not detect lsb_release executable, can not gather required information") + endif() + + execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --id OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --release OUTPUT_VARIABLE LSB_RELEASE_VERSION_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --codename OUTPUT_VARIABLE LSB_RELEASE_CODENAME_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(LSB_RELEASE_ID_SHORT "${LSB_RELEASE_ID_SHORT}" PARENT_SCOPE) + set(LSB_RELEASE_VERSION_SHORT "${LSB_RELEASE_VERSION_SHORT}" PARENT_SCOPE) + set(LSB_RELEASE_CODENAME_SHORT "${LSB_RELEASE_CODENAME_SHORT}" PARENT_SCOPE) +endfunction() diff --git a/libultraship/cmake/automate-vcpkg.cmake b/libultraship/cmake/automate-vcpkg.cmake new file mode 100644 index 000000000..4764c6428 --- /dev/null +++ b/libultraship/cmake/automate-vcpkg.cmake @@ -0,0 +1,193 @@ +#------------------------------------------------------------------------------------------------------------ +# +# Automate-VCPKG by Andre Taulien +# =============================== +# +# Project Repository: https://github.com/REGoth-project/Automate-VCPKG +# License ..........: MIT, see end of file. +# +# Based on: https://github.com/sutambe/cpptruths/blob/vcpkg_cmake_blog/cpp0x/vcpkg_test/CMakeLists.txt +# +# +# While [Vcpkg](https://github.com/microsoft/vcpkg) on it's own is awesome, it does add +# a little bit of complexity to getting a project to build. Even more if the one trying +# to compile your application is not too fond of the commandline. Additionally, CMake +# commands tend to get rather long with the toolchain path. +# +# To keep things simple for new users who just want to get the project to build, this +# script offers a solution. +# +# Lets assume your main `CMakelists.txt` looks something like this: +# +# cmake_minimum_required (VERSION 3.12.0) +# project (MyProject) +# +# add_executable(MyExecutable main.c) +# +# To integrate Vcpkg into that `CMakelists.txt`, simple put the following lines before the +# call to `project(MyProject)`: +# +# include(cmake/automate-vcpkg.cmake) +# +# vcpkg_bootstrap() +# vcpkg_install_packages(libsquish physfs) +# +# The call to `vcpkg_bootstrap()` will clone the official Vcpkg repository and bootstrap it. +# If it detected an existing environment variable defining a valid `VCPKG_ROOT`, it will +# update the existing installation of Vcpkg. +# +# Arguments to `vcpkg_install_packages()` are the packages you want to install using Vcpkg. +# +# If you want to keep the possibility for users to chose their own copy of Vcpkg, you can +# simply not run the code snippet mentioned above, something like this will work: +# +# option(SKIP_AUTOMATE_VCPKG "When ON, you will need to built the packages +# required by MyProject on your own or supply your own vcpkg toolchain.") +# +# if (NOT SKIP_AUTOMATE_VCPKG) +# include(cmake/automate-vcpkg.cmake) +# +# vcpkg_bootstrap() +# vcpkg_install_packages(libsquish physfs) +# endif() +# +# Then, the user has to supply the packages on their own, be it through Vcpkg or manually +# specifying their locations. +#------------------------------------------------------------------------------------------------------------ + +cmake_minimum_required (VERSION 3.12) + +if(WIN32) + set(VCPKG_FALLBACK_ROOT ${CMAKE_CURRENT_BINARY_DIR}/vcpkg CACHE STRING "vcpkg configuration directory to use if vcpkg was not installed on the system before") +else() + set(VCPKG_FALLBACK_ROOT ${CMAKE_CURRENT_BINARY_DIR}/.vcpkg CACHE STRING "vcpkg configuration directory to use if vcpkg was not installed on the system before") +endif() + +# On Windows, Vcpkg defaults to x86, even on x64 systems. If we're +# doing a 64-bit build, we need to fix that. +if (WIN32) + + # Since the compiler checks haven't run yet, we need to figure + # out the value of CMAKE_SIZEOF_VOID_P ourselfs. + # We also need to make sure the triplet isn't already set because + # ARM64 will be overriden by the 64bit check + + include(CheckTypeSize) + enable_language(C) + check_type_size("void*" SIZEOF_VOID_P BUILTIN_TYPES_ONLY) + + if (SIZEOF_VOID_P EQUAL 8 AND NOT DEFINED ${CMAKE_VS_PLATFORM_NAME}) + message(STATUS "Using Vcpkg triplet 'x64-windows'") + + set(VCPKG_TRIPLET x64-windows) + endif() +endif() + +if(NOT DEFINED VCPKG_ROOT) + if(NOT DEFINED ENV{VCPKG_ROOT}) + set(VCPKG_ROOT ${VCPKG_FALLBACK_ROOT}) + else() + set(VCPKG_ROOT $ENV{VCPKG_ROOT}) + endif() +endif() + +# Installs a new copy of Vcpkg or updates an existing one +macro(vcpkg_bootstrap) + _install_or_update_vcpkg() + + # Find out whether the user supplied their own VCPKG toolchain file + if(NOT DEFINED ${CMAKE_TOOLCHAIN_FILE}) + # We know this wasn't set before so we need point the toolchain file to the newly found VCPKG_ROOT + set(CMAKE_TOOLCHAIN_FILE ${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake CACHE STRING "") + + # Just setting vcpkg.cmake as toolchain file does not seem to actually pull in the code + include(${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) + + set(AUTOMATE_VCPKG_USE_SYSTEM_VCPKG OFF) + else() + # VCPKG_ROOT has been defined by the toolchain file already + set(AUTOMATE_VCPKG_USE_SYSTEM_VCPKG ON) + endif() + + message(STATUS "Automate VCPKG status:") + message(STATUS " VCPKG_ROOT.....: ${VCPKG_ROOT}") + message(STATUS " VCPKG_EXEC.....: ${VCPKG_EXEC}") + message(STATUS " VCPKG_BOOTSTRAP: ${VCPKG_BOOTSTRAP}") +endmacro() + +macro(_install_or_update_vcpkg) + if(NOT EXISTS ${VCPKG_ROOT}) + message(STATUS "Cloning vcpkg in ${VCPKG_ROOT}") + execute_process(COMMAND git clone https://github.com/Microsoft/vcpkg.git ${VCPKG_ROOT} --depth 1) + + # If a reproducible build is desired (and potentially old libraries are # ok), uncomment the + # following line and pin the vcpkg repository to a specific githash. + # execute_process(COMMAND git checkout 745a0aea597771a580d0b0f4886ea1e3a94dbca6 WORKING_DIRECTORY ${VCPKG_ROOT}) + else() + # The following command has no effect if the vcpkg repository is in a detached head state. + message(STATUS "Auto-updating vcpkg in ${VCPKG_ROOT}") + execute_process(COMMAND git pull WORKING_DIRECTORY ${VCPKG_ROOT}) + endif() + + if(NOT EXISTS ${VCPKG_ROOT}/README.md) + message(FATAL_ERROR "***** FATAL ERROR: Could not clone vcpkg *****") + endif() + + if(WIN32) + set(VCPKG_EXEC ${VCPKG_ROOT}/vcpkg.exe) + set(VCPKG_BOOTSTRAP ${VCPKG_ROOT}/bootstrap-vcpkg.bat) + else() + set(VCPKG_EXEC ${VCPKG_ROOT}/vcpkg) + set(VCPKG_BOOTSTRAP ${VCPKG_ROOT}/bootstrap-vcpkg.sh) + endif() + + if(NOT EXISTS ${VCPKG_EXEC}) + message("Bootstrapping vcpkg in ${VCPKG_ROOT}") + execute_process(COMMAND ${VCPKG_BOOTSTRAP} WORKING_DIRECTORY ${VCPKG_ROOT}) + endif() + + if(NOT EXISTS ${VCPKG_EXEC}) + message(FATAL_ERROR "***** FATAL ERROR: Could not bootstrap vcpkg *****") + endif() + +endmacro() + +# Installs the list of packages given as parameters using Vcpkg +macro(vcpkg_install_packages) + + # Need the given list to be space-separated + #string (REPLACE ";" " " PACKAGES_LIST_STR "${ARGN}") + + message(STATUS "Installing/Updating the following vcpkg-packages: ${PACKAGES_LIST_STR}") + + if (VCPKG_TRIPLET) + set(ENV{VCPKG_DEFAULT_TRIPLET} "${VCPKG_TRIPLET}") + endif() + + execute_process( + COMMAND ${VCPKG_EXEC} install ${ARGN} + WORKING_DIRECTORY ${VCPKG_ROOT} + ) +endmacro() + +# MIT License +# +# Copyright (c) 2019 REGoth-project +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. diff --git a/libultraship/cmake/cvars.cmake b/libultraship/cmake/cvars.cmake new file mode 100644 index 000000000..19108e0f8 --- /dev/null +++ b/libultraship/cmake/cvars.cmake @@ -0,0 +1,47 @@ +set(CVAR_VSYNC_ENABLED "gVsyncEnabled" CACHE STRING "") +set(CVAR_Z_FIGHTING_MODE "gZFightingMode" CACHE STRING "") +set(CVAR_INTERNAL_RESOLUTION "gInternalResolution" CACHE STRING "") +set(CVAR_MSAA_VALUE "gMSAAValue" CACHE STRING "") +set(CVAR_SDL_WINDOWED_FULLSCREEN "gSdlWindowedFullscreen" CACHE STRING "") +set(CVAR_TEXTURE_FILTER "gTextureFilter" CACHE STRING "") +set(CVAR_IMGUI_CONTROLLER_NAV "gControlNav" CACHE STRING "") +set(CVAR_CONSOLE_WINDOW_OPEN "gConsoleEnabled" CACHE STRING "") +set(CVAR_CONTROLLER_CONFIGURATION_WINDOW_OPEN "gControllerConfigurationEnabled" CACHE STRING "") +set(CVAR_CONTROLLER_DISCONNECTED_WINDOW_OPEN "gControllerDisconnectedWindowEnabled" CACHE STRING "") +set(CVAR_CONTROLLER_REORDERING_WINDOW_OPEN "gControllerReorderingWindowEnabled" CACHE STRING "") +set(CVAR_GFX_DEBUGGER_WINDOW_OPEN "gGfxDebuggerEnabled" CACHE STRING "") +set(CVAR_STATS_WINDOW_OPEN "gStatsEnabled" CACHE STRING "") +set(CVAR_ENABLE_MULTI_VIEWPORTS "gEnableMultiViewports" CACHE STRING "") +set(CVAR_LOW_RES_MODE "gLowResMode" CACHE STRING "") +set(CVAR_SIMULATED_INPUT_LAG "gSimulatedInputLag" CACHE STRING "") +set(CVAR_GAME_OVERLAY_FONT "gOverlayFont" CACHE STRING "") +set(CVAR_MENU_BAR_OPEN "gOpenMenuBar" CACHE STRING "") +set(CVAR_PREFIX_CONTROLLERS "gControllers" CACHE STRING "") +set(CVAR_PREFIX_ADVANCED_RESOLUTION "gAdvancedResolution" CACHE STRING "") +set(CVAR_AUDIO_CHANNELS_SETTING "gAudioChannelsSetting" CACHE STRING "") +set(CVAR_ALLOW_BACKGROUND_INPUTS "gAllowBackgroundInputs" CACHE STRING "") + +add_compile_definitions( + CVAR_VSYNC_ENABLED="${CVAR_VSYNC_ENABLED}" + CVAR_Z_FIGHTING_MODE="${CVAR_Z_FIGHTING_MODE}" + CVAR_INTERNAL_RESOLUTION="${CVAR_INTERNAL_RESOLUTION}" + CVAR_MSAA_VALUE="${CVAR_MSAA_VALUE}" + CVAR_SDL_WINDOWED_FULLSCREEN="${CVAR_SDL_WINDOWED_FULLSCREEN}" + CVAR_TEXTURE_FILTER="${CVAR_TEXTURE_FILTER}" + CVAR_IMGUI_CONTROLLER_NAV="${CVAR_IMGUI_CONTROLLER_NAV}" + CVAR_CONSOLE_WINDOW_OPEN="${CVAR_CONSOLE_WINDOW_OPEN}" + CVAR_CONTROLLER_CONFIGURATION_WINDOW_OPEN="${CVAR_CONTROLLER_CONFIGURATION_WINDOW_OPEN}" + CVAR_CONTROLLER_DISCONNECTED_WINDOW_OPEN="${CVAR_CONTROLLER_DISCONNECTED_WINDOW_OPEN}" + CVAR_CONTROLLER_REORDERING_WINDOW_OPEN="${CVAR_CONTROLLER_REORDERING_WINDOW_OPEN}" + CVAR_GFX_DEBUGGER_WINDOW_OPEN="${CVAR_GFX_DEBUGGER_WINDOW_OPEN}" + CVAR_STATS_WINDOW_OPEN="${CVAR_STATS_WINDOW_OPEN}" + CVAR_ENABLE_MULTI_VIEWPORTS="${CVAR_ENABLE_MULTI_VIEWPORTS}" + CVAR_LOW_RES_MODE="${CVAR_LOW_RES_MODE}" + CVAR_SIMULATED_INPUT_LAG="${CVAR_SIMULATED_INPUT_LAG}" + CVAR_GAME_OVERLAY_FONT="${CVAR_GAME_OVERLAY_FONT}" + CVAR_MENU_BAR_OPEN="${CVAR_MENU_BAR_OPEN}" + CVAR_PREFIX_CONTROLLERS="${CVAR_PREFIX_CONTROLLERS}" + CVAR_PREFIX_ADVANCED_RESOLUTION="${CVAR_PREFIX_ADVANCED_RESOLUTION}" + CVAR_AUDIO_CHANNELS_SETTING="${CVAR_AUDIO_CHANNELS_SETTING}" + CVAR_ALLOW_BACKGROUND_INPUTS="${CVAR_ALLOW_BACKGROUND_INPUTS}" +) diff --git a/libultraship/cmake/dependencies/android.cmake b/libultraship/cmake/dependencies/android.cmake new file mode 100644 index 000000000..36666a0fb --- /dev/null +++ b/libultraship/cmake/dependencies/android.cmake @@ -0,0 +1,73 @@ +include(FetchContent) + +#=================== SDL2 =================== +find_package(SDL2 QUIET) +if (NOT ${SDL2_FOUND}) + FetchContent_Declare( + SDL2 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG release-2.32.10 + ) + message("SDL2 not found. Downloading now...") + FetchContent_MakeAvailable(SDL2) + message("SDL2 downloaded to " ${FETCHCONTENT_BASE_DIR}/sdl2-src) +endif() + +#=================== nlohmann-json =================== +find_package(nlohmann_json QUIET) +if (NOT ${nlohmann_json_FOUND}) + FetchContent_Declare( + nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.12.0 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(nlohmann_json) +endif() + +#=================== tinyxml2 =================== +find_package(tinyxml2 QUIET) +if (NOT ${tinyxml2_FOUND}) + set(tinyxml2_BUILD_TESTING OFF) + FetchContent_Declare( + tinyxml2 + GIT_REPOSITORY https://github.com/leethomason/tinyxml2.git + GIT_TAG 11.0.0 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(tinyxml2) +endif() + +#=================== spdlog =================== +find_package(spdlog QUIET) +if (NOT ${spdlog_FOUND}) + FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.16.0 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(spdlog) +endif() + +#=================== libzip =================== +find_package(libzip QUIET) +if (NOT ${libzip_FOUND}) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + set(BUILD_TOOLS OFF) + set(BUILD_REGRESS OFF) + set(BUILD_EXAMPLES OFF) + set(BUILD_DOC OFF) + set(BUILD_OSSFUZZ OFF) + set(BUILD_SHARED_LIBS OFF) + FetchContent_Declare( + libzip + GIT_REPOSITORY https://github.com/nih-at/libzip.git + GIT_TAG v1.11.4 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(libzip) + list(APPEND ADDITIONAL_LIB_INCLUDES ${libzip_SOURCE_DIR}/lib ${libzip_BINARY_DIR}) +endif() + +target_link_libraries(ImGui PUBLIC SDL2::SDL2) diff --git a/libultraship/cmake/dependencies/common.cmake b/libultraship/cmake/dependencies/common.cmake new file mode 100644 index 000000000..8f5813ac5 --- /dev/null +++ b/libultraship/cmake/dependencies/common.cmake @@ -0,0 +1,113 @@ +include(FetchContent) + +find_package(OpenGL QUIET) + +#=================== ImGui =================== +set(imgui_fixes_and_config_patch_file ${CMAKE_CURRENT_SOURCE_DIR}/cmake/dependencies/patches/imgui-fixes-and-config.patch) +set(imgui_apply_patch_command ${CMAKE_COMMAND} -Dpatch_file=${imgui_fixes_and_config_patch_file} -Dwith_reset=TRUE -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/dependencies/git-patch.cmake) + +FetchContent_Declare( + ImGui + GIT_REPOSITORY https://github.com/ocornut/imgui.git + GIT_TAG v1.91.9b-docking + PATCH_COMMAND ${imgui_apply_patch_command} +) +FetchContent_MakeAvailable(ImGui) +list(APPEND ADDITIONAL_LIB_INCLUDES ${imgui_SOURCE_DIR} ${imgui_SOURCE_DIR}/backends) + +add_library(ImGui STATIC) +set_property(TARGET ImGui PROPERTY CXX_STANDARD 20) + +target_sources(ImGui + PRIVATE + ${imgui_SOURCE_DIR}/imgui_demo.cpp + ${imgui_SOURCE_DIR}/imgui_draw.cpp + ${imgui_SOURCE_DIR}/imgui_tables.cpp + ${imgui_SOURCE_DIR}/imgui_widgets.cpp + ${imgui_SOURCE_DIR}/imgui.cpp +) + +target_sources(ImGui + PRIVATE + ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp + ${imgui_SOURCE_DIR}/backends/imgui_impl_sdl2.cpp +) + +target_include_directories(ImGui PUBLIC ${imgui_SOURCE_DIR} ${imgui_SOURCE_DIR}/backends PRIVATE ${SDL2_INCLUDE_DIRS}) + +# ========= StormLib ============= +if(INCLUDE_MPQ_SUPPORT) + set(stormlib_patch_file ${CMAKE_CURRENT_SOURCE_DIR}/cmake/dependencies/patches/stormlib-optimizations.patch) + set(stormlib_apply_patch_command ${CMAKE_COMMAND} -Dpatch_file=${stormlib_patch_file} -Dwith_reset=TRUE -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/dependencies/git-patch.cmake) + + FetchContent_Declare( + StormLib + GIT_REPOSITORY https://github.com/ladislav-zezula/StormLib.git + GIT_TAG v9.25 + PATCH_COMMAND ${stormlib_apply_patch_command} + ) + FetchContent_MakeAvailable(StormLib) + list(APPEND ADDITIONAL_LIB_INCLUDES ${stormlib_SOURCE_DIR}/src) +endif() + +#=================== STB =================== +set(STB_DIR ${CMAKE_BINARY_DIR}/_deps/stb) +file(DOWNLOAD "https://github.com/nothings/stb/raw/0bc88af4de5fb022db643c2d8e549a0927749354/stb_image.h" "${STB_DIR}/stb_image.h") +file(WRITE "${STB_DIR}/stb_impl.c" "#define STB_IMAGE_IMPLEMENTATION\n#include \"stb_image.h\"") + +add_library(stb STATIC) + +target_sources(stb PRIVATE + ${STB_DIR}/stb_image.h + ${STB_DIR}/stb_impl.c +) + +target_include_directories(stb PUBLIC ${STB_DIR}) +list(APPEND ADDITIONAL_LIB_INCLUDES ${STB_DIR}) + +#=================== libgfxd =================== +if (GFX_DEBUG_DISASSEMBLER) + FetchContent_Declare( + libgfxd + GIT_REPOSITORY https://github.com/glankk/libgfxd.git + GIT_TAG 008f73dca8ebc9151b205959b17773a19c5bd0da + ) + FetchContent_MakeAvailable(libgfxd) + + add_library(libgfxd STATIC) + set_property(TARGET libgfxd PROPERTY C_STANDARD 11) + + target_sources(libgfxd PRIVATE + ${libgfxd_SOURCE_DIR}/gbi.h + ${libgfxd_SOURCE_DIR}/gfxd.h + ${libgfxd_SOURCE_DIR}/priv.h + ${libgfxd_SOURCE_DIR}/gfxd.c + ${libgfxd_SOURCE_DIR}/uc.c + ${libgfxd_SOURCE_DIR}/uc_f3d.c + ${libgfxd_SOURCE_DIR}/uc_f3db.c + ${libgfxd_SOURCE_DIR}/uc_f3dex.c + ${libgfxd_SOURCE_DIR}/uc_f3dex2.c + ${libgfxd_SOURCE_DIR}/uc_f3dexb.c + ) + + target_include_directories(libgfxd PUBLIC ${libgfxd_SOURCE_DIR}) +endif() + +#======== thread-pool ======== +FetchContent_Declare( + ThreadPool + GIT_REPOSITORY https://github.com/bshoshany/thread-pool.git + GIT_TAG v4.1.0 +) +FetchContent_MakeAvailable(ThreadPool) + +list(APPEND ADDITIONAL_LIB_INCLUDES ${threadpool_SOURCE_DIR}/include) + +#=========== prism =========== +option(PRISM_STANDALONE "Build prism as a standalone library" OFF) +FetchContent_Declare( + prism + GIT_REPOSITORY https://github.com/KiritoDv/prism-processor.git + GIT_TAG bbcbc7e3f890a5806b579361e7aa0336acd547e7 +) +FetchContent_MakeAvailable(prism) \ No newline at end of file diff --git a/libultraship/cmake/dependencies/git-patch.cmake b/libultraship/cmake/dependencies/git-patch.cmake new file mode 100644 index 000000000..deb3e35da --- /dev/null +++ b/libultraship/cmake/dependencies/git-patch.cmake @@ -0,0 +1,61 @@ +# In variables: patch_file, with_reset + +function(patch) + execute_process( + COMMAND git apply ${patch_file} + RESULT_VARIABLE ret + ERROR_QUIET + ) + set(ret ${ret} PARENT_SCOPE) +endfunction() + +function(check_patch) + execute_process( + COMMAND git apply --reverse --check ${patch_file} + RESULT_VARIABLE ret + ERROR_QUIET + ) + set(ret ${ret} PARENT_SCOPE) +endfunction() + +# Applies the patch or checks if it has already been applied successfully previously. Will error otherwise. +function(patch_if_needed) + patch() + if(NOT ret EQUAL 0) + check_patch() + endif() + set(ret ${ret} PARENT_SCOPE) +endfunction() + +# Resets code and reapply patch, if old (potentially incompatible) patch applied +function(patch_if_needed_with_reset) + patch_if_needed() + if(NOT ret EQUAL 0) + message(STATUS "Failed to patch in current state, clearing changes to reapply") + execute_process( + COMMAND git status --porcelain + RESULT_VARIABLE is_changed + ) + if(NOT is_changed EQUAL 0) + message(WARNING "Patch inapplyable in clean state") + set(ret 1) + else() + execute_process(COMMAND git reset --hard) + patch_if_needed() + endif() + endif() + set(ret ${ret} PARENT_SCOPE) +endfunction() + +message(STATUS "Trying to apply patch ${patch_file}") +if(with_reset) + patch_if_needed_with_reset() +else() + patch_if_needed() +endif() + +if(NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to apply patch ${patch_file}") +else() + message(STATUS "Successfully patched with ${patch_file}") +endif() diff --git a/libultraship/cmake/dependencies/ios.cmake b/libultraship/cmake/dependencies/ios.cmake new file mode 100644 index 000000000..aad5c5868 --- /dev/null +++ b/libultraship/cmake/dependencies/ios.cmake @@ -0,0 +1,90 @@ +include(FetchContent) + +#=================== SDL2 =================== +find_package(SDL2 QUIET) +if (NOT ${SDL2_FOUND}) + FetchContent_Declare( + SDL2 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG release-2.32.10 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(SDL2) +endif() + +#=================== nlohmann-json =================== +find_package(nlohmann_json QUIET) +if (NOT ${nlohmann_json_FOUND}) + FetchContent_Declare( + nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.12.0 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(nlohmann_json) +endif() + +#=================== tinyxml2 =================== +find_package(tinyxml2 QUIET) +if (NOT ${tinyxml2_FOUND}) + set(tinyxml2_BUILD_TESTING OFF) + FetchContent_Declare( + tinyxml2 + GIT_REPOSITORY https://github.com/leethomason/tinyxml2.git + GIT_TAG 11.0.0 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(tinyxml2) +endif() + +#=================== spdlog =================== +find_package(spdlog QUIET) +if (NOT ${spdlog_FOUND}) + FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.16.0 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(spdlog) +endif() + +#=================== libzip =================== +find_package(libzip QUIET) +if (NOT ${libzip_FOUND}) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + set(BUILD_TOOLS OFF) + set(BUILD_REGRESS OFF) + set(BUILD_EXAMPLES OFF) + set(BUILD_DOC OFF) + set(BUILD_OSSFUZZ OFF) + set(BUILD_SHARED_LIBS OFF) + FetchContent_Declare( + libzip + GIT_REPOSITORY https://github.com/nih-at/libzip.git + GIT_TAG v1.11.4 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(libzip) + list(APPEND ADDITIONAL_LIB_INCLUDES ${libzip_SOURCE_DIR}/lib ${libzip_BINARY_DIR}) +endif() + +#=================== Metal-cpp =================== +FetchContent_Declare( + metalcpp + GIT_REPOSITORY https://github.com/briaguya-ai/single-header-metal-cpp.git + GIT_TAG macOS13_iOS16 +) +FetchContent_MakeAvailable(metalcpp) +list(APPEND ADDITIONAL_LIB_INCLUDES ${metalcpp_SOURCE_DIR}) + +#=================== ImGui =================== +target_sources(ImGui + PRIVATE + ${imgui_SOURCE_DIR}/backends/imgui_impl_metal.mm +) + +target_include_directories(ImGui PRIVATE ${metalcpp_SOURCE_DIR}) +target_compile_definitions(ImGui PUBLIC IMGUI_IMPL_METAL_CPP) + +target_link_libraries(ImGui PUBLIC SDL2::SDL2-static SDL2::SDL2main) diff --git a/libultraship/cmake/dependencies/linux.cmake b/libultraship/cmake/dependencies/linux.cmake new file mode 100644 index 000000000..02301f130 --- /dev/null +++ b/libultraship/cmake/dependencies/linux.cmake @@ -0,0 +1,10 @@ +#=================== ImGui =================== +find_package(SDL2 REQUIRED) +target_link_libraries(ImGui PUBLIC SDL2::SDL2) + +if (USE_OPENGLES) + target_link_libraries(ImGui PUBLIC ${OPENGL_GLESv2_LIBRARY}) + add_compile_definitions(IMGUI_IMPL_OPENGL_ES3) +else() + target_link_libraries(ImGui PUBLIC ${OPENGL_opengl_LIBRARY}) +endif() diff --git a/libultraship/cmake/dependencies/mac.cmake b/libultraship/cmake/dependencies/mac.cmake new file mode 100644 index 000000000..1a1694d36 --- /dev/null +++ b/libultraship/cmake/dependencies/mac.cmake @@ -0,0 +1,42 @@ +include(FetchContent) + +#=================== spdlog =================== +# macports has issues with this because of fmt +# brew doesn't support building multiarch +find_package(spdlog QUIET) +if (NOT ${spdlog_FOUND}) + FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.14.1 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(spdlog) +endif() + +#=================== Metal-cpp =================== +FetchContent_Declare( + metalcpp + GIT_REPOSITORY https://github.com/briaguya-ai/single-header-metal-cpp.git + GIT_TAG macOS13_iOS16 +) +FetchContent_MakeAvailable(metalcpp) +list(APPEND ADDITIONAL_LIB_INCLUDES ${metalcpp_SOURCE_DIR}) + +#=================== ImGui =================== +target_sources(ImGui + PRIVATE + ${imgui_SOURCE_DIR}/backends/imgui_impl_metal.mm +) + +target_include_directories(ImGui PRIVATE ${metalcpp_SOURCE_DIR}) +target_compile_definitions(ImGui PUBLIC IMGUI_IMPL_METAL_CPP) + +find_package(SDL2 REQUIRED) +target_link_libraries(ImGui PUBLIC SDL2::SDL2) + +find_package(GLEW REQUIRED) +target_link_libraries(ImGui PUBLIC ${OPENGL_opengl_LIBRARY} GLEW::GLEW) +set_target_properties(ImGui PROPERTIES + XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES +) diff --git a/libultraship/cmake/dependencies/openbsd.cmake b/libultraship/cmake/dependencies/openbsd.cmake new file mode 100644 index 000000000..585f4bb0b --- /dev/null +++ b/libultraship/cmake/dependencies/openbsd.cmake @@ -0,0 +1,20 @@ +#=================== ImGui =================== + +find_package(SDL2 REQUIRED) + +# XXX sdl2-config.cmake doesn't include /usr/X11R6/include but sdl2-config does +# ideally this could be fixed from sdl2 port +# set(SDL2_INCLUDE_DIRS "${SDL2_INCLUDE_DIRS};/usr/X11R6/include") +# note this is a central place to bundle such hacks for libultraship consumers +# otherwise each game may need its own fix as we are not in the same scope +include_directories("/usr/X11R6/include") + +target_link_libraries(ImGui PUBLIC SDL2::SDL2) + +if (USE_OPENGLES) + # XXX copy linux.cmake but maybe this actually need gles3_LIBRARY + target_link_libraries(ImGui PUBLIC ${OPENGL_gles2_LIBRARY}) + add_compile_definitions(IMGUI_IMPL_OPENGL_ES3) +else() + target_link_libraries(ImGui PUBLIC ${OPENGL_gl_LIBRARY}) +endif() diff --git a/libultraship/cmake/dependencies/patches/imgui-fixes-and-config.patch b/libultraship/cmake/dependencies/patches/imgui-fixes-and-config.patch new file mode 100644 index 000000000..6d890e900 --- /dev/null +++ b/libultraship/cmake/dependencies/patches/imgui-fixes-and-config.patch @@ -0,0 +1,112 @@ +From 28c317c55769110b4f83003aa9e868f66157b3fe Mon Sep 17 00:00:00 2001 +From: briaguya <70942617+briaguya-ai@users.noreply.github.com> +Date: Sat, 17 May 2025 04:41:55 -0400 +Subject: [PATCH 1/3] Revert "Backends: Metal: Fixed memory leaks. (#8276, + #8166)" + +This reverts commit e6a7c7689f57038b2519fe43e55e3d57103ad0f7. +--- + backends/imgui_impl_metal.mm | 8 -------- + docs/CHANGELOG.txt | 1 - + 2 files changed, 9 deletions(-) + +diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm +index 9ab4a342adfd..388c4f3984e1 100644 +--- a/backends/imgui_impl_metal.mm ++++ b/backends/imgui_impl_metal.mm +@@ -18,7 +18,6 @@ + // (minor and older changes stripped away, please see git history for details) + // 2025-XX-XX: Metal: Added support for multiple windows via the ImGuiPlatformIO interface. + // 2025-02-03: Metal: Crash fix. (#8367) +-// 2025-01-08: Metal: Fixed memory leaks when using metal-cpp (#8276, #8166) or when using multiple contexts (#7419). + // 2022-08-23: Metal: Update deprecated property 'sampleCount'->'rasterSampleCount'. + // 2022-07-05: Metal: Add dispatch synchronization. + // 2022-06-30: Metal: Use __bridge for ARC based systems. +@@ -171,11 +170,7 @@ void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor) + { + ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); + IM_ASSERT(bd != nil && "Context or backend not initialized! Did you call ImGui_ImplMetal_Init()?"); +-#ifdef IMGUI_IMPL_METAL_CPP +- bd->SharedMetalContext.framebufferDescriptor = [[[FramebufferDescriptor alloc] initWithRenderPassDescriptor:renderPassDescriptor]autorelease]; +-#else + bd->SharedMetalContext.framebufferDescriptor = [[FramebufferDescriptor alloc] initWithRenderPassDescriptor:renderPassDescriptor]; +-#endif + if (bd->SharedMetalContext.depthStencilState == nil) + ImGui_ImplMetal_CreateDeviceObjects(bd->SharedMetalContext.device); + } +@@ -383,9 +378,6 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id device) + depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways; + bd->SharedMetalContext.depthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDescriptor]; + ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows(); +-#ifdef IMGUI_IMPL_METAL_CPP +- [depthStencilDescriptor release]; +-#endif + ImGui_ImplMetal_CreateFontsTexture(device); + return true; + } +diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt +index 6c4f19cfef02..e4a4cbf98ef6 100644 +--- a/docs/CHANGELOG.txt ++++ b/docs/CHANGELOG.txt +@@ -355,7 +355,6 @@ Other changes: + though it is currently not doing anything particular. (#8163, #7998, #7988) + - Backends: Allegro5: Avoid calling al_set_mouse_cursor() repeatedly since it appears + to leak on on X11 (#8256). [@Helodity] +-- Backends: Metal: Fixed leaks when using metal-cpp. (#8276, #8166) [@selimsandal] + - Backends: Metal: Fixed resource leak when using multiple contexts. (#7419) [@anszom] + - Backends: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for + platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) [@Zer0xFF] + +From 56f5907cf5224e1a2f64289029cc2b46e51d3b6c Mon Sep 17 00:00:00 2001 +From: briaguya <70942617+briaguya-ai@users.noreply.github.com> +Date: Sat, 17 May 2025 04:42:41 -0400 +Subject: [PATCH 2/3] gamepad fix + +--- + backends/imgui_impl_sdl2.cpp | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp +index f3a31c5c3c5e..2e6a1ad9d1f1 100644 +--- a/backends/imgui_impl_sdl2.cpp ++++ b/backends/imgui_impl_sdl2.cpp +@@ -845,9 +845,6 @@ static void ImGui_ImplSDL2_UpdateGamepads() + bd->WantUpdateGamepadsList = false; + } + +- // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. +- if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) +- return; + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + if (bd->Gamepads.Size == 0) + return; + +From 90c4297624a0fe993f1ac0e538bccdc80a0fc5f2 Mon Sep 17 00:00:00 2001 +From: briaguya <70942617+briaguya-ai@users.noreply.github.com> +Date: Sat, 17 May 2025 04:42:56 -0400 +Subject: [PATCH 3/3] config + +--- + imconfig.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/imconfig.h b/imconfig.h +index a1e29e849bc8..ee64116ff0cf 100644 +--- a/imconfig.h ++++ b/imconfig.h +@@ -110,7 +110,7 @@ + operator MyVec4() const { return MyVec4(x,y,z,w); } + */ + //---- ...Or use Dear ImGui's own very basic math operators. +-//#define IMGUI_DEFINE_MATH_OPERATORS ++#define IMGUI_DEFINE_MATH_OPERATORS + + //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. + // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). +@@ -139,3 +139,6 @@ namespace ImGui + void MyFunction(const char* name, MyMatrix44* mtx); + } + */ ++ ++// handle https://github.com/ocornut/imgui/issues/1641 the old way ++#define ImTextureID void* diff --git a/libultraship/cmake/dependencies/patches/stormlib-optimizations.patch b/libultraship/cmake/dependencies/patches/stormlib-optimizations.patch new file mode 100644 index 000000000..3c61693cf --- /dev/null +++ b/libultraship/cmake/dependencies/patches/stormlib-optimizations.patch @@ -0,0 +1,123 @@ +From ff338b230544f8b2bb68d2fbe075175ed2fd758c Mon Sep 17 00:00:00 2001 +From: briaguya <70942617+briaguya-ai@users.noreply.github.com> +Date: Wed, 8 May 2024 02:44:49 -0400 +Subject: [PATCH] bring back optimizations + +Co-authored-by: GaryOderNichts <12049776+GaryOderNichts@users.noreply.github.com> +--- + src/SBaseCommon.cpp | 6 +++++- + src/SFileCreateArchive.cpp | 2 ++ + src/SFileListFile.cpp | 35 ++++++++++++++++++----------------- + src/StormLib.h | 3 +++ + 4 files changed, 28 insertions(+), 18 deletions(-) + +diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp +index 77590d6..6fed00e 100644 +--- a/src/SBaseCommon.cpp ++++ b/src/SBaseCommon.cpp +@@ -884,8 +884,10 @@ ULONGLONG FindFreeMpqSpace(TMPQArchive * ha) + ULONGLONG FreeSpacePos = ha->pHeader->dwHeaderSize; + DWORD dwChunkCount; + ++ TFileEntry* startEntry = (ha->useFreeSpaceOptimization && ha->lastFreeSpaceEntry != nullptr) ? ha->lastFreeSpaceEntry : ha->pFileTable; ++ + // Parse the entire block table +- for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) ++ for(pFileEntry = startEntry; pFileEntry < pFileTableEnd; pFileEntry++) + { + // Only take existing files with nonzero size + if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && (pFileEntry->dwCmpSize != 0)) +@@ -906,6 +908,8 @@ ULONGLONG FindFreeMpqSpace(TMPQArchive * ha) + dwChunkCount = ((pFileEntry->dwCmpSize - 1) / pHeader->dwRawChunkSize) + 1; + FreeSpacePos += dwChunkCount * MD5_DIGEST_SIZE; + } ++ ++ ha->lastFreeSpaceEntry = pFileEntry; + } + } + } +diff --git a/src/SFileCreateArchive.cpp b/src/SFileCreateArchive.cpp +index c0ea367..d392a83 100644 +--- a/src/SFileCreateArchive.cpp ++++ b/src/SFileCreateArchive.cpp +@@ -224,6 +224,8 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea + ha->dwFileFlags3 = pCreateInfo->dwFileFlags3 ? MPQ_FILE_EXISTS : 0; + ha->dwAttrFlags = pCreateInfo->dwAttrFlags; + ha->dwFlags = dwMpqFlags | MPQ_FLAG_CHANGED; ++ ha->useFreeSpaceOptimization = true; ++ ha->lastFreeSpaceEntry = nullptr; + pStream = NULL; + + // Fill the MPQ header +diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp +index b2a3a3c..d0c3c67 100644 +--- a/src/SFileListFile.cpp ++++ b/src/SFileListFile.cpp +@@ -409,6 +409,7 @@ static LPBYTE CreateListFile(TMPQArchive * ha, DWORD * pcbListFile) + static DWORD SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName) + { + TFileEntry * pFileEntry; ++ TMPQHash * pFirstHash; + TMPQHash * pHashEnd; + TMPQHash * pHash; + DWORD dwName1; +@@ -443,25 +444,25 @@ static DWORD SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szF + pHashEnd = ha->pHashTable + (ha->dwRealHashTableSize / sizeof(TMPQHash)); + + // Go through the hash table and put the name in each item that has the same name pair +- for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) +- { +- if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) +- { +- // Allocate file name for the file entry +- AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName); +- } +- } ++ // for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) ++ // { ++ // if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) ++ // { ++ // // Allocate file name for the file entry ++ // AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName); ++ // } ++ // } + + // Go while we found something +- //pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); +- //while(pHash != NULL) +- //{ +- // // Allocate file name for the file entry +- // AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName); +- +- // // Now find the next language version of the file +- // pHash = GetNextHashEntry(ha, pFirstHash, pHash); +- //} ++ pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); ++ while(pHash != NULL) ++ { ++ // Allocate file name for the file entry ++ AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName); ++ ++ // Now find the next language version of the file ++ pHash = GetNextHashEntry(ha, pFirstHash, pHash); ++ } + + return ERROR_SUCCESS; + } +diff --git a/src/StormLib.h b/src/StormLib.h +index 4aa51c1..bda11d3 100644 +--- a/src/StormLib.h ++++ b/src/StormLib.h +@@ -860,6 +860,9 @@ typedef struct _TMPQArchive + ULONGLONG CompactBytesProcessed; // Amount of bytes that have been processed during a particular compact call + ULONGLONG CompactTotalBytes; // Total amount of bytes to be compacted + void * pvCompactUserData; // User data thats passed to the callback ++ ++ TFileEntry* lastFreeSpaceEntry; ++ bool useFreeSpaceOptimization; + } TMPQArchive; + + // File handle structure +-- +2.43.0 + diff --git a/libultraship/cmake/dependencies/windows-vcpkg.cmake b/libultraship/cmake/dependencies/windows-vcpkg.cmake new file mode 100644 index 000000000..4f6465bc0 --- /dev/null +++ b/libultraship/cmake/dependencies/windows-vcpkg.cmake @@ -0,0 +1,17 @@ +if (USE_AUTO_VCPKG) + include(cmake/automate-vcpkg.cmake) + +if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32") + set(VCPKG_TRIPLET x86-windows-static) + set(VCPKG_TARGET_TRIPLET x86-windows-static) +elseif ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64") + set(VCPKG_TRIPLET x64-windows-static) + set(VCPKG_TARGET_TRIPLET x64-windows-static) +elseif ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "arm64") + set(VCPKG_TRIPLET arm64-windows-static) + set(VCPKG_TARGET_TRIPLET arm64-windows-static) +endif() + + vcpkg_bootstrap() + vcpkg_install_packages(zlib bzip2 sdl2 glew libzip nlohmann-json tinyxml2 spdlog) +endif() diff --git a/libultraship/cmake/dependencies/windows.cmake b/libultraship/cmake/dependencies/windows.cmake new file mode 100644 index 000000000..6b3b7b237 --- /dev/null +++ b/libultraship/cmake/dependencies/windows.cmake @@ -0,0 +1,12 @@ +#=================== ImGui =================== +target_sources(ImGui + PRIVATE + ${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.cpp + ${imgui_SOURCE_DIR}/backends/imgui_impl_win32.cpp +) + +find_package(SDL2 CONFIG REQUIRED) +target_link_libraries(ImGui PUBLIC SDL2::SDL2 SDL2::SDL2main) + +find_package(GLEW REQUIRED) +target_link_libraries(ImGui PUBLIC opengl32 GLEW::GLEW) diff --git a/libultraship/cmake/ios-toolchain-populate.cmake b/libultraship/cmake/ios-toolchain-populate.cmake new file mode 100644 index 000000000..eb93fea4d --- /dev/null +++ b/libultraship/cmake/ios-toolchain-populate.cmake @@ -0,0 +1,15 @@ +set(PLATFORM "OS64COMBINED") +include(FetchContent) +FetchContent_Declare(iostoolchain + GIT_REPOSITORY https://github.com/leetal/ios-cmake + GIT_TAG 06465b27698424cf4a04a5ca4904d50a3c966c45 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" +) +FetchContent_GetProperties(iostoolchain) +if(NOT iostoolchain_POPULATED) + FetchContent_Populate(iostoolchain) +endif() +set(CMAKE_IOS_TOOLCHAIN_FILE ${iostoolchain_SOURCE_DIR}/ios.toolchain.cmake) +set_property(GLOBAL PROPERTY IOS_TOOLCHAIN_FILE ${CMAKE_IOS_TOOLCHAIN_FILE}) +include(${CMAKE_IOS_TOOLCHAIN_FILE}) \ No newline at end of file diff --git a/libultraship/include/fast/Fast3dWindow.h b/libultraship/include/fast/Fast3dWindow.h new file mode 100644 index 000000000..38ed53b8f --- /dev/null +++ b/libultraship/include/fast/Fast3dWindow.h @@ -0,0 +1,76 @@ +#pragma once +#include "ship/window/Window.h" +#include "ship/window/gui/Gui.h" +#include "ship/controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h" +#include "FastMouseStateManager.h" + +union Gfx; +#include "interpreter.h" + +namespace Fast { +class Fast3dWindow : public Ship::Window { + public: + Fast3dWindow(); + Fast3dWindow(std::vector> guiWindows); + Fast3dWindow(std::shared_ptr gui); + Fast3dWindow(std::shared_ptr gui, std::shared_ptr mouseStateManager); + ~Fast3dWindow(); + + void Init() override; + void Close() override; + void RunGuiOnly() override; + void StartFrame() override; + void EndFrame() override; + bool IsFrameReady() override; + void HandleEvents() override; + void SetCursorVisibility(bool visible) override; + uint32_t GetWidth() override; + uint32_t GetHeight() override; + int32_t GetPosX() override; + int32_t GetPosY() override; + float GetAspectRatio() override; + void SetMousePos(Ship::Coords pos) override; + Ship::Coords GetMousePos() override; + Ship::Coords GetMouseDelta() override; + Ship::CoordsF GetMouseWheel() override; + bool GetMouseState(Ship::MouseBtn btn) override; + void SetMouseCapture(bool capture) override; + bool IsMouseCaptured() override; + uint32_t GetCurrentRefreshRate() override; + bool SupportsWindowedFullscreen() override; + bool CanDisableVerticalSync() override; + void SetResolutionMultiplier(float multiplier) override; + void SetMsaaLevel(uint32_t value) override; + void SetFullscreen(bool isFullscreen) override; + bool IsFullscreen() override; + bool IsRunning() override; + uintptr_t GetGfxFrameBuffer() override; + const char* GetKeyName(int32_t scancode) override; + + void InitWindowManager(); + int32_t GetTargetFps(); + void SetTargetFps(int32_t fps); + void SetMaximumFrameLatency(int32_t latency); + void GetPixelDepthPrepare(float x, float y); + uint16_t GetPixelDepth(float x, float y); + void SetTextureFilter(FilteringMode filteringMode); + void SetRendererUCode(UcodeHandlers ucode); + void EnableSRGBMode(); + bool DrawAndRunGraphicsCommands(Gfx* commands, const std::unordered_map& mtxReplacements); + + std::weak_ptr GetInterpreterWeak() const; + + protected: + static bool KeyDown(int32_t scancode); + static bool KeyUp(int32_t scancode); + static void AllKeysUp(); + static bool MouseButtonDown(int button); + static bool MouseButtonUp(int button); + static void OnFullscreenChanged(bool isNowFullscreen); + + private: + GfxRenderingAPI* mRenderingApi; + GfxWindowBackend* mWindowManagerApi; + std::shared_ptr mInterpreter = nullptr; +}; +} // namespace Fast diff --git a/libultraship/include/fast/FastMouseStateManager.h b/libultraship/include/fast/FastMouseStateManager.h new file mode 100644 index 000000000..6631791f5 --- /dev/null +++ b/libultraship/include/fast/FastMouseStateManager.h @@ -0,0 +1,15 @@ +#pragma once + +#include "ship/window/MouseStateManager.h" + +namespace Fast { +class FastMouseStateManager : public Ship::MouseStateManager { + public: + void ResetCursorVisibilityTimer() override; + void SetCursorVisibilityTime(uint32_t seconds); + uint32_t GetCursorVisibilityTime(); + + private: + uint32_t mCursorVisibleSeconds = 3; +}; +} // namespace Fast diff --git a/libultraship/include/fast/LICENSE.txt b/libultraship/include/fast/LICENSE.txt new file mode 100644 index 000000000..2857b7eaf --- /dev/null +++ b/libultraship/include/fast/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Emill, MaikelChan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libultraship/include/fast/README.md b/libultraship/include/fast/README.md new file mode 100644 index 000000000..942427ecb --- /dev/null +++ b/libultraship/include/fast/README.md @@ -0,0 +1,30 @@ +# Nintendo 64 Fast3D renderer + +Implementation of a Fast3D renderer for games built originally for the Nintendo 64 platform. + +For rendering OpenGL, Direct3D 11 and Direct3D 12 are supported. + +Supported windowing systems are GLX (used on Linux), DXGI (used on Windows) and SDL (generic). + +# Usage + +See `gfx_pc.h`. You will also need a copy of `PR/gbi.h`, found in libultra. + +First call `gfx_init(struct GfxBackend *wapi, struct GfxRenderingAPI *rapi, const char *game_name, bool start_in_fullscreen)` and supply the desired backends at program start. + +Some callbacks can be set on `wapi`. See `gfx_window_manager_api.h` for more info. + +Each game main loop iteration should look like this: + +```C +gfx_start_frame(); // Handles input events such as keyboard and window events +// perform game logic here +gfx_run(cmds); // submit display list and render a frame +// do more expensive work here, such as play audio +gfx_end_frame(); // this just waits until the frame is shown on the screen (vsync), to provide correct game timing +``` + +When you are ready to start the main loop, call `wapi->main_loop(one_iteration_func)`. + +For the best experience, please change the Vtx and Mtx structures to use floats instead of fixed point arithmetic (`GBI_FLOATS`). + diff --git a/libultraship/include/fast/backends/gfx_direct3d11.h b/libultraship/include/fast/backends/gfx_direct3d11.h new file mode 100644 index 000000000..6585d8014 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_direct3d11.h @@ -0,0 +1,11 @@ +#ifdef ENABLE_DX11 + +#ifndef GFX_DIRECT3D11_H +#define GFX_DIRECT3D11_H + +#include "gfx_rendering_api.h" +#include + +#endif + +#endif diff --git a/libultraship/include/fast/backends/gfx_direct3d_common.h b/libultraship/include/fast/backends/gfx_direct3d_common.h new file mode 100644 index 000000000..16f96ea62 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_direct3d_common.h @@ -0,0 +1,185 @@ +#pragma once + +#if defined(ENABLE_DX11) || defined(ENABLE_DX12) + +#ifdef __cplusplus +#include "../interpreter.h" +#include +#include +#include "gfx_rendering_api.h" +#include "d3d11.h" +#include "d3dcompiler.h" + +namespace Fast { + +struct PerFrameCB { + uint32_t noise_frame; + float noise_scale; + uint32_t padding[2]; // constant buffers must be multiples of 16 bytes in size +}; + +struct PerDrawCB { + struct Texture { + uint32_t width; + uint32_t height; + uint32_t linear_filtering; + uint32_t padding; + } mTextures[SHADER_MAX_TEXTURES]; +}; + +struct Coord { + int x, y; +}; + +struct TextureData { + Microsoft::WRL::ComPtr texture; + Microsoft::WRL::ComPtr resource_view; + Microsoft::WRL::ComPtr sampler_state; + uint32_t width; + uint32_t height; + bool linear_filtering; +}; + +struct FramebufferDX11 { + Microsoft::WRL::ComPtr render_target_view; + Microsoft::WRL::ComPtr depth_stencil_view; + Microsoft::WRL::ComPtr depth_stencil_srv; + uint32_t texture_id; + bool has_depth_buffer; + uint32_t msaa_level; +}; + +struct ShaderProgramD3D11 { + Microsoft::WRL::ComPtr vertex_shader; + Microsoft::WRL::ComPtr pixel_shader; + Microsoft::WRL::ComPtr input_layout; + Microsoft::WRL::ComPtr blend_state; + + uint64_t shader_id0; + uint32_t shader_id1; + uint8_t numInputs; + uint8_t numFloats; + bool usedTextures[SHADER_MAX_TEXTURES]; +}; + +class GfxWindowBackendDXGI; + +class GfxRenderingAPIDX11 final : public GfxRenderingAPI { + public: + GfxRenderingAPIDX11() = default; + ~GfxRenderingAPIDX11() override; + GfxRenderingAPIDX11(GfxWindowBackendDXGI* backend); + const char* GetName() override; + int GetMaxTextureSize() override; + GfxClipParameters GetClipParameters() override; + void UnloadShader(struct ShaderProgram* oldPrg) override; + void LoadShader(struct ShaderProgram* newPrg) override; + struct ShaderProgram* CreateAndLoadNewShader(uint64_t shaderId0, uint32_t shaderId1) override; + struct ShaderProgram* LookupShader(uint64_t shaderId0, uint32_t shaderId1) override; + void ShaderGetInfo(struct ShaderProgram* prg, uint8_t* numInputs, bool usedTextures[2]) override; + uint32_t NewTexture() override; + void SelectTexture(int tile, uint32_t textureId) override; + void UploadTexture(const uint8_t* rgba32Buf, uint32_t width, uint32_t height) override; + void SetSamplerParameters(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt) override; + void SetDepthTestAndMask(bool depth_test, bool z_upd) override; + void SetZmodeDecal(bool decal) override; + void SetViewport(int x, int y, int width, int height) override; + void SetScissor(int x, int y, int width, int height) override; + void SetUseAlpha(bool useAlpha) override; + void DrawTriangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) override; + void Init() override; + void OnResize() override; + void StartFrame() override; + void EndFrame() override; + void FinishRender() override; + int CreateFramebuffer() override; + void UpdateFramebufferParameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, + bool opengl_invertY, bool render_target, bool has_depth_buffer, + bool can_extract_depth) override; + void StartDrawToFramebuffer(int fbId, float noiseScale) override; + void CopyFramebuffer(int fbDstId, int fbSrcId, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, + int dstX1, int dstY1) override; + void ClearFramebuffer(bool color, bool depth) override; + void ReadFramebufferToCPU(int fbId, uint32_t width, uint32_t height, uint16_t* rgba16Buf) override; + void ResolveMSAAColorBuffer(int fbIdTarger, int fbIdSrc) override; + std::unordered_map, uint16_t, hash_pair_ff> + GetPixelDepth(int fb_id, const std::set>& coordinates) override; + void* GetFramebufferTextureId(int fbId) override; + void SelectTextureFb(int fbId) override; + void DeleteTexture(uint32_t texId) override; + void SetTextureFilter(FilteringMode mode) override; + FilteringMode GetTextureFilter() override; + void SetSrgbMode() override; + ImTextureID GetTextureById(int id) override; + + PFN_D3D11_CREATE_DEVICE mDX11CreateDevice; + Microsoft::WRL::ComPtr mContext; + Microsoft::WRL::ComPtr mDevice; + GfxWindowBackendDXGI* mWindowBackend = nullptr; + D3D_FEATURE_LEVEL mFeatureLevel; + + private: + void CreateDepthStencilObjects(uint32_t width, uint32_t height, uint32_t msaa_count, ID3D11DepthStencilView** view, + ID3D11ShaderResourceView** srv); + + HMODULE mDX11Module; + + HMODULE mCompilerModule; + pD3DCompile mD3dCompile; + + uint32_t mMsaaNumQualityLevels[D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT]; + + Microsoft::WRL::ComPtr mRasterizerState; + Microsoft::WRL::ComPtr mDepthStencilState; + Microsoft::WRL::ComPtr mVertexBuffer; + Microsoft::WRL::ComPtr mPerFrameCb; + Microsoft::WRL::ComPtr mPerDrawCb; + Microsoft::WRL::ComPtr mCoordBuffer; + Microsoft::WRL::ComPtr mCoordBufferSrv; + Microsoft::WRL::ComPtr mDepthValueOutputBuffer; + Microsoft::WRL::ComPtr mDepthValueOutputBufferCopy; + Microsoft::WRL::ComPtr mDepthValueOutputUav; + Microsoft::WRL::ComPtr mComputeShader; + Microsoft::WRL::ComPtr mComputeShaderMsaa; + Microsoft::WRL::ComPtr mComputeShaderMsaaBlob; + size_t mCoordBufferSize; + +#if DEBUG_D3D + Microsoft::WRL::ComPtr debug; +#endif + + PerFrameCB mPerFrameCbData; + PerDrawCB mPerDrawCbData; + + std::map, struct ShaderProgramD3D11> mShaderProgramPool; + + std::vector mTextures; + int mCurrentTile; + uint32_t mCurrentTextureIds[SHADER_MAX_TEXTURES]; + + std::vector mFrameBuffers; + + // Current state + + struct ShaderProgramD3D11* mShaderProgram; + + int32_t mRenderTargetHeight; + int mCurrentFramebuffer; + FilteringMode mCurrentFilterMode = FILTER_NONE; + + // Previous states (to prevent setting states needlessly) + + struct ShaderProgramD3D11* mLastShaderProgram = nullptr; + uint32_t mLastVertexBufferStride = 0; + Microsoft::WRL::ComPtr mLastBlendState = nullptr; + Microsoft::WRL::ComPtr mLastResourceViews[SHADER_MAX_TEXTURES] = { nullptr, nullptr }; + Microsoft::WRL::ComPtr mLastSamplerStates[SHADER_MAX_TEXTURES] = { nullptr, nullptr }; + + D3D_PRIMITIVE_TOPOLOGY mLastPrimitaveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; +}; + +std::string gfx_direct3d_common_build_shader(size_t& numFloats, const CCFeatures& cc_features, + bool include_root_signature, bool three_point_filtering, bool use_srgb); +} // namespace Fast +#endif +#endif diff --git a/libultraship/include/fast/backends/gfx_dxgi.h b/libultraship/include/fast/backends/gfx_dxgi.h new file mode 100644 index 000000000..2d959f2f8 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_dxgi.h @@ -0,0 +1,120 @@ +#pragma once +#if defined(ENABLE_DX11) || defined(ENABLE_DX12) + +#include "gfx_rendering_api.h" + +#include + +#include + +namespace Fast { + +class GfxWindowBackendDXGI final : public GfxWindowBackend { + public: + GfxWindowBackendDXGI() = default; + ~GfxWindowBackendDXGI() override; + + void Init(const char* gameName, const char* apiName, bool startFullScreen, uint32_t width, uint32_t height, + int32_t posX, int32_t posY) override; + void Close() override; + void SetKeyboardCallbacks(bool (*onKeyDown)(int scancode), bool (*onKeyUp)(int scancode), + void (*onnAllKeysUp)()) override; + void SetMouseCallbacks(bool (*onMouseButtonDown)(int btn), bool (*onnMouseButtonUp)(int btn)) override; + void SetFullscreenChangedCallback(void (*onnFullscreenChanged)(bool is_now_fullscreen)) override; + void SetFullscreen(bool fullscreen) override; + void GetActiveWindowRefreshRate(uint32_t* refreshRate) override; + void SetCursorVisibility(bool visability) override; + void SetMousePos(int32_t posX, int32_t posY) override; + void GetMousePos(int32_t* x, int32_t* y) override; + void GetMouseDelta(int32_t* x, int32_t* y) override; + void GetMouseWheel(float* x, float* y) override; + bool GetMouseState(uint32_t btn) override; + void SetMouseCapture(bool capture) override; + bool IsMouseCaptured() override; + void GetDimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) override; + void HandleEvents() override; + bool IsFrameReady() override; + void SwapBuffersBegin() override; + void SwapBuffersEnd() override; + double GetTime() override; + int GetTargetFps(); + void SetTargetFps(int fps) override; + void SetMaxFrameLatency(int latency) override; + const char* GetKeyName(int scancode) override; + bool CanDisableVsync() override; + bool IsRunning() override; + void Destroy() override; + bool IsFullscreen() override; + + HWND GetWindowHandle(); + IDXGISwapChain1* GetSwapChain(); + // These need to be public to be accessible in the window callback + void CreateSwapChain(IUnknown* mDevice, std::function&& before_destroy_fn); + void CreateFactoryAndDevice(bool debug, int d3d_version, class GfxRenderingAPIDX11* self, + bool (*createFunc)(class GfxRenderingAPIDX11* self, bool SoftwareRenderer)); + void OnKeydown(WPARAM wParam, LPARAM lParam); + void OnKeyup(WPARAM wParam, LPARAM lParam); + void OnMouseButtonDown(int btn); + void OnMouseButtonUp(int btn); + void HandleRawInputBuffered(); + void UpdateMousePrevPos(); + void ApplyMouseCaptureClip(); + + std::tuple mMonitor; // 0: Handle, 1: Display Monitor Rect, 2: Is_Primary + uint32_t current_width, current_height; // Width and height of client areas + std::vector> monitor_list; + int32_t mPosX, mPosY; // Screen coordinates + double mDetectedHz; + double mDisplayPeriod; // (1000 / dxgi.mDetectedHz) in ms + void (*mOnAllKeysUp)(void); + POINT mRawMouseDeltaBuf; + float mMouseWheel[2]; + bool mIsMouseCaptured; + bool mIsMouseHovered; + bool mInFocus; + bool mHasMousePosition; + + private: + void LoadDxgi(); + void ApplyMaxFrameLatency(bool first); + void ToggleBorderlessWindowFullScreen(bool enable, bool callCallback); + + HWND h_wnd; + // These four only apply in windowed mode. + + HMODULE dxgi_module; + HRESULT(__stdcall* CreateDXGIFactory1)(REFIID riid, void** factory); + HRESULT(__stdcall* CreateDXGIFactory2)(UINT flags, REFIID iid, void** factory); + + bool mLastMaximizedState; + + bool mDXGI11_4; + Microsoft::WRL::ComPtr mFactory; + Microsoft::WRL::ComPtr swap_chain; + HANDLE mWaitableObject; + Microsoft::WRL::ComPtr mSwapChainDevice; // D3D11 Device or D3D12 Command Queue + std::function mBeforeDestroySwapChainFn; + uint64_t mFrameTimeStamp; // in units of 1/FRAME_INTERVAL_NS_DENOMINATOR nanoseconds + std::map mFrameStats; + std::set> mPendingFrameStats; + bool mDroppedFrame; + bool mZeroLatency; + uint32_t mMaxFrameLatency; + uint32_t mAppliedMaxFrameLatency; + HANDLE mTimer; + bool mTearingSupport; + bool mMousePressed[5]; + LARGE_INTEGER mPreviousPresentTime; + + RAWINPUTDEVICE mRawInputDevice[1]; + POINT mPrevMouseCursorPos; +}; + +} // namespace Fast + +#ifdef DECLARE_GFX_DXGI_FUNCTIONS +void ThrowIfFailed(HRESULT res); +void ThrowIfFailed(HRESULT res, HWND h_wnd, const char* message); +#endif + +#endif diff --git a/libultraship/include/fast/backends/gfx_metal.h b/libultraship/include/fast/backends/gfx_metal.h new file mode 100644 index 000000000..a977275b4 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_metal.h @@ -0,0 +1,224 @@ +// +// gfx_metal.h +// libultraship +// +// Created by David Chavez on 16.08.22. +// +#pragma once +#ifdef __APPLE__ + +#include "gfx_rendering_api.h" +#include "../interpreter.h" + +#include +#include + +static constexpr size_t kMaxVertexBufferPoolSize = 3; +static constexpr size_t METAL_MAX_MULTISAMPLE_SAMPLE_COUNT = 8; +static constexpr size_t MAX_PIXEL_DEPTH_COORDS = 1024; + +namespace MTL { +class Texture; +class SamplerState; +class CommandBuffer; +class RenderPassDescriptor; +class RenderCommandEncoder; +class SamplerState; +class ScissorRect; +class Device; +class Function; +class Buffer; +class RenderPipelineState; +class CommandQueue; +class Viewport; +} // namespace MTL + +namespace CA { +class MetalDrawable; +class MetalLayer; +} // namespace CA + +namespace NS { +class AutoreleasePool; +} + +static int cantor(uint64_t a, uint64_t b) { + return (a + b + 1.0) * (a + b) / 2 + b; +} + +struct hash_pair_shader_ids { + size_t operator()(const std::pair& p) const { + auto value1 = p.first; + auto value2 = p.second; + return cantor(value1, value2); + } +}; + +namespace Fast { + +struct ShaderProgramMetal { + uint64_t shader_id0; + uint32_t shader_id1; + + uint8_t numInputs; + uint8_t numFloats; + bool usedTextures[SHADER_MAX_TEXTURES]; + + // hashed by msaa_level + MTL::RenderPipelineState* pipeline_state_variants[9]; +}; + +struct TextureDataMetal { + MTL::Texture* texture; + MTL::Texture* msaaTexture; + MTL::SamplerState* sampler; + uint32_t width; + uint32_t height; + uint32_t filtering; + bool linear_filtering; +}; + +struct FramebufferMetal { + MTL::CommandBuffer* mCommandBuffer; + MTL::RenderPassDescriptor* mRenderPassDescriptor; + MTL::RenderCommandEncoder* mCommandEncoder; + + MTL::Texture* mDepthTexture; + MTL::Texture* mMsaaDepthTexture; + uint32_t mTextureId; + bool mHasDepthBuffer; + uint32_t mMsaaLevel; + bool mRenderTarget; + + // State + bool mHasEndedEncoding; + bool mHasBoundVertexShader; + bool mHasBoundFragShader; + + struct ShaderProgramMetal* mLastShaderProgram; + MTL::Texture* mLastBoundTextures[SHADER_MAX_TEXTURES]; + MTL::SamplerState* mLastBoundSamplers[SHADER_MAX_TEXTURES]; + MTL::ScissorRect* mScissorRect; + MTL::Viewport* mViewport; + + int8_t mLastDepthTest = -1; + int8_t mLastDepthMask = -1; + int8_t mLastZmodeDecal = -1; +}; + +struct FrameUniforms { + simd::int1 frameCount; + simd::float1 noiseScale; +}; + +struct DrawUniforms { + simd::int1 textureFiltering[SHADER_MAX_TEXTURES]; +}; + +struct CoordUniforms { + simd::uint2 coords[MAX_PIXEL_DEPTH_COORDS]; +}; + +class GfxRenderingAPIMetal final : public GfxRenderingAPI { + public: + ~GfxRenderingAPIMetal() override = default; + const char* GetName() override; + int GetMaxTextureSize() override; + GfxClipParameters GetClipParameters() override; + void UnloadShader(ShaderProgram* oldPrg) override; + void LoadShader(ShaderProgram* newPrg) override; + ShaderProgram* CreateAndLoadNewShader(uint64_t shaderId0, uint32_t shaderId1) override; + ShaderProgram* LookupShader(uint64_t shaderId0, uint32_t shaderId1) override; + void ShaderGetInfo(ShaderProgram* prg, uint8_t* numInputs, bool usedTextures[2]) override; + uint32_t NewTexture() override; + void SelectTexture(int tile, uint32_t textureId) override; + void UploadTexture(const uint8_t* rgba32Buf, uint32_t width, uint32_t height) override; + void SetSamplerParameters(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt) override; + void SetDepthTestAndMask(bool depth_test, bool z_upd) override; + void SetZmodeDecal(bool decal) override; + void SetViewport(int x, int y, int width, int height) override; + void SetScissor(int x, int y, int width, int height) override; + void SetUseAlpha(bool useAlpha) override; + void DrawTriangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) override; + void Init() override; + void OnResize() override; + void StartFrame() override; + void EndFrame() override; + void FinishRender() override; + int CreateFramebuffer() override; + void UpdateFramebufferParameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, + bool opengl_invertY, bool render_target, bool has_depth_buffer, + bool can_extract_depth) override; + void StartDrawToFramebuffer(int fbId, float noiseScale) override; + void CopyFramebuffer(int fbDstId, int fbSrcId, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, + int dstX1, int dstY1) override; + void ClearFramebuffer(bool color, bool depth) override; + void ReadFramebufferToCPU(int fbId, uint32_t width, uint32_t height, uint16_t* rgba16Buf) override; + void ResolveMSAAColorBuffer(int fbIdTarger, int fbIdSrc) override; + std::unordered_map, uint16_t, hash_pair_ff> + GetPixelDepth(int fb_id, const std::set>& coordinates) override; + void* GetFramebufferTextureId(int fbId) override; + void SelectTextureFb(int fbId) override; + void DeleteTexture(uint32_t texId) override; + void SetTextureFilter(FilteringMode mode) override; + FilteringMode GetTextureFilter() override; + void SetSrgbMode() override; + ImTextureID GetTextureById(int id) override; + + void NewFrame(); + void SetupFloatingFrame(); + void RenderDrawData(ImDrawData* drawData); + bool MetalInit(SDL_Renderer* renderer); + + private: + bool NonUniformThreadGroupSupported(); + void SetupScreenFramebuffer(uint32_t width, uint32_t height); + // Elements that only need to be setup once + SDL_Renderer* mRenderer; + CA::MetalLayer* mLayer; // CA::MetalLayer* + MTL::Device* mDevice; + MTL::CommandQueue* mCommandQueue; + + int mCurrentVertexBufferPoolIndex = 0; + MTL::Buffer* mVertexBufferPool[kMaxVertexBufferPoolSize]; + std::unordered_map, struct ShaderProgramMetal, hash_pair_shader_ids> + mShaderProgramPool; + + std::vector mTextures; + std::vector mFramebuffers; + FrameUniforms mFrameUniforms; + CoordUniforms mCoordUniforms; + DrawUniforms mDrawUniforms; + MTL::Buffer* mFrameUniformBuffer; + + uint32_t mMsaaNumQualityLevels[METAL_MAX_MULTISAMPLE_SAMPLE_COUNT]; + + // Depth querying + MTL::Buffer* mCoordUniformBuffer; + MTL::Buffer* mDepthValueOutputBuffer; + size_t mCoordBufferSize; + MTL::Function* mDepthComputeFunction; + MTL::Function* mConvertToRgb5a1Function; + + // Current state + struct ShaderProgramMetal* mShaderProgram; + CA::MetalDrawable* mCurrentDrawable; + std::set mDrawnFramebuffers; + NS::AutoreleasePool* mFrameAutoreleasePool; + + int mCurrentTile; + uint32_t mCurrentTextureIds[SHADER_MAX_TEXTURES]; + + int32_t mRenderTargetHeight; + int mCurrentFramebuffer; + size_t mCurrentVertexBufferOffset; + FilteringMode mCurrentFilterMode = FILTER_THREE_POINT; + + bool mNonUniformThreadgroupSupported; +}; + +} // namespace Fast + +bool Metal_IsSupported(); + +#endif diff --git a/libultraship/include/fast/backends/gfx_metal_shader.h b/libultraship/include/fast/backends/gfx_metal_shader.h new file mode 100644 index 000000000..fd17a39f6 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_metal_shader.h @@ -0,0 +1,20 @@ +// +// gfx_metal_shader.h +// libultraship +// +// Created by David Chavez on 16.08.22. +// + +#ifdef __APPLE__ +#ifdef __cplusplus +#pragma once +#include +#include + +struct CCFeatures; + +MTL::VertexDescriptor* gfx_metal_build_shader(std::string& result, size_t& numFloats, const CCFeatures& cc_features, + bool three_point_filtering); + +#endif +#endif diff --git a/libultraship/include/fast/backends/gfx_opengl.h b/libultraship/include/fast/backends/gfx_opengl.h new file mode 100644 index 000000000..9252985a2 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_opengl.h @@ -0,0 +1,140 @@ +#ifdef ENABLE_OPENGL +#pragma once + +#include "gfx_rendering_api.h" +#include "../interpreter.h" + +#ifdef _MSC_VER +#include +// #define GL_GLEXT_PROTOTYPES 1 +#include +#elif FOR_WINDOWS +#include +#include "SDL.h" +#define GL_GLEXT_PROTOTYPES 1 +#include "SDL_opengl.h" +#elif __APPLE__ +#include +#include +#elif USE_OPENGLES +#include +#include +#else +#include +#define GL_GLEXT_PROTOTYPES 1 +#include +#endif +namespace Fast { +struct ShaderProgram { + GLuint openglProgramId; + uint8_t numInputs; + bool usedTextures[SHADER_MAX_TEXTURES]; + uint8_t numFloats; + GLint attribLocations[16]; + uint8_t attribSizes[16]; + uint8_t numAttribs; + GLint frameCountLocation; + GLint noiseScaleLocation; + GLint texture_width_location; + GLint texture_height_location; + GLint texture_filtering_location; +}; + +struct FramebufferOGL { + uint32_t width, height; + bool has_depth_buffer; + uint32_t msaa_level; + bool invertY; + + GLuint fbo, clrbuf, clrbufMsaa, rbo; +}; + +struct TextureInfo { + uint16_t width; + uint16_t height; + uint16_t filtering; +}; + +class GfxRenderingAPIOGL final : public GfxRenderingAPI { + public: + ~GfxRenderingAPIOGL() override = default; + const char* GetName() override; + int GetMaxTextureSize() override; + GfxClipParameters GetClipParameters() override; + void UnloadShader(ShaderProgram* oldPrg) override; + void LoadShader(ShaderProgram* newPrg) override; + ShaderProgram* CreateAndLoadNewShader(uint64_t shaderId0, uint32_t shaderId1) override; + ShaderProgram* LookupShader(uint64_t shaderId0, uint32_t shaderId1) override; + void ShaderGetInfo(ShaderProgram* prg, uint8_t* numInputs, bool usedTextures[2]) override; + uint32_t NewTexture() override; + void SelectTexture(int tile, uint32_t textureId) override; + void UploadTexture(const uint8_t* rgba32Buf, uint32_t width, uint32_t height) override; + void SetSamplerParameters(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt) override; + void SetDepthTestAndMask(bool depth_test, bool z_upd) override; + void SetZmodeDecal(bool decal) override; + void SetViewport(int x, int y, int width, int height) override; + void SetScissor(int x, int y, int width, int height) override; + void SetUseAlpha(bool useAlpha) override; + void DrawTriangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) override; + void Init() override; + void OnResize() override; + void StartFrame() override; + void EndFrame() override; + void FinishRender() override; + int CreateFramebuffer() override; + void UpdateFramebufferParameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, + bool opengl_invertY, bool render_target, bool has_depth_buffer, + bool can_extract_depth) override; + void StartDrawToFramebuffer(int fbId, float noiseScale) override; + void CopyFramebuffer(int fbDstId, int fbSrcId, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, + int dstX1, int dstY1) override; + void ClearFramebuffer(bool color, bool depth) override; + void ReadFramebufferToCPU(int fbId, uint32_t width, uint32_t height, uint16_t* rgba16Buf) override; + void ResolveMSAAColorBuffer(int fbIdTarger, int fbIdSrc) override; + std::unordered_map, uint16_t, hash_pair_ff> + GetPixelDepth(int fb_id, const std::set>& coordinates) override; + void* GetFramebufferTextureId(int fbId) override; + void SelectTextureFb(int fbId) override; + void DeleteTexture(uint32_t texId) override; + void SetTextureFilter(FilteringMode mode) override; + FilteringMode GetTextureFilter() override; + void SetSrgbMode() override; + ImTextureID GetTextureById(int id) override; + + private: + void SetUniforms(ShaderProgram* prg) const; + std::string BuildFsShader(const CCFeatures& cc_features); + void SetPerDrawUniforms(); + + std::vector textures; + GLuint mCurrentTextureIds[SHADER_MAX_TEXTURES]; + GLuint mLastBoundTextures[SHADER_MAX_TEXTURES] = {}; + uint8_t mCurrentTile; + int8_t mLastActiveTexture = -1; + int8_t mLastBlendEnabled = -1; + int8_t mLastScissorEnabled = -1; + + std::map, ShaderProgram> mShaderProgramPool; + ShaderProgram* mCurrentShaderProgram; + ShaderProgram* mLastLoadedShader = nullptr; + + GLuint mOpenglVbo = 0; +#if defined(__APPLE__) || defined(USE_OPENGLES) + GLuint mOpenglVao; +#endif + + uint32_t mFrameCount = 0; + + std::vector mFrameBuffers; + size_t mCurrentFrameBuffer = 0; + float mCurrentNoiseScale = 0.0f; + FilteringMode mCurrentFilterMode = FILTER_THREE_POINT; + + GLint mMaxMsaaLevel = 1; + GLuint mPixelDepthRb = 0; + GLuint mPixelDepthFb = 0; + size_t mPixelDepthRbSize = 0; +}; + +} // namespace Fast +#endif diff --git a/libultraship/include/fast/backends/gfx_rendering_api.h b/libultraship/include/fast/backends/gfx_rendering_api.h new file mode 100644 index 000000000..d95bcc0d8 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_rendering_api.h @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include +#include +#include "imconfig.h" + +namespace Fast { +struct ShaderProgram; + +struct GfxClipParameters { + bool z_is_from_0_to_1; + bool invertY; +}; + +enum FilteringMode { FILTER_THREE_POINT, FILTER_LINEAR, FILTER_NONE }; + +// A hash function used to hash a: pair +struct hash_pair_ff { + size_t operator()(const std::pair& p) const { + const auto hash1 = std::hash{}(p.first); + const auto hash2 = std::hash{}(p.second); + + // If hash1 == hash2, their XOR is zero. + return (hash1 != hash2) ? hash1 ^ hash2 : hash1; + } +}; + +class GfxRenderingAPI { + public: + virtual ~GfxRenderingAPI() = default; + virtual const char* GetName() = 0; + virtual int GetMaxTextureSize() = 0; + virtual GfxClipParameters GetClipParameters() = 0; + virtual void UnloadShader(ShaderProgram* oldPrg) = 0; + virtual void LoadShader(ShaderProgram* newPrg) = 0; + virtual ShaderProgram* CreateAndLoadNewShader(uint64_t shaderId0, uint32_t shaderId1) = 0; + virtual ShaderProgram* LookupShader(uint64_t shaderId0, uint32_t shaderId1) = 0; + virtual void ShaderGetInfo(ShaderProgram* prg, uint8_t* numInputs, bool usedTextures[2]) = 0; + virtual uint32_t NewTexture() = 0; + virtual void SelectTexture(int tile, uint32_t textureId) = 0; + virtual void UploadTexture(const uint8_t* rgba32Buf, uint32_t width, uint32_t height) = 0; + virtual void SetSamplerParameters(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt) = 0; + virtual void SetDepthTestAndMask(bool depth_test, bool z_upd) = 0; + virtual void SetZmodeDecal(bool decal) = 0; + virtual void SetViewport(int x, int y, int width, int height) = 0; + virtual void SetScissor(int x, int y, int width, int height) = 0; + virtual void SetUseAlpha(bool useAlpha) = 0; + virtual void DrawTriangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) = 0; + virtual void Init() = 0; + virtual void OnResize() = 0; + virtual void StartFrame() = 0; + virtual void EndFrame() = 0; + virtual void FinishRender() = 0; + virtual int CreateFramebuffer() = 0; + virtual void UpdateFramebufferParameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, + bool opengl_invertY, bool render_target, bool has_depth_buffer, + bool can_extract_depth) = 0; + virtual void StartDrawToFramebuffer(int fbId, float noiseScale) = 0; + virtual void CopyFramebuffer(int fbDstId, int fbSrcId, int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, + int dstY0, int dstX1, int dstY1) = 0; + virtual void ClearFramebuffer(bool color, bool depth) = 0; + virtual void ReadFramebufferToCPU(int fbId, uint32_t width, uint32_t height, uint16_t* rgba16Buf) = 0; + virtual void ResolveMSAAColorBuffer(int fbIdTarger, int fbIdSrc) = 0; + virtual std::unordered_map, uint16_t, hash_pair_ff> + GetPixelDepth(int fb_id, const std::set>& coordinates) = 0; + virtual void* GetFramebufferTextureId(int fbId) = 0; + virtual void SelectTextureFb(int fbId) = 0; + virtual void DeleteTexture(uint32_t texId) = 0; + virtual void SetTextureFilter(FilteringMode mode) = 0; + virtual FilteringMode GetTextureFilter() = 0; + virtual void SetSrgbMode() = 0; + virtual ImTextureID GetTextureById(int id) = 0; + + protected: + int8_t mCurrentDepthTest = 0; + int8_t mCurrentDepthMask = 0; + int8_t mCurrentZmodeDecal = 0; + int8_t mLastDepthTest = -1; + int8_t mLastDepthMask = -1; + int8_t mLastZmodeDecal = -1; + bool mSrgbMode = false; +}; +} // namespace Fast diff --git a/libultraship/include/fast/backends/gfx_screen_config.h b/libultraship/include/fast/backends/gfx_screen_config.h new file mode 100644 index 000000000..30cac0e27 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_screen_config.h @@ -0,0 +1,4 @@ +#pragma once + +#define DESIRED_SCREEN_WIDTH 640 +#define DESIRED_SCREEN_HEIGHT 480 diff --git a/libultraship/include/fast/backends/gfx_sdl.h b/libultraship/include/fast/backends/gfx_sdl.h new file mode 100644 index 000000000..1f03cb639 --- /dev/null +++ b/libultraship/include/fast/backends/gfx_sdl.h @@ -0,0 +1,65 @@ +#pragma once + +#include "gfx_window_manager_api.h" +namespace Fast { +class GfxWindowBackendSDL2 final : public GfxWindowBackend { + public: + GfxWindowBackendSDL2() = default; + ~GfxWindowBackendSDL2() override; + + void Init(const char* gameName, const char* apiName, bool startFullScreen, uint32_t width, uint32_t height, + int32_t posX, int32_t posY) override; + void Close() override; + void SetKeyboardCallbacks(bool (*onKeyDown)(int scancode), bool (*onKeyUp)(int scancode), + void (*onAllKeysUp)()) override; + void SetMouseCallbacks(bool (*onMouseButtonDown)(int btn), bool (*onMouseButtonUp)(int btn)) override; + void SetFullscreenChangedCallback(void (*onFullscreenChanged)(bool is_now_fullscreen)) override; + void SetFullscreen(bool fullscreen) override; + void GetActiveWindowRefreshRate(uint32_t* refreshRate) override; + void SetCursorVisibility(bool visability) override; + void SetMousePos(int32_t posX, int32_t posY) override; + void GetMousePos(int32_t* x, int32_t* y) override; + void GetMouseDelta(int32_t* x, int32_t* y) override; + void GetMouseWheel(float* x, float* y) override; + bool GetMouseState(uint32_t btn) override; + void SetMouseCapture(bool capture) override; + bool IsMouseCaptured() override; + void GetDimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) override; + void HandleEvents() override; + bool IsFrameReady() override; + void SwapBuffersBegin() override; + void SwapBuffersEnd() override; + double GetTime() override; + int GetTargetFps(); + void SetTargetFps(int fps) override; + void SetMaxFrameLatency(int latency) override; + const char* GetKeyName(int scancode) override; + bool CanDisableVsync() override; + bool IsRunning() override; + void Destroy() override; + bool IsFullscreen() override; + + private: + void SetFullscreenImpl(bool on, bool call_callback); + void HandleSingleEvent(SDL_Event& event); + int TranslateScancode(int scancode) const; + int UntranslateScancode(int translatedScancode) const; + void OnKeydown(int scancode) const; + void OnKeyup(int scancode) const; + void OnMouseButtonDown(int btn) const; + void OnMouseButtonUp(int btn) const; + void SyncFramerateWithTime() const; + + SDL_Window* mWnd; + SDL_Rect mCursorClip; + SDL_GLContext mCtx; + SDL_Renderer* mRenderer; + int mSdlToLusTable[512]; + float mMouseWheelX = 0.0f; + float mMouseWheelY = 0.0f; + // OTRTODO: These are redundant. Info can be queried from SDL. + int mWindowWidth = 640; + int mWindowHeight = 480; + void (*mOnAllKeysUp)(); +}; +} // namespace Fast diff --git a/libultraship/include/fast/backends/gfx_window_manager_api.h b/libultraship/include/fast/backends/gfx_window_manager_api.h new file mode 100644 index 000000000..3263d50db --- /dev/null +++ b/libultraship/include/fast/backends/gfx_window_manager_api.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +namespace Fast { +class GfxWindowBackend { + public: + virtual ~GfxWindowBackend() = default; + virtual void Init(const char* gameName, const char* apiName, bool startFullScreen, uint32_t width, uint32_t height, + int32_t posX, int32_t posY) = 0; + virtual void Close() = 0; + virtual void SetKeyboardCallbacks(bool (*mOnKeyDown)(int scancode), bool (*mOnKeyUp)(int scancode), + void (*mOnAllKeysUp)()) = 0; + virtual void SetMouseCallbacks(bool (*mOnMouseButtonDown)(int btn), bool (*mOnMouseButtonUp)(int btn)) = 0; + virtual void SetFullscreenChangedCallback(void (*mOnFullscreenChanged)(bool is_now_fullscreen)) = 0; + virtual void SetFullscreen(bool fullscreen) = 0; + virtual void GetActiveWindowRefreshRate(uint32_t* refreshRate) = 0; + virtual void SetCursorVisibility(bool visability) = 0; + virtual void SetMousePos(int32_t posX, int32_t posY) = 0; + virtual void GetMousePos(int32_t* x, int32_t* y) = 0; + virtual void GetMouseDelta(int32_t* x, int32_t* y) = 0; + virtual void GetMouseWheel(float* x, float* y) = 0; + virtual bool GetMouseState(uint32_t btn) = 0; + virtual void SetMouseCapture(bool capture) = 0; + virtual bool IsMouseCaptured() = 0; + virtual void GetDimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) = 0; + virtual void HandleEvents() = 0; + virtual bool IsFrameReady() = 0; + virtual void SwapBuffersBegin() = 0; + virtual void SwapBuffersEnd() = 0; + virtual double GetTime() = 0; + virtual int GetTargetFps() = 0; + virtual void SetTargetFps(int fps) = 0; + virtual void SetMaxFrameLatency(int latency) = 0; + virtual const char* GetKeyName(int scancode) = 0; + virtual bool CanDisableVsync() = 0; + virtual bool IsRunning() = 0; + virtual void Destroy() = 0; + virtual bool IsFullscreen() = 0; + + protected: + void (*mOnFullscreenChanged)(bool isNowFullscreen); + bool (*mOnKeyDown)(int scancode); + bool (*mOnKeyUp)(int scancode); + bool (*mOnMouseButtonDown)(int btn); + bool (*mOnMouseButtonUp)(int btn); + uint32_t mTargetFps = 60; + bool mFullScreen; + bool mIsRunning = true; + bool mVsyncEnabled = true; +}; +} // namespace Fast diff --git a/libultraship/include/fast/f3dex.h b/libultraship/include/fast/f3dex.h new file mode 100644 index 000000000..894d236ae --- /dev/null +++ b/libultraship/include/fast/f3dex.h @@ -0,0 +1,153 @@ +#pragma once + +#define G_IMMFIRST -65 + +constexpr int8_t F3DEX_G_SPNOOP = OPCODE(0x0); +constexpr int8_t F3DEX_G_MTX = OPCODE(0x1); +constexpr int8_t F3DEX_G_RESERVED0 = OPCODE(0x2); +constexpr int8_t F3DEX_G_MOVEMEM = OPCODE(0x3); +constexpr int8_t F3DEX_G_VTX = OPCODE(0x4); +constexpr int8_t F3DEX_G_RESERVED1 = OPCODE(0x5); +constexpr int8_t F3DEX_G_DL = OPCODE(0x6); +constexpr int8_t F3DEX_G_RESERVED2 = OPCODE(0x7); +constexpr int8_t F3DEX_G_RESERVED3 = OPCODE(0x8); +constexpr int8_t F3DEX_G_TRI1 = OPCODE(G_IMMFIRST - 0); +constexpr int8_t F3DEX_G_CULLDL = OPCODE(G_IMMFIRST - 1); +constexpr int8_t F3DEX_G_POPMTX = OPCODE(G_IMMFIRST - 2); +constexpr int8_t F3DEX_G_MOVEWORD = OPCODE(G_IMMFIRST - 3); +constexpr int8_t F3DEX_G_TEXTURE = OPCODE(G_IMMFIRST - 4); +constexpr int8_t F3DEX_G_SETOTHERMODE_H = OPCODE(G_IMMFIRST - 5); +constexpr int8_t F3DEX_G_SETOTHERMODE_L = OPCODE(G_IMMFIRST - 6); +constexpr int8_t F3DEX_G_ENDDL = OPCODE(G_IMMFIRST - 7); +constexpr int8_t F3DEX_G_SETGEOMETRYMODE = OPCODE(G_IMMFIRST - 8); +constexpr int8_t F3DEX_G_CLEARGEOMETRYMODE = OPCODE(G_IMMFIRST - 9); +constexpr int8_t F3DEX_G_LINE3D = OPCODE(G_IMMFIRST - 10); +constexpr int8_t F3DEX_G_RDPHALF_1 = OPCODE(G_IMMFIRST - 11); +constexpr int8_t F3DEX_G_RDPHALF_2 = OPCODE(G_IMMFIRST - 12); +constexpr int8_t F3DEX_G_MODIFYVTX = OPCODE(G_IMMFIRST - 13); +constexpr int8_t F3DEX_G_RDPHALF_CONT = OPCODE(G_IMMFIRST - 13); +constexpr int8_t F3DEX_G_TRI2 = OPCODE(G_IMMFIRST - 14); +constexpr int8_t F3DEX_G_BRANCH_Z = OPCODE(G_IMMFIRST - 15); +constexpr int8_t F3DEX_G_LOAD_UCODE = OPCODE(G_IMMFIRST - 16); +constexpr int8_t F3DEX_G_QUAD = OPCODE(G_IMMFIRST - 10); + +// S2DEX +constexpr int8_t F3DEX_G_SPRITE2D_BASE = OPCODE(9); +constexpr int8_t F3DEX_G_SPRITE2D_SCALEFLIP = OPCODE(G_IMMFIRST - 1); +constexpr int8_t F3DEX_G_SPRITE2D_DRAW = OPCODE(G_IMMFIRST - 2); +constexpr int8_t F3DEX_G_NOOP = OPCODE(0xc0); + +/* + * G_MTX: parameter flags + */ +constexpr int8_t F3DEX_G_MTX_MODELVIEW = 0x00; +constexpr int8_t F3DEX_G_MTX_PROJECTION = 0x01; +constexpr int8_t F3DEX_G_MTX_MUL = 0x00; +constexpr int8_t F3DEX_G_MTX_LOAD = 0x02; +constexpr int8_t F3DEX_G_MTX_NOPUSH = 0x00; +constexpr int8_t F3DEX_G_MTX_PUSH = 0x04; + +/* + * flags for G_SETGEOMETRYMODE + * (this rendering state is maintained in RSP) + * + * DO NOT USE THE LOW 8 BITS OF GEOMETRYMODE: + * The weird bit-ordering is for the micro-code: the lower byte + * can be OR'd in with G_TRI_SHADE (11001100) to construct + * the triangle command directly. Don't break it... + * + * DO NOT USE THE HIGH 8 BITS OF GEOMETRYMODE: + * The high byte is OR'd with 0x703 to form the clip code mask. + * If it is set to 0x04, this will cause near clipping to occur. + * If it is zero, near clipping will not occur. + * + * Further explanation: + * G_SHADE is necessary in order to see the color that you passed + * down with the vertex. If G_SHADE isn't set, you need to set the DP + * appropriately and use primcolor to see anything. + * + * G_SHADING_SMOOTH enabled means use all 3 colors of the triangle. + * If it is not set, then do 'flat shading', where only one vertex color + * is used (and all 3 vertices are set to that same color by the ucode) + * See the man page for gSP1Triangle(). + * + */ + +constexpr uint32_t F3DEX_G_CLIPPING = 0x00800000; +constexpr uint32_t F3DEX_G_TEXTURE_ENABLE = 0x00000002; +constexpr uint32_t F3DEX_G_SHADING_SMOOTH = 0x00000200; +constexpr uint32_t F3DEX_G_CULL_FRONT = 0x00001000; +constexpr uint32_t F3DEX_G_CULL_BACK = 0x00002000; +constexpr uint32_t F3DEX_G_CULL_BOTH = 0x00003000; + +/* + * MOVEMEM indices + * + * Each of these indexes an entry in a dmem table + * which points to a 1-4 word block of dmem in + * which to store a 1-4 word DMA. + * + */ +constexpr uint8_t F3DEX_G_MV_VIEWPORT = 0x80; +constexpr uint8_t F3DEX_G_MV_LOOKATY = 0x82; +constexpr uint8_t F3DEX_G_MV_LOOKATX = 0x84; +constexpr uint8_t F3DEX_G_MV_L0 = 0x86; +constexpr uint8_t F3DEX_G_MV_L1 = 0x88; +constexpr uint8_t F3DEX_G_MV_L2 = 0x8a; +constexpr uint8_t F3DEX_G_MV_L3 = 0x8c; +constexpr uint8_t F3DEX_G_MV_L4 = 0x8e; +constexpr uint8_t F3DEX_G_MV_L5 = 0x90; +constexpr uint8_t F3DEX_G_MV_L6 = 0x92; +constexpr uint8_t F3DEX_G_MV_L7 = 0x94; +constexpr uint8_t F3DEX_G_MV_TXTATT = 0x96; +constexpr uint8_t F3DEX_G_MV_MATRIX_1 = 0x9e; +constexpr uint8_t F3DEX_G_MV_MATRIX_2 = 0x98; +constexpr uint8_t F3DEX_G_MV_MATRIX_3 = 0x9a; +constexpr uint8_t F3DEX_G_MV_MATRIX_4 = 0x9c; + +/* + * MOVEWORD indices + * + * Each of these indexes an entry in a dmem table + * which points to a word in dmem in dmem where + * an immediate word will be stored. + * + */ +constexpr int8_t F3DEX_G_MW_POINTS = 0x0c; + +/* + * These are offsets from the address in the dmem table + */ + +constexpr int8_t F3DEX_G_MWO_aLIGHT_2 = OPCODE(0x20); +constexpr int8_t F3DEX_G_MWO_bLIGHT_2 = OPCODE(0x24); +constexpr int8_t F3DEX_G_MWO_aLIGHT_3 = OPCODE(0x40); +constexpr int8_t F3DEX_G_MWO_bLIGHT_3 = OPCODE(0x44); +constexpr int8_t F3DEX_G_MWO_aLIGHT_4 = OPCODE(0x60); +constexpr int8_t F3DEX_G_MWO_bLIGHT_4 = OPCODE(0x64); +constexpr int8_t F3DEX_G_MWO_aLIGHT_5 = OPCODE(0x80); +constexpr int8_t F3DEX_G_MWO_bLIGHT_5 = OPCODE(0x84); +constexpr int8_t F3DEX_G_MWO_aLIGHT_6 = OPCODE(0xa0); +constexpr int8_t F3DEX_G_MWO_bLIGHT_6 = OPCODE(0xa4); +constexpr int8_t F3DEX_G_MWO_aLIGHT_7 = OPCODE(0xc0); +constexpr int8_t F3DEX_G_MWO_bLIGHT_7 = OPCODE(0xc4); +constexpr int8_t F3DEX_G_MWO_aLIGHT_8 = OPCODE(0xe0); +constexpr int8_t F3DEX_G_MWO_bLIGHT_8 = OPCODE(0xe4); + +/*===========================================================================* + * GBI Commands for S2DEX microcode + *===========================================================================*/ +/* GBI Header */ +constexpr int8_t F3DEX_G_BG_1CYC = OPCODE(0x01); +constexpr int8_t F3DEX_G_BG_COPY = OPCODE(0x02); +constexpr int8_t F3DEX_G_OBJ_RECTANGLE = OPCODE(0x03); +constexpr int8_t F3DEX_G_OBJ_SPRITE = OPCODE(0x04); +constexpr int8_t F3DEX_G_OBJ_MOVEMEM = OPCODE(0x05); +constexpr int8_t F3DEX_G_SELECT_DL = OPCODE(0xb0); +constexpr int8_t F3DEX_G_OBJ_RENDERMODE = OPCODE(0xb1); +constexpr int8_t F3DEX_G_OBJ_RECTANGLE_R = OPCODE(0xb2); +constexpr int8_t F3DEX_G_OBJ_LOADTXTR = OPCODE(0xc1); +constexpr int8_t F3DEX_G_OBJ_LDTX_SPRITE = OPCODE(0xc2); +constexpr int8_t F3DEX_G_OBJ_LDTX_RECT = OPCODE(0xc3); +constexpr int8_t F3DEX_G_OBJ_LDTX_RECT_R = OPCODE(0xc4); +constexpr int8_t F3DEX_G_RDPHALF_0 = OPCODE(0xe4); diff --git a/libultraship/include/fast/f3dex2.h b/libultraship/include/fast/f3dex2.h new file mode 100644 index 000000000..e47d391dd --- /dev/null +++ b/libultraship/include/fast/f3dex2.h @@ -0,0 +1,149 @@ +#pragma once + +constexpr int8_t F3DEX2_G_NOOP = OPCODE(0x00); +constexpr int8_t F3DEX2_G_RDPHALF_2 = OPCODE(0xf1); +constexpr int8_t F3DEX2_G_SETOTHERMODE_H = OPCODE(0xe3); +constexpr int8_t F3DEX2_G_SETOTHERMODE_L = OPCODE(0xe2); +constexpr int8_t F3DEX2_G_RDPHALF_1 = OPCODE(0xe1); +constexpr int8_t F3DEX2_G_SPNOOP = OPCODE(0xe0); +constexpr int8_t F3DEX2_G_ENDDL = OPCODE(0xdf); +constexpr int8_t F3DEX2_G_DL = OPCODE(0xde); +constexpr int8_t F3DEX2_G_LOAD_UCODE = OPCODE(0xdd); +constexpr int8_t F3DEX2_G_MOVEMEM = OPCODE(0xdc); +constexpr int8_t F3DEX2_G_MOVEWORD = OPCODE(0xdb); +constexpr int8_t F3DEX2_G_MTX = OPCODE(0xda); +constexpr int8_t F3DEX2_G_GEOMETRYMODE = OPCODE(0xd9); +constexpr int8_t F3DEX2_G_POPMTX = OPCODE(0xd8); +constexpr int8_t F3DEX2_G_TEXTURE = OPCODE(0xd7); +constexpr int8_t F3DEX2_G_DMA_IO = OPCODE(0xd6); +constexpr int8_t F3DEX2_G_SPECIAL_1 = OPCODE(0xd5); +constexpr int8_t F3DEX2_G_SPECIAL_2 = OPCODE(0xd4); +constexpr int8_t F3DEX2_G_SPECIAL_3 = OPCODE(0xd3); + +constexpr int8_t F3DEX2_G_VTX = OPCODE(0x01); +constexpr int8_t F3DEX2_G_MODIFYVTX = OPCODE(0x02); +constexpr int8_t F3DEX2_G_CULLDL = OPCODE(0x03); +constexpr int8_t F3DEX2_G_BRANCH_Z = OPCODE(0x04); +constexpr int8_t F3DEX2_G_TRI1 = OPCODE(0x05); +constexpr int8_t F3DEX2_G_TRI2 = OPCODE(0x06); +constexpr int8_t F3DEX2_G_QUAD = OPCODE(0x07); +constexpr int8_t F3DEX2_G_LINE3D = OPCODE(0x08); + +/* + * G_MTX: parameter flags + */ +constexpr int8_t F3DEX2_G_MTX_MODELVIEW = OPCODE(0x00); +constexpr int8_t F3DEX2_G_MTX_PROJECTION = OPCODE(0x04); +constexpr int8_t F3DEX2_G_MTX_MUL = OPCODE(0x00); +constexpr int8_t F3DEX2_G_MTX_LOAD = OPCODE(0x02); +constexpr int8_t F3DEX2_G_MTX_NOPUSH = OPCODE(0x00); +constexpr int8_t F3DEX2_G_MTX_PUSH = OPCODE(0x01); + +/* + * flags for G_SETGEOMETRYMODE + * (this rendering state is maintained in RSP) + * + * DO NOT USE THE LOW 8 BITS OF GEOMETRYMODE: + * The weird bit-ordering is for the micro-code: the lower byte + * can be OR'd in with G_TRI_SHADE (11001100) to construct + * the triangle command directly. Don't break it... + * + * DO NOT USE THE HIGH 8 BITS OF GEOMETRYMODE: + * The high byte is OR'd with 0x703 to form the clip code mask. + * If it is set to 0x04, this will cause near clipping to occur. + * If it is zero, near clipping will not occur. + * + * Further explanation: + * G_SHADE is necessary in order to see the color that you passed + * down with the vertex. If G_SHADE isn't set, you need to set the DP + * appropriately and use primcolor to see anything. + * + * G_SHADING_SMOOTH enabled means use all 3 colors of the triangle. + * If it is not set, then do 'flat shading', where only one vertex color + * is used (and all 3 vertices are set to that same color by the ucode) + * See the man page for gSP1Triangle(). + * + */ +constexpr uint32_t F3DEX2_G_CLIPPING = 0x00800000; +constexpr uint32_t F3DEX2_G_TEXTURE_ENABLE = 0x00000000; +constexpr uint32_t F3DEX2_G_SHADING_SMOOTH = 0x00200000; +constexpr uint32_t F3DEX2_G_CULL_FRONT = 0x00000200; +constexpr uint32_t F3DEX2_G_CULL_BACK = 0x00000400; +constexpr uint32_t F3DEX2_G_CULL_BOTH = 0x00000600; + +/* + * MOVEMEM indices + * + * Each of these indexes an entry in a dmem table + * which points to a 1-4 word block of dmem in + * which to store a 1-4 word DMA. + * + */ +constexpr int8_t F3DEX2_G_MV_MMTX = OPCODE(2); +constexpr int8_t F3DEX2_G_MV_PMTX = OPCODE(6); +constexpr int8_t F3DEX2_G_MV_VIEWPORT = OPCODE(8); +constexpr int8_t F3DEX2_G_MV_LIGHT = OPCODE(10); +constexpr int8_t F3DEX2_G_MV_POINT = OPCODE(12); +constexpr int8_t F3DEX2_G_MV_MATRIX = OPCODE(14); +constexpr int8_t F3DEX2_G_MVO_LOOKATX = OPCODE(0 * 24); +constexpr int8_t F3DEX2_G_MVO_LOOKATY = OPCODE(1 * 24); +constexpr int8_t F3DEX2_G_MVO_L0 = OPCODE(2 * 24); +constexpr int8_t F3DEX2_G_MVO_L1 = OPCODE(3 * 24); +constexpr int8_t F3DEX2_G_MVO_L2 = OPCODE(4 * 24); +constexpr int8_t F3DEX2_G_MVO_L3 = OPCODE(5 * 24); +constexpr int8_t F3DEX2_G_MVO_L4 = OPCODE(6 * 24); +constexpr int8_t F3DEX2_G_MVO_L5 = OPCODE(7 * 24); +constexpr int8_t F3DEX2_G_MVO_L6 = OPCODE(8 * 24); +constexpr int8_t F3DEX2_G_MVO_L7 = OPCODE(9 * 24); + +/* + * MOVEWORD indices + * + * Each of these indexes an entry in a dmem table + * which points to a word in dmem in dmem where + * an immediate word will be stored. + * + */ +constexpr int8_t F3DEX2_G_MW_FORCEMTX = 0x0c; + +/* + * MOVEWORD indices + * + * Each of these indexes an entry in a dmem table + * which points to a word in dmem in dmem where + * an immediate word will be stored. + * + * These are offsets from the address in the dmem table + */ +constexpr int8_t F3DEX2_G_MWO_aLIGHT_2 = OPCODE(0x18); +constexpr int8_t F3DEX2_G_MWO_bLIGHT_2 = OPCODE(0x1c); +constexpr int8_t F3DEX2_G_MWO_aLIGHT_3 = OPCODE(0x30); +constexpr int8_t F3DEX2_G_MWO_bLIGHT_3 = OPCODE(0x34); +constexpr int8_t F3DEX2_G_MWO_aLIGHT_4 = OPCODE(0x48); +constexpr int8_t F3DEX2_G_MWO_bLIGHT_4 = OPCODE(0x4c); +constexpr int8_t F3DEX2_G_MWO_aLIGHT_5 = OPCODE(0x60); +constexpr int8_t F3DEX2_G_MWO_bLIGHT_5 = OPCODE(0x64); +constexpr int8_t F3DEX2_G_MWO_aLIGHT_6 = OPCODE(0x78); +constexpr int8_t F3DEX2_G_MWO_bLIGHT_6 = OPCODE(0x7c); +constexpr int8_t F3DEX2_G_MWO_aLIGHT_7 = OPCODE(0x90); +constexpr int8_t F3DEX2_G_MWO_bLIGHT_7 = OPCODE(0x94); +constexpr int8_t F3DEX2_G_MWO_aLIGHT_8 = OPCODE(0xa8); +constexpr int8_t F3DEX2_G_MWO_bLIGHT_8 = OPCODE(0xac); + +/*===========================================================================* + * GBI Commands for S2DEX microcode + *===========================================================================*/ +/* GBI Header */ +constexpr int8_t F3DEX2_G_OBJ_RECTANGLE_R = OPCODE(0xda); +constexpr int8_t F3DEX2_G_OBJ_MOVEMEM = OPCODE(0xdc); +constexpr int8_t F3DEX2_G_RDPHALF_0 = OPCODE(0xe4); +constexpr int8_t F3DEX2_G_OBJ_RECTANGLE = OPCODE(0x01); +constexpr int8_t F3DEX2_G_OBJ_SPRITE = OPCODE(0x02); +constexpr int8_t F3DEX2_G_SELECT_DL = OPCODE(0x04); +constexpr int8_t F3DEX2_G_OBJ_LOADTXTR = OPCODE(0x05); +constexpr int8_t F3DEX2_G_OBJ_LDTX_SPRITE = OPCODE(0x06); +constexpr int8_t F3DEX2_G_OBJ_LDTX_RECT = OPCODE(0x07); +constexpr int8_t F3DEX2_G_OBJ_LDTX_RECT_R = OPCODE(0x08); +constexpr int8_t F3DEX2_G_BG_1CYC = OPCODE(0x09); +constexpr int8_t F3DEX2_G_BG_COPY = OPCODE(0x0a); +constexpr int8_t F3DEX2_G_OBJ_RENDERMODE = OPCODE(0x0b); \ No newline at end of file diff --git a/libultraship/include/fast/interpreter.h b/libultraship/include/fast/interpreter.h new file mode 100644 index 000000000..ee526729c --- /dev/null +++ b/libultraship/include/fast/interpreter.h @@ -0,0 +1,531 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fast/lus_gbi.h" +#include "fast/types.h" +#include "fast/ucodehandlers.h" +#include "backends/gfx_rendering_api.h" + +#include "fast/resource/type/Texture.h" +#include "ship/resource/Resource.h" + +// TODO figure out why changing these to 640x480 makes the game only render in a quarter of the window +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 + +#include +#include + +#ifdef __cplusplus +#include +#endif + +/*enum { + CC_0, + CC_TEXEL0, + CC_TEXEL1, + CC_PRIM, + CC_SHADE, + CC_ENV, + CC_TEXEL0A, + CC_LOD +};*/ + +enum { + SHADER_0, + SHADER_INPUT_1, + SHADER_INPUT_2, + SHADER_INPUT_3, + SHADER_INPUT_4, + SHADER_INPUT_5, + SHADER_INPUT_6, + SHADER_INPUT_7, + SHADER_TEXEL0, + SHADER_TEXEL0A, + SHADER_TEXEL1, + SHADER_TEXEL1A, + SHADER_1, + SHADER_COMBINED, + SHADER_NOISE +}; + +#ifdef __cplusplus +enum class ShaderOpts { + ALPHA, + FOG, + TEXTURE_EDGE, + NOISE, + _2CYC, + ALPHA_THRESHOLD, + INVISIBLE, + GRAYSCALE, + TEXEL0_CLAMP_S, + TEXEL0_CLAMP_T, + TEXEL1_CLAMP_S, + TEXEL1_CLAMP_T, + TEXEL0_MASK, + TEXEL1_MASK, + TEXEL0_BLEND, + TEXEL1_BLEND, + USE_SHADER, + MAX +}; + +#define SHADER_OPT(opt) ((uint64_t)(1 << static_cast(ShaderOpts::opt))) +#endif + +struct ColorCombinerKey { + uint64_t combine_mode; + uint64_t options; + +#ifdef __cplusplus + auto operator<=>(const ColorCombinerKey&) const = default; +#endif +}; + +#define SHADER_MAX_TEXTURES 6 +#define SHADER_FIRST_TEXTURE 0 +#define SHADER_FIRST_MASK_TEXTURE 2 +#define SHADER_FIRST_REPLACEMENT_TEXTURE 4 + +struct CCFeatures { + int c[2][2][4]; + bool opt_alpha; + bool opt_fog; + bool opt_texture_edge; + bool opt_noise; + bool opt_2cyc; + bool opt_alpha_threshold; + bool opt_invisible; + bool opt_grayscale; + bool usedTextures[2]; + bool used_masks[2]; + bool used_blend[2]; + bool clamp[2][2]; + int numInputs; + bool do_single[2][2]; + bool do_multiply[2][2]; + bool do_mix[2][2]; + bool color_alpha_same[2]; + int16_t shader_id; +}; + +void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeatures* cc_features); + +union Gfx; + +namespace Fast { + +class GfxRenderingAPI; +class GfxWindowBackend; + +constexpr size_t MAX_SEGMENT_POINTERS = 16; + +struct GfxExecStack { + // This is a dlist stack used to handle dlist calls. + std::stack cmd_stack = {}; + // This is also a dlist stack but a std::vector is used to make it possible + // to iterate on the elements. + // The purpose of this is to identify an instruction at a poin in time + // which would not be possible with just a F3DGfx* because a dlist can be called multiple times + // what we do instead is store the call path that leads to the instruction (including branches) + std::vector gfx_path = {}; + struct CodeDisp { + const char* file; + int line; + }; + // stack for OpenDisp/CloseDisps + std::vector disp_stack{}; + + void start(F3DGfx* dlist); + void stop(); + F3DGfx*& currCmd(); + void openDisp(const char* file, int line); + void closeDisp(); + const std::vector& getDisp() const; + void branch(F3DGfx* caller); + void call(F3DGfx* caller, F3DGfx* callee); + F3DGfx* ret(); +}; + +struct XYWidthHeight { + int16_t x, y; + uint32_t width, height; +}; + +struct GfxDimensions { + float internal_mul; + uint32_t width, height; + float aspect_ratio; +}; + +struct TextureCacheKey { + const uint8_t* texture_addr; + const uint8_t* palette_addrs[2]; + uint8_t fmt, siz; + uint8_t palette_index; + uint32_t size_bytes; + + bool operator==(const TextureCacheKey&) const noexcept = default; + + struct Hasher { + size_t operator()(const TextureCacheKey& key) const noexcept { + uintptr_t addr = (uintptr_t)key.texture_addr; + return (size_t)(addr ^ (addr >> 5)); + } + }; +}; + +typedef std::unordered_map TextureCacheMap; +typedef std::pair TextureCacheNode; + +struct TextureCacheValue { + uint32_t texture_id; + uint8_t cms, cmt; + bool linear_filter; + + std::list::iterator lru_location; +}; + +struct TextureCacheMapIter { + TextureCacheMap::iterator it; +}; + +struct RGBA { + uint8_t r, g, b, a; +}; + +struct LoadedVertex { + float x, y, z, w; + float u, v; + struct RGBA color; + uint8_t clip_rej; +}; + +struct RawTexMetadata { + uint16_t width, height; + float h_byte_scale = 1, v_pixel_scale = 1; + std::shared_ptr resource; + Fast::TextureType type; +}; + +struct ShaderMod { + bool enabled = false; + int16_t id; + uint8_t type; +}; + +#define MAX_LIGHTS 32 +#define MAX_VERTICES 64 + +struct RSP { + float modelview_matrix_stack[11][4][4]; + uint8_t modelview_matrix_stack_size; + + float MP_matrix[4][4]; + float P_matrix[4][4]; + + F3DLight_t lookat[2]; + F3DLight current_lights[MAX_LIGHTS + 1]; + float current_lights_coeffs[MAX_LIGHTS][3]; + float current_lookat_coeffs[2][3]; // lookat_x, lookat_y + uint8_t current_num_lights; // includes ambient light + bool lights_changed; + + uint32_t geometry_mode; + int16_t fog_mul, fog_offset; + + uint32_t extra_geometry_mode; + + struct { + // U0.16 + uint16_t s, t; + } texture_scaling_factor; + + struct LoadedVertex loaded_vertices[MAX_VERTICES + 4]; + ShaderMod current_shader; +}; + +struct RDP { + const uint8_t* palettes[2]; + struct { + const uint8_t* addr; + uint8_t siz; + uint32_t width; + uint32_t tex_flags; + struct RawTexMetadata raw_tex_metadata; + } texture_to_load; + struct { + const uint8_t* addr; + uint32_t orig_size_bytes; + uint32_t size_bytes; + uint32_t full_image_line_size_bytes; + uint32_t line_size_bytes; + uint32_t tex_flags; + struct RawTexMetadata raw_tex_metadata; + bool masked; + bool blended; + } loaded_texture[2]; + struct { + uint8_t fmt; + uint8_t siz; + uint8_t cms, cmt; + uint8_t shifts, shiftt; + float uls, ult, lrs, lrt; + uint16_t tmem; // 0-511, in 64-bit word units + uint32_t line_size_bytes; + uint8_t palette; + uint8_t tmem_index; // 0 or 1 for offset 0 kB or offset 2 kB, respectively + } texture_tile[8]; + bool textures_changed[2]; + + uint8_t first_tile_index; + + uint32_t other_mode_l, other_mode_h; + uint64_t combine_mode; + bool grayscale; + ShaderMod current_shader; + + uint8_t prim_lod_fraction; + struct RGBA env_color, prim_color, fog_color, fill_color, grayscale_color; + struct XYWidthHeight viewport, scissor; + bool viewport_or_scissor_changed; + void* z_buf_address; + void* color_image_address; +}; + +typedef enum Attribute { + MTX_PROJECTION, + MTX_LOAD, + MTX_PUSH, + MTX_NOPUSH, + CULL_FRONT, + CULL_BACK, + CULL_BOTH, + MV_VIEWPORT, + MV_LIGHT, +} Attribute; + +extern GfxExecStack g_exec_stack; + +struct GfxTextureCache { + TextureCacheMap map; + std::list lru; + std::vector free_texture_ids; +}; + +struct ColorCombiner { + uint64_t shader_id0; + uint32_t shader_id1; + bool usedTextures[2]; + struct ShaderProgram* prg[16]; + uint8_t shader_input_mapping[2][7]; +}; + +struct RenderingState { + uint8_t depth_test_and_mask; // 1: depth test, 2: depth mask + bool decal_mode; + bool alpha_blend; + struct XYWidthHeight viewport, scissor; + struct ShaderProgram* mShaderProgram; + TextureCacheNode* mTextures[SHADER_MAX_TEXTURES]; +}; + +struct FBInfo { + uint32_t orig_width, orig_height; // Original shape + uint32_t applied_width, applied_height; // Up-scaled for the viewport + uint32_t native_width, native_height; // Max "native" size of the screen, used for up-scaling + bool resize; // Scale to match the viewport +}; + +struct MaskedTextureEntry { + uint8_t* mask; + uint8_t* replacementData; +}; + +class Interpreter { + public: + Interpreter(); + ~Interpreter(); + + void Init(GfxWindowBackend* wapi, class GfxRenderingAPI* rapi, const char* game_name, bool start_in_fullscreen, + uint32_t width, uint32_t height, uint32_t posX, uint32_t posY); + void Destroy(); + void GetDimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY); + GfxRenderingAPI* GetCurrentRenderingAPI(); + void StartFrame(); + void RunGuiOnly(); + void Run(Gfx* commands, const std::unordered_map& mtx_replacements); + void EndFrame(); + void HandleWindowEvents(); + bool IsFrameReady(); + bool ViewportMatchesRendererResolution(); + int GetTargetFps(); + void SetTargetFps(int fps); + void SetMaxFrameLatency(int latency); + int CreateFrameBuffer(uint32_t width, uint32_t height, uint32_t native_width, uint32_t native_height, + uint8_t resize); + void SetFrameBuffer(int fb, float noiseScale); + void CopyFrameBuffer(int fb_dst_id, int fb_src_id, bool copyOnce, bool* hasCopiedPtr); + void ResetFrameBuffer(); + void AdjustPixelDepthCoordinates(float& x, float& y); + void GetPixelDepthPrepare(float x, float y); + uint16_t GetPixelDepth(float x, float y); + void RegisterBlendedTexture(const char* name, uint8_t* mask, uint8_t* replacement); + void UnregisterBlendedTexture(const char* name); + + void SetNativeDimensions(float width, float height); + void SetResolutionMultiplier(float multiplier); + void SetMsaaLevel(uint32_t level); + void GetCurDimensions(uint32_t* width, uint32_t* height); + + // private: TODO make these private + void Flush(); + ShaderProgram* LookupOrCreateShaderProgram(uint64_t id0, uint64_t id1); + ColorCombiner* LookupOrCreateColorCombiner(const ColorCombinerKey& key); + void TextureCacheClear(); + bool TextureCacheLookup(int i, const TextureCacheKey& key); + void TextureCacheDelete(const uint8_t* origAddr); + void ImportTextureRgba16(int tile, bool importReplacement); + void ImportTextureRgba32(int tile, bool importReplacement); + void ImportTextureIA4(int tile, bool importReplacement); + void ImportTextureIA8(int tile, bool importReplacement); + void ImportTextureIA16(int tile, bool importReplacement); + void ImportTextureI4(int tile, bool importReplacement); + void ImportTextureI8(int tile, bool importReplacement); + void ImportTextureCi4(int tile, bool importReplacement); + void ImportTextureCi8(int tile, bool importReplacement); + void ImportTextureRaw(int tile, bool importReplacement); + void ImportTextureImg(int tile, bool importReplacement); + void ImportTexture(int i, int tile, bool importReplacement); + void ImportTextureMask(int i, int tile); + void CalculateNormalDir(const F3DLight_t*, float coeffs[3]); + + void GfxSpMatrix(uint8_t params, const int32_t* addr); + void GfxSpPopMatrix(uint32_t count); + void GfxSpVertex(size_t numVertices, size_t destIndex, const F3DVtx* vertices); + void GfxSpModifyVertex(uint16_t vtxIdx, uint8_t where, uint32_t val); + void GfxSpTri1(uint8_t vtx1Idx, uint8_t vtx2Idx, uint8_t vtx3Idx, bool isRect); + void GfxSpGeometryMode(uint32_t clear, uint32_t set); + void GfxSpExtraGeometryMode(uint32_t clear, uint32_t set); + void GfxSpMovememF3dex2(uint8_t index, uint8_t offset, const void* data); + void GfxSpMovememF3d(uint8_t index, uint8_t offset, const void* data); + void GfxSpMovewordF3dex2(uint8_t index, uint16_t offset, uintptr_t data); + void GfxSpMovewordF3d(uint8_t index, uint16_t offset, uintptr_t data); + void GfxSpTexture(uint16_t sc, uint16_t tc, uint8_t level, uint8_t tile, uint8_t on); + void GfxDpSetScissor(uint32_t mode, uint32_t ulx, uint32_t uly, uint32_t lrx, uint32_t lry); + void GfxDpSetTextureImage(uint32_t format, uint32_t size, uint32_t width, const char* texPath, uint32_t texFlags, + RawTexMetadata rawTexMetdata, const void* addr); + void GfxDpSetTile(uint8_t fmt, uint32_t siz, uint32_t line, uint32_t tmem, uint8_t tile, uint32_t palette, + uint32_t cmt, uint32_t maskt, uint32_t shiftt, uint32_t cms, uint32_t masks, uint32_t shifts); + void GfxDpSetTileSize(uint8_t tile, uint16_t uls, uint16_t ult, uint16_t lrs, uint16_t lrt); + void GfxDpLoadTlut(uint8_t tile, uint32_t high_index); + void GfxDpLoadBlock(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t dxt); + void GfxDpLoadTile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t lrt); + void GfxDpSetCombineMode(uint32_t rgb, uint32_t alpha, uint32_t rgb_cyc2, uint32_t alpha_cyc2); + void GfxDpSetGrayscaleColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + void GfxDpSetEnvColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + void GfxDpSetPrimColor(uint8_t m, uint8_t r, uint8_t l, uint8_t g, uint8_t b, uint8_t a); + void GfxDpSetFogColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + void GfxDpSetBlendColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + void GfxDpSetFillColor(uint32_t pickedColor); + void GfxDrawRectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry); + void GfxDpTextureRectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry, uint8_t tile, int16_t uls, + int16_t ult, int16_t dsdx, int16_t dtdy, bool flip); + void GfxDpImageRectangle(int32_t tile, int32_t w, int32_t h, int32_t ulx, int32_t uly, int16_t uls, int16_t ult, + int32_t lrx, int32_t lry, int16_t lrs, int16_t lrt); + void GfxDpFillRectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry); + void GfxDpSetZImage(void* zBufAddr); + void GfxDpSetColorImage(uint32_t format, uint32_t size, uint32_t width, void* address); + void GfxSpSetOtherMode(uint32_t shift, uint32_t num_bits, uint64_t mode); + void GfxDpSetOtherMode(uint32_t h, uint32_t l); + + void Gfxs2dexBgCopy(F3DuObjBg* bg); + void Gfxs2dexBg1cyc(F3DuObjBg* bg); + void Gfxs2dexRecyCopy(F3DuObjSprite* spr); + + void AdjustWidthHeightForScale(uint32_t& width, uint32_t& height, uint32_t nativeWidth, + uint32_t nativeHeight) const; + float AdjXForAspectRatio(float x) const; + void AdjustVIewportOrScissor(XYWidthHeight* area); + void CalcAndSetViewport(const F3DVp_t* viewport); + int16_t CreateShader(const std::string& path); + + void SpReset(); + void* SegAddr(uintptr_t w1); + + static const char* CCMUXtoStr(uint32_t ccmux); + static const char* ACMUXtoStr(uint32_t acmux); + static void GenerateCC(ColorCombiner* comb, const ColorCombinerKey& key); + static std::string GetBaseTexturePath(const std::string& path); + static void NormalizeVector(float v[3]); + static void TransposedMatrixMul(float res[3], const float a[3], const float b[4][4]); + static void MatrixMul(float res[4][4], const float a[4][4], const float b[4][4]); + + RSP* mRsp; + RDP* mRdp; + RenderingState mRenderingState{}; + + GfxTextureCache mTextureCache{}; + std::map mColorCombinerPool; // color_combiner_pool; + std::map::iterator mPrevCombiner = mColorCombinerPool.end(); + uint8_t* mTexUploadBuffer = nullptr; + + GfxDimensions mGfxCurrentWindowDimensions{}; // gfx_current_window_dimensions; + int32_t mCurWindowPosX{}; + int32_t mCurWindowPosY{}; + GfxDimensions mCurDimensions{}; // gfx_current_dimensions; + GfxDimensions mPrvDimensions{}; // gfx_prev_dimensions; + XYWidthHeight mGameWindowViewport{}; // gfx_current_game_window_viewport; + XYWidthHeight mNativeDimensions{}; // gfx_native_dimensions; + XYWidthHeight mPrevNativeDimensions{}; // gfx_prev_native_dimensions; + uintptr_t mGfxFrameBuffer{}; + + unsigned int mMsaaLevel = 1; + bool mDroppedFrame{}; + float* mBufVbo; // 3 vertices in a triangle and 32 floats per vtx + size_t mBufVboLen{}; + size_t mBufVboNumTris{}; + GfxWindowBackend* mWapi = nullptr; + GfxRenderingAPI* mRapi = nullptr; + + uintptr_t mSegmentPointers[MAX_SEGMENT_POINTERS]{}; + + bool mFbActive{}; + bool mRendersToFb{}; // game_renders_to_framebuffer; + std::map::iterator mActiveFrameBuffer; + std::map mFrameBuffers; + + int mGameFb{}; // game_framebuffer; + int mGameFbMsaaResolved{}; // game_framebuffer_msaa_resolved; + + std::set> mGetPixelDepthPending; // get_pixel_depth_pending; + std::unordered_map, uint16_t, hash_pair_ff> mGetPixelDepthCached; // get_pixel_depth_cached; + std::map mMaskedTextures; + + const std::unordered_map* mCurMtxReplacements; + bool mMarkerOn; // This was originally a debug feature. Now it seems to control s2dex? + std::vector shader_ids; + int mInterpolationIndex; + int mInterpolationIndexTarget; +}; + +void gfx_set_target_ucode(UcodeHandlers ucode); +void gfx_push_current_dir(char* path); +int32_t gfx_check_image_signature(const char* imgData); +const char* GfxGetOpcodeName(int8_t opcode); + +} // namespace Fast + +extern "C" void gfx_texture_cache_clear(); +extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height, uint32_t native_width, uint32_t native_height, + uint8_t resize); diff --git a/libultraship/include/fast/lus_gbi.h b/libultraship/include/fast/lus_gbi.h new file mode 100644 index 000000000..4e8facd68 --- /dev/null +++ b/libultraship/include/fast/lus_gbi.h @@ -0,0 +1,1367 @@ +#pragma once +#include +namespace Fast { +#define OPCODE(x) (int8_t)(x) + +#include "f3dex.h" +#include "f3dex2.h" + +/* RDP commands: */ +constexpr int8_t RDP_G_SETCIMG = OPCODE(0xff); /* -1 */ +constexpr int8_t RDP_G_SETZIMG = OPCODE(0xfe); /* -2 */ +constexpr int8_t RDP_G_SETTIMG = OPCODE(0xfd); /* -3 */ +constexpr int8_t RDP_G_SETCOMBINE = OPCODE(0xfc); /* -4 */ +constexpr int8_t RDP_G_SETENVCOLOR = OPCODE(0xfb); /* -5 */ +constexpr int8_t RDP_G_SETPRIMCOLOR = OPCODE(0xfa); /* -6 */ +constexpr int8_t RDP_G_SETBLENDCOLOR = OPCODE(0xf9); /* -7 */ +constexpr int8_t RDP_G_SETFOGCOLOR = OPCODE(0xf8); /* -8 */ +constexpr int8_t RDP_G_SETFILLCOLOR = OPCODE(0xf7); /* -9 */ +constexpr int8_t RDP_G_FILLRECT = OPCODE(0xf6); /* -10 */ +constexpr int8_t RDP_G_SETTILE = OPCODE(0xf5); /* -11 */ +constexpr int8_t RDP_G_LOADTILE = OPCODE(0xf4); /* -12 */ +constexpr int8_t RDP_G_LOADBLOCK = OPCODE(0xf3); /* -13 */ +constexpr int8_t RDP_G_SETTILESIZE = OPCODE(0xf2); /* -14 */ +constexpr int8_t RDP_G_LOADTLUT = OPCODE(0xf0); /* -16 */ +constexpr int8_t RDP_G_RDPSETOTHERMODE = OPCODE(0xef); /* -17 */ +constexpr int8_t RDP_G_SETPRIMDEPTH = OPCODE(0xee); /* -18 */ +constexpr int8_t RDP_G_SETSCISSOR = OPCODE(0xed); /* -19 */ +constexpr int8_t RDP_G_SETCONVERT = OPCODE(0xec); /* -20 */ +constexpr int8_t RDP_G_SETKEYR = OPCODE(0xeb); /* -21 */ +constexpr int8_t RDP_G_SETKEYGB = OPCODE(0xea); /* -22 */ +constexpr int8_t RDP_G_RDPFULLSYNC = OPCODE(0xe9); /* -23 */ +constexpr int8_t RDP_G_RDPTILESYNC = OPCODE(0xe8); /* -24 */ +constexpr int8_t RDP_G_RDPPIPESYNC = OPCODE(0xe7); /* -25 */ +constexpr int8_t RDP_G_RDPLOADSYNC = OPCODE(0xe6); /* -26 */ +constexpr int8_t RDP_G_TEXRECTFLIP = OPCODE(0xe5); /* -27 */ +constexpr int8_t RDP_G_TEXRECT = OPCODE(0xe4); /* -28 */ + +// CUSTOM OTR COMMANDS +constexpr int8_t OTR_G_SETTIMG_OTR_HASH = OPCODE(0x20); +constexpr int8_t OTR_G_SETFB = OPCODE(0x21); +constexpr int8_t OTR_G_RESETFB = OPCODE(0x22); +constexpr int8_t OTR_G_SETTIMG_FB = OPCODE(0x23); +constexpr int8_t OTR_G_VTX_OTR_FILEPATH = OPCODE(0x24); +constexpr int8_t OTR_G_SETTIMG_OTR_FILEPATH = OPCODE(0x25); +constexpr int8_t OTR_G_TRI1_OTR = OPCODE(0x26); +constexpr int8_t OTR_G_DL_OTR_FILEPATH = OPCODE(0x27); +constexpr int8_t OTR_G_PUSHCD = OPCODE(0x28); +constexpr int8_t OTR_G_MTX_OTR_FILEPATH = OPCODE(0x29); +constexpr int8_t OTR_G_DL_OTR_HASH = OPCODE(0x31); +constexpr int8_t OTR_G_VTX_OTR_HASH = OPCODE(0x32); +constexpr int8_t OTR_G_MARKER = OPCODE(0x33); +constexpr int8_t OTR_G_INVALTEXCACHE = OPCODE(0x34); +constexpr int8_t OTR_G_BRANCH_Z_OTR = OPCODE(0x35); +constexpr int8_t OTR_G_MTX_OTR = OPCODE(0x36); +constexpr int8_t OTR_G_TEXRECT_WIDE = OPCODE(0x37); +constexpr int8_t OTR_G_FILLWIDERECT = OPCODE(0x38); + +/* GFX Effects */ + +// RDP Cmd +constexpr int8_t OTR_G_SETGRAYSCALE = OPCODE(0x39); +constexpr int8_t OTR_G_EXTRAGEOMETRYMODE = OPCODE(0x3a); +constexpr int8_t OTR_G_COPYFB = OPCODE(0x3b); +constexpr int8_t OTR_G_IMAGERECT = OPCODE(0x3c); +constexpr int8_t OTR_G_DL_INDEX = OPCODE(0x3d); +constexpr int8_t OTR_G_READFB = OPCODE(0x3e); +constexpr int8_t OTR_G_REGBLENDEDTEX = OPCODE(0x3f); +constexpr int8_t OTR_G_SETINTENSITY = OPCODE(0x40); +constexpr int8_t OTR_G_MOVEMEM_HASH = OPCODE(0x42); +constexpr int8_t OTR_G_LOAD_SHADER = OPCODE(0x43); +constexpr int8_t RDP_G_SETTILESIZE_INTERP = OPCODE(0x44); +constexpr int8_t RDP_G_SETTARGETINTERPINDEX = OPCODE(0x45); + +/* + * The following commands are the "generated" RDP commands; the user + * never sees them, the RSP microcode generates them. + * + * The layout of the bits is magical, to save work in the ucode. + * These id's are -56, -52, -54, -50, -55, -51, -53, -49, ... + * edge, shade, texture, zbuff bits: estz + */ +#define G_TRI_FILL 0xc8 /* fill triangle: 11001000 */ +#define G_TRI_SHADE 0xcc /* shade triangle: 11001100 */ +#define G_TRI_TXTR 0xca /* texture triangle: 11001010 */ +#define G_TRI_SHADE_TXTR 0xce /* shade, texture triangle: 11001110 */ +#define G_TRI_FILL_ZBUFF 0xc9 /* fill, zbuff triangle: 11001001 */ +#define G_TRI_SHADE_ZBUFF 0xcd /* shade, zbuff triangle: 11001101 */ +#define G_TRI_TXTR_ZBUFF 0xcb /* texture, zbuff triangle: 11001011 */ +#define G_TRI_SHADE_TXTR_ZBUFF 0xcf /* shade, txtr, zbuff trngl: 11001111 */ + +/* + * A TRI_FILL triangle is just the edges. You need to set the DP + * to use primcolor, in order to see anything. (it is NOT a triangle + * that gets rendered in 'fill mode'. Triangles can't be rendered + * in 'fill mode') + * + * A TRI_SHADE is a gouraud triangle that has colors interpolated. + * Flat-shaded triangles (from the software) are still gouraud shaded, + * it's just the colors are all the same and the deltas are 0. + * + * Other triangle types, and combinations are more obvious. + */ + +/* masks to build RDP triangle commands: */ +#define G_RDP_TRI_FILL_MASK 0x08 +#define G_RDP_TRI_SHADE_MASK 0x04 +#define G_RDP_TRI_TXTR_MASK 0x02 +#define G_RDP_TRI_ZBUFF_MASK 0x01 + +/* + * HACK: + * This is a dreadful hack. For version 1.0 hardware, there are still + * some 'bowtie' hangs. This parameter can be increased to avoid + * the hangs. Every increase of 4 chops one scanline off of every + * triangle. Values of 4,8,12 should be sufficient to avoid any + * bowtie hang. + * + * Change this value, then recompile ALL of your program (including static + * display lists!) + * + * THIS WILL BE REMOVED FOR HARDWARE VERSION 2.0! + */ +#define BOWTIE_VAL 0 + +/* gets added to RDP command, in order to test for addres fixup: */ +#define G_RDP_ADDR_FIXUP 3 /* |RDP cmds| <= this, do addr fixup */ +#ifdef _LANGUAGE_ASSEMBLY +#define G_RDP_TEXRECT_CHECK ((-1 * G_TEXRECTFLIP) & 0xff) +#endif + +/* macros for command parsing: */ +#define GDMACMD(x) (x) +#define GIMMCMD(x) = OPCODE(G_IMMFIRST - (x)) +#define GRDPCMD(x) (0xff - (x)) + +#define G_DMACMDSIZ 128 +#define G_IMMCMDSIZ 64 +#define G_RDPCMDSIZ 64 + +/* + * Coordinate shift values, number of bits of fraction + */ +#define G_TEXTURE_IMAGE_FRAC 2 +#define G_TEXTURE_SCALE_FRAC 16 +#define G_SCALE_FRAC 8 +#define G_ROTATE_FRAC 16 + +/* + * Parameters to graphics commands + */ + +/* + * Data packing macros + */ + +/* + * Maximum z-buffer value, used to initialize the z-buffer. + * Note : this number is NOT the viewport z-scale constant. + * See the comment next to G_MAXZ for more info. + */ +#define G_MAXFBZ 0x3fff /* 3b exp, 11b mantissa */ + +#define GPACK_RGBA5551(r, g, b, a) ((((r) << 8) & 0xf800) | (((g) << 3) & 0x7c0) | (((b) >> 2) & 0x3e) | ((a)&0x1)) +#define GPACK_ZDZ(z, dz) ((z) << 2 | (dz)) + +/* + * flags for G_SETGEOMETRYMODE + * (this rendering state is maintained in RSP) + * + * DO NOT USE THE LOW 8 BITS OF GEOMETRYMODE: + * The weird bit-ordering is for the micro-code: the lower byte + * can be OR'd in with G_TRI_SHADE (11001100) to construct + * the triangle command directly. Don't break it... + * + * DO NOT USE THE HIGH 8 BITS OF GEOMETRYMODE: + * The high byte is OR'd with 0x703 to form the clip code mask. + * If it is set to 0x04, this will cause near clipping to occur. + * If it is zero, near clipping will not occur. + * + * Further explanation: + * G_SHADE is necessary in order to see the color that you passed + * down with the vertex. If G_SHADE isn't set, you need to set the DP + * appropriately and use primcolor to see anything. + * + * G_SHADING_SMOOTH enabled means use all 3 colors of the triangle. + * If it is not set, then do 'flat shading', where only one vertex color + * is used (and all 3 vertices are set to that same color by the ucode) + * See the man page for gSP1Triangle(). + * + */ + +#define G_ZBUFFER 0x00000001 +#define G_SHADE 0x00000004 +#define G_FOG 0x00010000 +#define G_LIGHTING 0x00020000 +#define G_TEXTURE_GEN 0x00040000 +#define G_TEXTURE_GEN_LINEAR 0x00080000 +#define G_LOD 0x00100000 +#define G_LIGHTING_POSITIONAL 0x00400000 + +/* + * G_EXTRAGEOMETRY flags: set extra custom geometry modes + */ +#define G_EX_INVERT_CULLING 0x00000001 +#define G_EX_ALWAYS_EXECUTE_BRANCH 0x00000002 + +/* + * G_SETIMG fmt: set image formats + */ +#define G_IM_FMT_RGBA 0 +#define G_IM_FMT_YUV 1 +#define G_IM_FMT_CI 2 +#define G_IM_FMT_IA 3 +#define G_IM_FMT_I 4 + +/* + * G_SETIMG siz: set image pixel size + */ +#define G_IM_SIZ_4b 0 +#define G_IM_SIZ_8b 1 +#define G_IM_SIZ_16b 2 +#define G_IM_SIZ_32b 3 +#define G_IM_SIZ_DD 5 + +#define G_IM_SIZ_4b_BYTES 0 +#define G_IM_SIZ_4b_TILE_BYTES G_IM_SIZ_4b_BYTES +#define G_IM_SIZ_4b_LINE_BYTES G_IM_SIZ_4b_BYTES + +#define G_IM_SIZ_8b_BYTES 1 +#define G_IM_SIZ_8b_TILE_BYTES G_IM_SIZ_8b_BYTES +#define G_IM_SIZ_8b_LINE_BYTES G_IM_SIZ_8b_BYTES + +#define G_IM_SIZ_16b_BYTES 2 +#define G_IM_SIZ_16b_TILE_BYTES G_IM_SIZ_16b_BYTES +#define G_IM_SIZ_16b_LINE_BYTES G_IM_SIZ_16b_BYTES + +#define G_IM_SIZ_32b_BYTES 4 +#define G_IM_SIZ_32b_TILE_BYTES 2 +#define G_IM_SIZ_32b_LINE_BYTES 2 + +#define G_IM_SIZ_4b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_8b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_16b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_32b_LOAD_BLOCK G_IM_SIZ_32b + +#define G_IM_SIZ_4b_SHIFT 2 +#define G_IM_SIZ_8b_SHIFT 1 +#define G_IM_SIZ_16b_SHIFT 0 +#define G_IM_SIZ_32b_SHIFT 0 + +#define G_IM_SIZ_4b_INCR 3 +#define G_IM_SIZ_8b_INCR 1 +#define G_IM_SIZ_16b_INCR 0 +#define G_IM_SIZ_32b_INCR 0 + +/* + * Texturing macros + */ + +/* These are also defined defined above for Sprite Microcode */ + +#define G_TX_LOADTILE 7 +#define G_TX_RENDERTILE 0 + +#define G_TX_NOMIRROR 0 +#define G_TX_WRAP 0 +#define G_TX_MIRROR 0x1 +#define G_TX_CLAMP 0x2 +#define G_TX_NOMASK 0 +#define G_TX_NOLOD 0 + +/* + * G_SETCOMBINE: color combine modes + */ +/* Color combiner constants: */ +#define G_CCMUX_COMBINED 0 +#define G_CCMUX_TEXEL0 1 +#define G_CCMUX_TEXEL1 2 +#define G_CCMUX_PRIMITIVE 3 +#define G_CCMUX_SHADE 4 +#define G_CCMUX_ENVIRONMENT 5 +#define G_CCMUX_CENTER 6 +#define G_CCMUX_SCALE 6 +#define G_CCMUX_COMBINED_ALPHA 7 +#define G_CCMUX_TEXEL0_ALPHA 8 +#define G_CCMUX_TEXEL1_ALPHA 9 +#define G_CCMUX_PRIMITIVE_ALPHA 10 +#define G_CCMUX_SHADE_ALPHA 11 +#define G_CCMUX_ENV_ALPHA 12 +#define G_CCMUX_LOD_FRACTION 13 +#define G_CCMUX_PRIM_LOD_FRAC 14 +#define G_CCMUX_NOISE 7 +#define G_CCMUX_K4 7 +#define G_CCMUX_K5 15 +#define G_CCMUX_1 6 +#define G_CCMUX_0 31 + +/* Alpha combiner constants: */ +#define G_ACMUX_COMBINED 0 +#define G_ACMUX_TEXEL0 1 +#define G_ACMUX_TEXEL1 2 +#define G_ACMUX_PRIMITIVE 3 +#define G_ACMUX_SHADE 4 +#define G_ACMUX_ENVIRONMENT 5 +#define G_ACMUX_LOD_FRACTION 0 +#define G_ACMUX_PRIM_LOD_FRAC 6 +#define G_ACMUX_1 6 +#define G_ACMUX_0 7 + +/* typical CC cycle 1 modes */ +#define G_CC_PRIMITIVE 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE +#define G_CC_SHADE 0, 0, 0, SHADE, 0, 0, 0, SHADE +#define G_CC_MODULATEI TEXEL0, 0, SHADE, 0, 0, 0, 0, SHADE +#define G_CC_MODULATEIA TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0 +#define G_CC_MODULATEIDECALA TEXEL0, 0, SHADE, 0, 0, 0, 0, TEXEL0 +#define G_CC_MODULATERGB G_CC_MODULATEI +#define G_CC_MODULATERGBA G_CC_MODULATEIA +#define G_CC_MODULATERGBDECALA G_CC_MODULATEIDECALA +#define G_CC_MODULATEI_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0 +#define G_CC_MODULATEIDECALA_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, TEXEL0 +#define G_CC_MODULATERGB_PRIM G_CC_MODULATEI_PRIM +#define G_CC_MODULATERGBA_PRIM G_CC_MODULATEIA_PRIM +#define G_CC_MODULATERGBDECALA_PRIM G_CC_MODULATEIDECALA_PRIM +#define G_CC_DECALRGB 0, 0, 0, TEXEL0, 0, 0, 0, SHADE +#define G_CC_DECALRGBA 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0 +#define G_CC_BLENDI ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDIA ENVIRONMENT, SHADE, TEXEL0, SHADE, TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDIDECALA ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_BLENDRGBA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDRGBDECALA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_ADDRGB 1, 0, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_ADDRGBDECALA 1, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_REFLECTRGB ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_REFLECTRGBDECALA ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_HILITERGB PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_HILITERGBA PRIMITIVE, SHADE, TEXEL0, SHADE, PRIMITIVE, SHADE, TEXEL0, SHADE +#define G_CC_HILITERGBDECALA PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_SHADEDECALA 0, 0, 0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_BLENDPE PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDPEDECALA PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, TEXEL0 + +/* oddball modes */ +#define _G_CC_BLENDPE ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, SHADE, 0 +#define _G_CC_BLENDPEDECALA ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, 0, 0, 0, TEXEL0 +#define _G_CC_TWOCOLORTEX PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +/* used for 1-cycle sparse mip-maps, primitive color has color of lowest LOD */ +#define _G_CC_SPARSEST PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0, PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0 +#define G_CC_TEMPLERP TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0 + +/* typical CC cycle 1 modes, usually followed by other cycle 2 modes */ +#define G_CC_TRILERP TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0, TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0 +#define G_CC_INTERFERENCE TEXEL0, 0, TEXEL1, 0, TEXEL0, 0, TEXEL1, 0 + +/* + * One-cycle color convert operation + */ +#define G_CC_1CYUV2RGB TEXEL0, K4, K5, TEXEL0, 0, 0, 0, SHADE + +/* + * NOTE: YUV2RGB expects TF step1 color conversion to occur in 2nd clock. + * Therefore, CC looks for step1 results in TEXEL1 + */ +#define G_CC_YUV2RGB TEXEL1, K4, K5, TEXEL1, 0, 0, 0, 0 + +/* typical CC cycle 2 modes */ +#define G_CC_PASS2 0, 0, 0, COMBINED, 0, 0, 0, COMBINED +#define G_CC_MODULATEI2 COMBINED, 0, SHADE, 0, 0, 0, 0, SHADE +#define G_CC_MODULATEIA2 COMBINED, 0, SHADE, 0, COMBINED, 0, SHADE, 0 +#define G_CC_MODULATERGB2 G_CC_MODULATEI2 +#define G_CC_MODULATERGBA2 G_CC_MODULATEIA2 +#define G_CC_MODULATEI_PRIM2 COMBINED, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM2 COMBINED, 0, PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0 +#define G_CC_MODULATERGB_PRIM2 G_CC_MODULATEI_PRIM2 +#define G_CC_MODULATERGBA_PRIM2 G_CC_MODULATEIA_PRIM2 +#define G_CC_DECALRGB2 0, 0, 0, COMBINED, 0, 0, 0, SHADE +/* + * ? +#define G_CC_DECALRGBA2 COMBINED, SHADE, COMBINED_ALPHA, SHADE, 0, 0, 0, SHADE +*/ +#define G_CC_BLENDI2 ENVIRONMENT, SHADE, COMBINED, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDIA2 ENVIRONMENT, SHADE, COMBINED, SHADE, COMBINED, 0, SHADE, 0 +#define G_CC_CHROMA_KEY2 TEXEL0, CENTER, SCALE, 0, 0, 0, 0, 0 +#define G_CC_HILITERGB2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, SHADE +#define G_CC_HILITERGBA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, ENVIRONMENT, COMBINED, TEXEL0, COMBINED +#define G_CC_HILITERGBDECALA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, TEXEL0 +#define G_CC_HILITERGBPASSA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, COMBINED + +/* + * G_SETOTHERMODE_L sft: shift count + */ +#define G_MDSFT_ALPHACOMPARE 0 +#define G_MDSFT_ZSRCSEL 2 +#define G_MDSFT_RENDERMODE 3 +#define G_MDSFT_BLENDER 16 + +/* + * G_SETOTHERMODE_H sft: shift count + */ +#define G_MDSFT_BLENDMASK 0 /* unsupported */ +#define G_MDSFT_ALPHADITHER 4 +#define G_MDSFT_RGBDITHER 6 + +#define G_MDSFT_COMBKEY 8 +#define G_MDSFT_TEXTCONV 9 +#define G_MDSFT_TEXTFILT 12 +#define G_MDSFT_TEXTLUT 14 +#define G_MDSFT_TEXTLOD 16 +#define G_MDSFT_TEXTDETAIL 17 +#define G_MDSFT_TEXTPERSP 19 +#define G_MDSFT_CYCLETYPE 20 +#define G_MDSFT_COLORDITHER 22 /* unsupported in HW 2.0 */ +#define G_MDSFT_PIPELINE 23 + +/* G_SETOTHERMODE_H gPipelineMode */ +#define G_PM_1PRIMITIVE (1 << G_MDSFT_PIPELINE) +#define G_PM_NPRIMITIVE (0 << G_MDSFT_PIPELINE) + +/* G_SETOTHERMODE_H gSetCycleType */ +#define G_CYC_1CYCLE (0 << G_MDSFT_CYCLETYPE) +#define G_CYC_2CYCLE (1 << G_MDSFT_CYCLETYPE) +#define G_CYC_COPY (2 << G_MDSFT_CYCLETYPE) +#define G_CYC_FILL (3 << G_MDSFT_CYCLETYPE) + +/* G_SETOTHERMODE_H gSetTexturePersp */ +#define G_TP_NONE (0 << G_MDSFT_TEXTPERSP) +#define G_TP_PERSP (1 << G_MDSFT_TEXTPERSP) + +/* G_SETOTHERMODE_H gSetTextureDetail */ +#define G_TD_CLAMP (0 << G_MDSFT_TEXTDETAIL) +#define G_TD_SHARPEN (1 << G_MDSFT_TEXTDETAIL) +#define G_TD_DETAIL (2 << G_MDSFT_TEXTDETAIL) + +/* G_SETOTHERMODE_H gSetTextureLOD */ +#define G_TL_TILE (0 << G_MDSFT_TEXTLOD) +#define G_TL_LOD (1 << G_MDSFT_TEXTLOD) + +/* G_SETOTHERMODE_H gSetTextureLUT */ +#define G_TT_NONE (0 << G_MDSFT_TEXTLUT) +#define G_TT_RGBA16 (2 << G_MDSFT_TEXTLUT) +#define G_TT_IA16 (3 << G_MDSFT_TEXTLUT) + +/* G_SETOTHERMODE_H gSetTextureFilter */ +#define G_TF_POINT (0 << G_MDSFT_TEXTFILT) +#define G_TF_AVERAGE (3 << G_MDSFT_TEXTFILT) +#define G_TF_BILERP (2 << G_MDSFT_TEXTFILT) + +/* G_SETOTHERMODE_H gSetTextureConvert */ +#define G_TC_CONV (0 << G_MDSFT_TEXTCONV) +#define G_TC_FILTCONV (5 << G_MDSFT_TEXTCONV) +#define G_TC_FILT (6 << G_MDSFT_TEXTCONV) + +/* G_SETOTHERMODE_H gSetCombineKey */ +#define G_CK_NONE (0 << G_MDSFT_COMBKEY) +#define G_CK_KEY (1 << G_MDSFT_COMBKEY) + +/* G_SETOTHERMODE_H gSetColorDither */ +#define G_CD_MAGICSQ (0 << G_MDSFT_RGBDITHER) +#define G_CD_BAYER (1 << G_MDSFT_RGBDITHER) +#define G_CD_NOISE (2 << G_MDSFT_RGBDITHER) + +#ifndef _HW_VERSION_1 +#define G_CD_DISABLE (3 << G_MDSFT_RGBDITHER) +#define G_CD_ENABLE G_CD_NOISE /* HW 1.0 compatibility mode */ +#else +#define G_CD_ENABLE (1 << G_MDSFT_COLORDITHER) +#define G_CD_DISABLE (0 << G_MDSFT_COLORDITHER) +#endif + +/* G_SETOTHERMODE_H gSetAlphaDither */ +#define G_AD_PATTERN (0 << G_MDSFT_ALPHADITHER) +#define G_AD_NOTPATTERN (1 << G_MDSFT_ALPHADITHER) +#define G_AD_NOISE (2 << G_MDSFT_ALPHADITHER) +#define G_AD_DISABLE (3 << G_MDSFT_ALPHADITHER) + +/* G_SETOTHERMODE_L gSetAlphaCompare */ +#define G_AC_NONE (0 << G_MDSFT_ALPHACOMPARE) +#define G_AC_THRESHOLD (1 << G_MDSFT_ALPHACOMPARE) +#define G_AC_DITHER (3 << G_MDSFT_ALPHACOMPARE) + +/* G_SETOTHERMODE_L gSetDepthSource */ +#define G_ZS_PIXEL (0 << G_MDSFT_ZSRCSEL) +#define G_ZS_PRIM (1 << G_MDSFT_ZSRCSEL) + +/* G_SETOTHERMODE_L gSetRenderMode */ +#define AA_EN 0x8 +#define Z_CMP 0x10 +#define Z_UPD 0x20 +#define IM_RD 0x40 +#define CLR_ON_CVG 0x80 +#define CVG_DST_CLAMP 0 +#define CVG_DST_WRAP 0x100 +#define CVG_DST_FULL 0x200 +#define CVG_DST_SAVE 0x300 +#define ZMODE_OPA 0 +#define ZMODE_INTER 0x400 +#define ZMODE_XLU 0x800 +#define ZMODE_DEC 0xc00 +#define CVG_X_ALPHA 0x1000 +#define ALPHA_CVG_SEL 0x2000 +#define FORCE_BL 0x4000 +#define TEX_EDGE 0x0000 /* used to be 0x8000 */ + +#define G_BL_CLR_IN 0 +#define G_BL_CLR_MEM 1 +#define G_BL_CLR_BL 2 +#define G_BL_CLR_FOG 3 +#define G_BL_1MA 0 +#define G_BL_A_MEM 1 +#define G_BL_A_IN 0 +#define G_BL_A_FOG 1 +#define G_BL_A_SHADE 2 +#define G_BL_1 2 +#define G_BL_0 3 + +#define GBL_c1(m1a, m1b, m2a, m2b) (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18 +#define GBL_c2(m1a, m1b, m2a, m2b) (m1a) << 28 | (m1b) << 24 | (m2a) << 20 | (m2b) << 16 + +#define RM_AA_ZB_OPA_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_SURF(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_DECAL(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ALPHA_CVG_SEL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_DECAL(clk) \ + AA_EN | Z_CMP | CVG_DST_WRAP | ALPHA_CVG_SEL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_DECAL(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ALPHA_CVG_SEL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ALPHA_CVG_SEL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_INTER(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_XLU_LINE(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_DEC_LINE(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_SAVE | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_TEX_EDGE(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_TEX_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_INTER | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_SUB_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_PCL_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | G_AC_DITHER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_TEX_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_SUB_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_OPA_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_OPA_SURF(clk) \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_XLU_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_XLU_LINE(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_DEC_LINE(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_TEX_EDGE(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_SUB_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_PCL_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_OPA_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_TEX_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_SUB_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OPA_SURF(clk) \ + Z_CMP | Z_UPD | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_ZB_XLU_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_XLU | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OPA_DECAL(clk) \ + Z_CMP | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_ZB_XLU_DECAL(clk) \ + Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_CLD_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_XLU | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OVL_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_PCL_SURF(clk) \ + Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_OPA_SURF(clk) CVG_DST_CLAMP | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_XLU_SURF(clk) \ + IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_TEX_EDGE(clk) \ + CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | TEX_EDGE | AA_EN | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_CLD_SURF(clk) \ + IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_PCL_SURF(clk) \ + CVG_DST_FULL | FORCE_BL | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_ADD(clk) \ + IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1) + +#define RM_NOOP(clk) GBL_c##clk(0, 0, 0, 0) + +#define RM_VISCVG(clk) IM_RD | FORCE_BL | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_BL, G_BL_A_MEM) + +/* for rendering to an 8-bit framebuffer */ +#define RM_OPA_CI(clk) CVG_DST_CLAMP | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define G_RM_AA_ZB_OPA_SURF RM_AA_ZB_OPA_SURF(1) +#define G_RM_AA_ZB_OPA_SURF2 RM_AA_ZB_OPA_SURF(2) +#define G_RM_AA_ZB_XLU_SURF RM_AA_ZB_XLU_SURF(1) +#define G_RM_AA_ZB_XLU_SURF2 RM_AA_ZB_XLU_SURF(2) +#define G_RM_AA_ZB_OPA_DECAL RM_AA_ZB_OPA_DECAL(1) +#define G_RM_AA_ZB_OPA_DECAL2 RM_AA_ZB_OPA_DECAL(2) +#define G_RM_AA_ZB_XLU_DECAL RM_AA_ZB_XLU_DECAL(1) +#define G_RM_AA_ZB_XLU_DECAL2 RM_AA_ZB_XLU_DECAL(2) +#define G_RM_AA_ZB_OPA_INTER RM_AA_ZB_OPA_INTER(1) +#define G_RM_AA_ZB_OPA_INTER2 RM_AA_ZB_OPA_INTER(2) +#define G_RM_AA_ZB_XLU_INTER RM_AA_ZB_XLU_INTER(1) +#define G_RM_AA_ZB_XLU_INTER2 RM_AA_ZB_XLU_INTER(2) +#define G_RM_AA_ZB_XLU_LINE RM_AA_ZB_XLU_LINE(1) +#define G_RM_AA_ZB_XLU_LINE2 RM_AA_ZB_XLU_LINE(2) +#define G_RM_AA_ZB_DEC_LINE RM_AA_ZB_DEC_LINE(1) +#define G_RM_AA_ZB_DEC_LINE2 RM_AA_ZB_DEC_LINE(2) +#define G_RM_AA_ZB_TEX_EDGE RM_AA_ZB_TEX_EDGE(1) +#define G_RM_AA_ZB_TEX_EDGE2 RM_AA_ZB_TEX_EDGE(2) +#define G_RM_AA_ZB_TEX_INTER RM_AA_ZB_TEX_INTER(1) +#define G_RM_AA_ZB_TEX_INTER2 RM_AA_ZB_TEX_INTER(2) +#define G_RM_AA_ZB_SUB_SURF RM_AA_ZB_SUB_SURF(1) +#define G_RM_AA_ZB_SUB_SURF2 RM_AA_ZB_SUB_SURF(2) +#define G_RM_AA_ZB_PCL_SURF RM_AA_ZB_PCL_SURF(1) +#define G_RM_AA_ZB_PCL_SURF2 RM_AA_ZB_PCL_SURF(2) +#define G_RM_AA_ZB_OPA_TERR RM_AA_ZB_OPA_TERR(1) +#define G_RM_AA_ZB_OPA_TERR2 RM_AA_ZB_OPA_TERR(2) +#define G_RM_AA_ZB_TEX_TERR RM_AA_ZB_TEX_TERR(1) +#define G_RM_AA_ZB_TEX_TERR2 RM_AA_ZB_TEX_TERR(2) +#define G_RM_AA_ZB_SUB_TERR RM_AA_ZB_SUB_TERR(1) +#define G_RM_AA_ZB_SUB_TERR2 RM_AA_ZB_SUB_TERR(2) + +#define G_RM_RA_ZB_OPA_SURF RM_RA_ZB_OPA_SURF(1) +#define G_RM_RA_ZB_OPA_SURF2 RM_RA_ZB_OPA_SURF(2) +#define G_RM_RA_ZB_OPA_DECAL RM_RA_ZB_OPA_DECAL(1) +#define G_RM_RA_ZB_OPA_DECAL2 RM_RA_ZB_OPA_DECAL(2) +#define G_RM_RA_ZB_OPA_INTER RM_RA_ZB_OPA_INTER(1) +#define G_RM_RA_ZB_OPA_INTER2 RM_RA_ZB_OPA_INTER(2) + +#define G_RM_AA_OPA_SURF RM_AA_OPA_SURF(1) +#define G_RM_AA_OPA_SURF2 RM_AA_OPA_SURF(2) +#define G_RM_AA_XLU_SURF RM_AA_XLU_SURF(1) +#define G_RM_AA_XLU_SURF2 RM_AA_XLU_SURF(2) +#define G_RM_AA_XLU_LINE RM_AA_XLU_LINE(1) +#define G_RM_AA_XLU_LINE2 RM_AA_XLU_LINE(2) +#define G_RM_AA_DEC_LINE RM_AA_DEC_LINE(1) +#define G_RM_AA_DEC_LINE2 RM_AA_DEC_LINE(2) +#define G_RM_AA_TEX_EDGE RM_AA_TEX_EDGE(1) +#define G_RM_AA_TEX_EDGE2 RM_AA_TEX_EDGE(2) +#define G_RM_AA_SUB_SURF RM_AA_SUB_SURF(1) +#define G_RM_AA_SUB_SURF2 RM_AA_SUB_SURF(2) +#define G_RM_AA_PCL_SURF RM_AA_PCL_SURF(1) +#define G_RM_AA_PCL_SURF2 RM_AA_PCL_SURF(2) +#define G_RM_AA_OPA_TERR RM_AA_OPA_TERR(1) +#define G_RM_AA_OPA_TERR2 RM_AA_OPA_TERR(2) +#define G_RM_AA_TEX_TERR RM_AA_TEX_TERR(1) +#define G_RM_AA_TEX_TERR2 RM_AA_TEX_TERR(2) +#define G_RM_AA_SUB_TERR RM_AA_SUB_TERR(1) +#define G_RM_AA_SUB_TERR2 RM_AA_SUB_TERR(2) + +#define G_RM_RA_OPA_SURF RM_RA_OPA_SURF(1) +#define G_RM_RA_OPA_SURF2 RM_RA_OPA_SURF(2) + +#define G_RM_ZB_OPA_SURF RM_ZB_OPA_SURF(1) +#define G_RM_ZB_OPA_SURF2 RM_ZB_OPA_SURF(2) +#define G_RM_ZB_XLU_SURF RM_ZB_XLU_SURF(1) +#define G_RM_ZB_XLU_SURF2 RM_ZB_XLU_SURF(2) +#define G_RM_ZB_OPA_DECAL RM_ZB_OPA_DECAL(1) +#define G_RM_ZB_OPA_DECAL2 RM_ZB_OPA_DECAL(2) +#define G_RM_ZB_XLU_DECAL RM_ZB_XLU_DECAL(1) +#define G_RM_ZB_XLU_DECAL2 RM_ZB_XLU_DECAL(2) +#define G_RM_ZB_CLD_SURF RM_ZB_CLD_SURF(1) +#define G_RM_ZB_CLD_SURF2 RM_ZB_CLD_SURF(2) +#define G_RM_ZB_OVL_SURF RM_ZB_OVL_SURF(1) +#define G_RM_ZB_OVL_SURF2 RM_ZB_OVL_SURF(2) +#define G_RM_ZB_PCL_SURF RM_ZB_PCL_SURF(1) +#define G_RM_ZB_PCL_SURF2 RM_ZB_PCL_SURF(2) + +#define G_RM_OPA_SURF RM_OPA_SURF(1) +#define G_RM_OPA_SURF2 RM_OPA_SURF(2) +#define G_RM_XLU_SURF RM_XLU_SURF(1) +#define G_RM_XLU_SURF2 RM_XLU_SURF(2) +#define G_RM_CLD_SURF RM_CLD_SURF(1) +#define G_RM_CLD_SURF2 RM_CLD_SURF(2) +#define G_RM_TEX_EDGE RM_TEX_EDGE(1) +#define G_RM_TEX_EDGE2 RM_TEX_EDGE(2) +#define G_RM_PCL_SURF RM_PCL_SURF(1) +#define G_RM_PCL_SURF2 RM_PCL_SURF(2) +#define G_RM_ADD RM_ADD(1) +#define G_RM_ADD2 RM_ADD(2) +#define G_RM_NOOP RM_NOOP(1) +#define G_RM_NOOP2 RM_NOOP(2) +#define G_RM_VISCVG RM_VISCVG(1) +#define G_RM_VISCVG2 RM_VISCVG(2) +#define G_RM_OPA_CI RM_OPA_CI(1) +#define G_RM_OPA_CI2 RM_OPA_CI(2) + +#define G_RM_FOG_SHADE_A GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA) +#define G_RM_FOG_PRIM_A GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_IN, G_BL_1MA) +#define G_RM_PASS GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +/* + * G_SETCONVERT: K0-5 + */ +#define G_CV_K0 175 +#define G_CV_K1 -43 +#define G_CV_K2 -89 +#define G_CV_K3 222 +#define G_CV_K4 114 +#define G_CV_K5 42 + +/* + * G_SETSCISSOR: interlace mode + */ +#define G_SC_NON_INTERLACE 0 +#define G_SC_ODD_INTERLACE 3 +#define G_SC_EVEN_INTERLACE 2 + +/* flags to inhibit pushing of the display list (on branch) */ +#define G_DL_PUSH 0x00 +#define G_DL_NOPUSH 0x01 + +#if defined(_MSC_VER) || defined(__GNUC__) +#define _LANGUAGE_C +#endif + +/* + * BEGIN C-specific section: (typedef's) + */ +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/* + * Data Structures + * + * NOTE: + * The DMA transfer hardware requires 64-bit aligned, 64-bit multiple- + * sized transfers. This important hardware optimization is unfortunately + * reflected in the programming interface, with some structures + * padded and alignment enforced. + * + * Since structures are aligned to the boundary of the "worst-case" + * element, we can't depend on the C compiler to align things + * properly. + * + * 64-bit structure alignment is enforced by wrapping structures with + * unions that contain a dummy "long long int". Why this works is + * explained in the ANSI C Spec, or on page 186 of the second edition + * of K&R, "The C Programming Language". + * + * The price we pay for this is a little awkwardness referencing the + * structures through the union. There is no memory penalty, since + * all the structures are at least 64-bits the dummy alignment field + * does not increase the size of the union. + * + * Static initialization of these union structures works because + * the ANSI C spec states that static initialization for unions + * works by using the first union element. We put the dummy alignment + * field last for this reason. + * + * (it's possible a newer 64-bit compiler from MIPS might make this + * easier with a flag, but we can't wait for it...) + * + */ + +/* + * Vertex (set up for use with colors) + */ +typedef struct { +#ifndef GBI_FLOATS + short ob[3]; /* x, y, z */ +#else + float ob[3]; /* x, y, z */ +#endif + unsigned short flag; + short tc[2]; /* texture coord */ + unsigned char cn[4]; /* color & alpha */ +} F3DVtx_t; + +/* + * Vertex (set up for use with normals) + */ +typedef struct { +#ifndef GBI_FLOATS + short ob[3]; /* x, y, z */ +#else + float ob[3]; /* x, y, z */ +#endif + unsigned short flag; + short tc[2]; /* texture coord */ + signed char n[3]; /* normal */ + unsigned char a; /* alpha */ +} F3DVtx_tn; + +typedef union F3DVtx { + F3DVtx_t v; /* Use this one for colors */ + F3DVtx_tn n; /* Use this one for normals */ + long long int force_structure_alignment; +} F3DVtx; + +/* + * Sprite structure + */ + +typedef struct F3DuSprite_t { + void* SourceImagePointer; + void* TlutPointer; + short Stride; + short SubImageWidth; + short SubImageHeight; + char SourceImageType; + char SourceImageBitSize; + short SourceImageOffsetS; + short SourceImageOffsetT; + /* 20 bytes for above */ + + /* padding to bring structure size to 64 bit allignment */ + char dummy[4]; + +} F3DuSprite_t; + +typedef union { + F3DuSprite_t s; + + /* Need to make sure this is 64 bit aligned */ + long long int force_structure_allignment[3]; +} F3DuSprite; + +/* + * Triangle face + */ +typedef struct { + unsigned char flag; + unsigned char v[3]; +} F3DTri; + +/* + * Viewport + */ + +/* + * + * This magic value is the maximum INTEGER z-range of the hardware + * (there are also 16-bits of fraction, which are introduced during + * any transformations). This is not just a good idea, it's the law. + * Feeding the hardware eventual z-coordinates (after any transforms + * or scaling) bigger than this, will not work. + * + * This number is DIFFERENT than G_MAXFBZ, which is the maximum value + * you want to use to initialize the z-buffer. + * + * The reason these are different is mildly interesting, but too long + * to explain here. It is basically the result of optimizations in the + * hardware. A more generic API might hide this detail from the users, + * but we don't have the ucode to do that... + * + */ +#define G_MAXZ 0x03ff /* 10 bits of integer screen-Z precision */ + +/* + * The viewport structure elements have 2 bits of fraction, necessary + * to accomodate the sub-pixel positioning scaling for the hardware. + * This can also be exploited to handle odd-sized viewports. + * + * Accounting for these fractional bits, using the default projection + * and viewing matrices, the viewport structure is initialized thusly: + * + * (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, G_MAXZ, 0, + * (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, 0, 0, + */ +typedef struct { + short vscale[4]; /* scale, 2 bits fraction */ + short vtrans[4]; /* translate, 2 bits fraction */ + /* both the above arrays are padded to 64-bit boundary */ +} F3DVp_t; + +typedef union { + F3DVp_t vp; + long long int force_structure_alignment; +} F3DVp; + +/* + * MOVEWORD indices + * + * Each of these indexes an entry in a dmem table + * which points to a word in dmem in dmem where + * an immediate word will be stored. + * + */ +#define G_MW_MATRIX 0x00 /* NOTE: also used by movemem */ +#define G_MW_NUMLIGHT 0x02 +#define G_MW_CLIP 0x04 +#define G_MW_SEGMENT 0x06 +#define G_MW_SEGMENT_INTERP 0x07 +#define G_MW_FOG 0x08 +#define G_MW_LIGHTCOL 0x0a +#define G_MW_PERSPNORM 0x0e + +/* + * These are offsets from the address in the dmem table + */ +#define G_MWO_NUMLIGHT 0x00 +#define G_MWO_CLIP_RNX 0x04 +#define G_MWO_CLIP_RNY 0x0c +#define G_MWO_CLIP_RPX 0x14 +#define G_MWO_CLIP_RPY 0x1c +#define G_MWO_SEGMENT_0 0x00 +#define G_MWO_SEGMENT_1 0x01 +#define G_MWO_SEGMENT_2 0x02 +#define G_MWO_SEGMENT_3 0x03 +#define G_MWO_SEGMENT_4 0x04 +#define G_MWO_SEGMENT_5 0x05 +#define G_MWO_SEGMENT_6 0x06 +#define G_MWO_SEGMENT_7 0x07 +#define G_MWO_SEGMENT_8 0x08 +#define G_MWO_SEGMENT_9 0x09 +#define G_MWO_SEGMENT_A 0x0a +#define G_MWO_SEGMENT_B 0x0b +#define G_MWO_SEGMENT_C 0x0c +#define G_MWO_SEGMENT_D 0x0d +#define G_MWO_SEGMENT_E 0x0e +#define G_MWO_SEGMENT_F 0x0f +#define G_MWO_FOG 0x00 +#define G_MWO_aLIGHT_1 0x00 +#define G_MWO_bLIGHT_1 0x04 +#define G_MWO_MATRIX_XX_XY_I 0x00 +#define G_MWO_MATRIX_XZ_XW_I 0x04 +#define G_MWO_MATRIX_YX_YY_I 0x08 +#define G_MWO_MATRIX_YZ_YW_I 0x0c +#define G_MWO_MATRIX_ZX_ZY_I 0x10 +#define G_MWO_MATRIX_ZZ_ZW_I 0x14 +#define G_MWO_MATRIX_WX_WY_I 0x18 +#define G_MWO_MATRIX_WZ_WW_I 0x1c +#define G_MWO_MATRIX_XX_XY_F 0x20 +#define G_MWO_MATRIX_XZ_XW_F 0x24 +#define G_MWO_MATRIX_YX_YY_F 0x28 +#define G_MWO_MATRIX_YZ_YW_F 0x2c +#define G_MWO_MATRIX_ZX_ZY_F 0x30 +#define G_MWO_MATRIX_ZZ_ZW_F 0x34 +#define G_MWO_MATRIX_WX_WY_F 0x38 +#define G_MWO_MATRIX_WZ_WW_F 0x3c +#define G_MWO_POINT_RGBA 0x10 +#define G_MWO_POINT_ST 0x14 +#define G_MWO_POINT_XYSCREEN 0x18 +#define G_MWO_POINT_ZSCREEN 0x1c + +/* + * Light structure. + * + * Note: only directional (infinite) lights are currently supported. + * + * Note: the weird order is for the DMEM alignment benefit of + * the microcode. + * + */ + +typedef struct { + unsigned char col[3]; /* diffuse light value (rgba) */ + char pad1; + unsigned char colc[3]; /* copy of diffuse light value (rgba) */ + char pad2; + signed char dir[3]; /* direction of light (normalized) */ + char pad3; +} F3DLight_t; + +typedef struct { + unsigned char col[3]; + unsigned char unk3; + unsigned char colc[3]; + unsigned char unk7; + short pos[3]; + unsigned char unkE; +} F3DPointLight_t; + +typedef struct { + unsigned char col[3]; /* ambient light value (rgba) */ + char pad1; + unsigned char colc[3]; /* copy of ambient light value (rgba) */ + char pad2; +} F3DAmbient_t; + +typedef struct { + int x1, y1, x2, y2; /* texture offsets for highlight 1/2 */ +} F3DHilite_t; + +typedef union { + F3DLight_t l; + F3DPointLight_t p; + long long int force_structure_alignment[2]; +} F3DLight; + +typedef union { + F3DAmbient_t l; + long long int force_structure_alignment[1]; +} F3DAmbient; + +typedef struct { + F3DAmbient a; + F3DLight l[7]; +} F3DLightsn; + +typedef struct { + F3DAmbient a; + F3DLight l[1]; +} F3DLights0; + +typedef struct { + F3DAmbient a; + F3DLight l[1]; +} F3DLights1; + +typedef struct { + F3DAmbient a; + F3DLight l[2]; +} F3DLights2; + +typedef struct { + F3DAmbient a; + F3DLight l[3]; +} F3DLights3; + +typedef struct { + F3DAmbient a; + F3DLight l[4]; +} F3DLights4; + +typedef struct { + F3DAmbient a; + F3DLight l[5]; +} F3DLights5; + +typedef struct { + F3DAmbient a; + F3DLight l[6]; +} F3DLights6; + +typedef struct { + F3DAmbient a; + F3DLight l[7]; +} F3DLights7; + +typedef struct { + F3DLight l[2]; +} F3DLookAt; + +typedef union { + F3DHilite_t h; + long int force_structure_alignment[4]; +} F3DHilite; + +#ifdef USE_GBI_TRACE +/* + * Trace structure + */ +typedef struct { + const char* file; + int idx; + bool valid; +} F3DTrace; +#endif + +/* + * Generic Gfx Packet + */ +typedef struct { + uintptr_t w0; + uintptr_t w1; +#ifdef USE_GBI_TRACE + F3DTrace trace; +#endif +} F3DGwords; + +#ifdef __cplusplus +#ifdef USE_GBI_TRACE +static_assert(sizeof(F3DGwords) == 2 * sizeof(void*) + sizeof(F3DTrace), "Display list size is bad"); +#else +static_assert(sizeof(F3DGwords) == 2 * sizeof(void*), "Display list size is bad"); +#endif +#endif + +/* + * This union is the fundamental type of the display list. + * It is, by law, exactly 64 bits in size. + */ +typedef union F3DGfx { + F3DGwords words; + long long int force_structure_alignment; +} F3DGfx; + +/*===========================================================================* + * GBI Commands for S2DEX microcode + *===========================================================================*/ +/* GBI Header */ +#define G_BGLT_LOADBLOCK 0x0033 +#define G_BGLT_LOADTILE 0xfff4 + +#define G_BG_FLAG_FLIPS 0x01 +#define G_BG_FLAG_FLIPT 0x10 + +/* Non scalable background plane */ +typedef struct { + unsigned short imageX; /* x-coordinate of upper-left position of texture (u10.5) */ + unsigned short imageW; /* width of the texture (u10.2) */ + short frameX; /* upper-left position of transferred frame (s10.2) */ + unsigned short frameW; /* width of transferred frame (u10.2) */ + + unsigned short imageY; /* y-coordinate of upper-left position of texture (u10.5) */ + unsigned short imageH; /* height of the texture (u10.2) */ + short frameY; /* upper-left position of transferred frame (s10.2) */ + unsigned short frameH; /* height of transferred frame (u10.2) */ + + unsigned long long int* imagePtr; /* texture source address on DRAM */ + unsigned short imageLoad; /* which to use, LoadBlock or LoadTile */ + unsigned char imageFmt; /* format of texel - G_IM_FMT_* */ + unsigned char imageSiz; /* size of texel - G_IM_SIZ_* */ + unsigned short imagePal; /* pallet number */ + unsigned short imageFlip; /* right & left image inversion (Inverted by G_BG_FLAG_FLIPS) */ + + /* The following is set in the initialization routine guS2DInitBg(). There is no need for the user to set it. */ + unsigned short tmemW; /* TMEM width and Word size of frame 1 line. + At LoadBlock, GS_PIX2TMEM(imageW/4,imageSiz) + At LoadTile GS_PIX2TMEM(frameW/4,imageSiz)+1 */ + unsigned short tmemH; /* height of TMEM loadable at a time (s13.2) 4 times value + When the normal texture, 512/tmemW*4 + When the CI texture, 256/tmemW*4 */ + unsigned short tmemLoadSH; /* SH value + At LoadBlock, tmemSize/2-1 + At LoadTile, tmemW*16-1 */ + unsigned short tmemLoadTH; /* TH value or Stride value + At LoadBlock, GS_CALC_DXT(tmemW) + At LoadTile, tmemH-1 */ + unsigned short tmemSizeW; /* skip value of imagePtr for image 1-line + At LoadBlock, tmemW*2 + At LoadTile, GS_PIX2TMEM(imageW/4,imageSiz)*2 */ + unsigned short tmemSize; /* skip value of imagePtr for 1-loading + = tmemSizeW*tmemH */ +} F3DuObjBg_t; /* 40 bytes */ + +/* Scalable background plane */ +typedef struct { + unsigned short imageX; /* x-coordinate of upper-left position of texture (u10.5) */ + unsigned short imageW; /* width of texture (u10.2) */ + short frameX; /* upper-left position of transferred frame (s10.2) */ + unsigned short frameW; /* width of transferred frame (u10.2) */ + + unsigned short imageY; /* y-coordinate of upper-left position of texture (u10.5) */ + unsigned short imageH; /* height of texture (u10.2) */ + short frameY; /* upper-left position of transferred frame (s10.2) */ + unsigned short frameH; /* height of transferred frame (u10.2) */ + + unsigned long long int* imagePtr; /* texture source address on DRAM */ + unsigned short imageLoad; /* Which to use, LoadBlock or LoadTile? */ + unsigned char imageFmt; /* format of texel - G_IM_FMT_* */ + unsigned char imageSiz; /* size of texel - G_IM_SIZ_* */ + unsigned short imagePal; /* pallet number */ + unsigned short imageFlip; /* right & left image inversion (Inverted by G_BG_FLAG_FLIPS) */ + + unsigned short scaleW; /* scale value of X-direction (u5.10) */ + unsigned short scaleH; /* scale value of Y-direction (u5.10) */ + int imageYorig; /* start point of drawing on image (s20.5) */ + + unsigned char padding[4]; + +} F3DuObjScaleBg_t; /* 40 bytes */ + +typedef union { + F3DuObjBg_t b; + F3DuObjScaleBg_t s; + long long int force_structure_alignment; +} F3DuObjBg; + +/*---------------------------------------------------------------------------* + * 2D Objects + *---------------------------------------------------------------------------*/ +#define G_OBJ_FLAG_FLIPS 1 << 0 /* inversion to S-direction */ +#define G_OBJ_FLAG_FLIPT 1 << 4 /* nversion to T-direction */ + +typedef struct { + short objX; /* s10.2 OBJ x-coordinate of upper-left end */ + unsigned short scaleW; /* u5.10 Scaling of u5.10 width direction */ + unsigned short imageW; /* u10.5 width of u10.5 texture (length of S-direction) */ + unsigned short paddingX; /* Unused - Always 0 */ + short objY; /* s10.2 OBJ y-coordinate of s10.2 OBJ upper-left end */ + unsigned short scaleH; /* u5.10 Scaling of u5.10 height direction */ + unsigned short imageH; /* u10.5 height of u10.5 texture (length of T-direction) */ + unsigned short paddingY; /* Unused - Always 0 */ + unsigned short imageStride; /* folding width of texel (In units of 64bit word) */ + unsigned short imageAdrs; /* texture header position in TMEM (In units of 64bit word) */ + unsigned char imageFmt; /* format of texel - G_IM_FMT_* */ + unsigned char imageSiz; /* size of texel - G_IM_SIZ_* */ + unsigned char imagePal; /* pallet number (0-7) */ + unsigned char imageFlags; /* The display flag - G_OBJ_FLAG_FLIP* */ +} F3DuObjSprite_t; /* 24 bytes */ + +typedef union { + F3DuObjSprite_t s; + long long int force_structure_alignment; +} F3DuObjSprite; + +/*---------------------------------------------------------------------------* + * 2D Matrix + *---------------------------------------------------------------------------*/ +typedef struct { + int A, B, C, D; /* s15.16 */ + short X, Y; /* s10.2 */ + unsigned short BaseScaleX; /* u5.10 */ + unsigned short BaseScaleY; /* u5.10 */ +} F3DuObjMtx_t; /* 24 bytes */ + +typedef union { + F3DuObjMtx_t m; + long long int force_structure_alignment; +} F3DuObjMtx; + +typedef struct { + short X, Y; /* s10.2 */ + unsigned short BaseScaleX; /* u5.10 */ + unsigned short BaseScaleY; /* u5.10 */ +} F3DuObjSubMtx_t; /* 8 bytes */ + +typedef union { + F3DuObjSubMtx_t m; + long long int force_structure_alignment; +} F3DuObjSubMtx; + +/*---------------------------------------------------------------------------* + * Loading into TMEM + *---------------------------------------------------------------------------*/ +#define G_OBJLT_TXTRBLOCK 0x00001033 +#define G_OBJLT_TXTRTILE 0x00fc1034 +#define G_OBJLT_TLUT 0x00000030 + +#define GS_TB_TSIZE(pix, siz) (GS_PIX2TMEM((pix), (siz)) - 1) +#define GS_TB_TLINE(pix, siz) (GS_CALC_DXT(GS_PIX2TMEM((pix), (siz)))) + +typedef struct { + unsigned int type; /* G_OBJLT_TXTRBLOCK divided into types */ + unsigned long long int* image; /* texture source address on DRAM */ + unsigned short tmem; /* loaded TMEM word address (8byteWORD) */ + unsigned short tsize; /* Texture size, Specified by macro GS_TB_TSIZE() */ + unsigned short tline; /* width of Texture 1-line, Specified by macro GS_TB_TLINE() */ + unsigned short sid; /* STATE ID Multipled by 4 (Either one of 0, 4, 8 and 12) */ + unsigned int flag; /* STATE flag */ + unsigned int mask; /* STATE mask */ +} F3DuObjTxtrBlock_t; /* 24 bytes */ + +#define GS_TT_TWIDTH(pix, siz) ((GS_PIX2TMEM((pix), (siz)) << 2) - 1) +#define GS_TT_THEIGHT(pix, siz) (((pix) << 2) - 1) + +typedef struct { + unsigned int type; /* G_OBJLT_TXTRTILE divided into types */ + unsigned long long int* image; /* texture source address on DRAM */ + unsigned short tmem; /* loaded TMEM word address (8byteWORD)*/ + unsigned short twidth; /* width of Texture (Specified by macro GS_TT_TWIDTH()) */ + unsigned short theight; /* height of Texture (Specified by macro GS_TT_THEIGHT()) */ + unsigned short sid; /* STATE ID Multipled by 4 (Either one of 0, 4, 8 and 12) */ + unsigned int flag; /* STATE flag */ + unsigned int mask; /* STATE mask */ +} F3DuObjTxtrTile_t; /* 24 bytes */ + +#define GS_PAL_HEAD(head) ((head) + 256) +#define GS_PAL_NUM(num) ((num)-1) + +typedef struct { + unsigned int type; /* G_OBJLT_TLUT divided into types */ + unsigned long long int* image; /* texture source address on DRAM */ + unsigned short phead; /* pallet number of load header (Between 256 and 511) */ + unsigned short pnum; /* loading pallet number -1 */ + unsigned short zero; /* Assign 0 all the time */ + unsigned short sid; /* STATE ID Multipled by 4 (Either one of 0, 4, 8 and 12)*/ + unsigned int flag; /* STATE flag */ + unsigned int mask; /* STATE mask */ +} F3DuObjTxtrTLUT_t; /* 24 bytes */ + +typedef union { + F3DuObjTxtrBlock_t block; + F3DuObjTxtrTile_t tile; + F3DuObjTxtrTLUT_t tlut; + long long int force_structure_alignment; +} F3DuObjTxtr; + +/*---------------------------------------------------------------------------* + * Loading into TMEM & 2D Objects + *---------------------------------------------------------------------------*/ +typedef struct { + F3DuObjTxtr txtr; + F3DuObjSprite sprite; +} F3DuObjTxSprite; /* 48 bytes */ +} +#endif diff --git a/libultraship/include/fast/resource/ResourceType.h b/libultraship/include/fast/resource/ResourceType.h new file mode 100644 index 000000000..a00fe30f0 --- /dev/null +++ b/libultraship/include/fast/resource/ResourceType.h @@ -0,0 +1,14 @@ +#pragma once + +namespace Fast { + +enum class ResourceType { + None = 0x00000000, + + DisplayList = 0x4F444C54, // ODLT + Light = 0x46669697, // LGTS + Matrix = 0x4F4D5458, // OMTX + Texture = 0x4F544558, // OTEX + Vertex = 0x4F565458, // OVTX +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/factory/DisplayListFactory.h b/libultraship/include/fast/resource/factory/DisplayListFactory.h new file mode 100644 index 000000000..05afcb751 --- /dev/null +++ b/libultraship/include/fast/resource/factory/DisplayListFactory.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ship/resource/Resource.h" +#include "ship/resource/ResourceFactoryBinary.h" +#include "ship/resource/ResourceFactoryXML.h" + +namespace Fast { +class ResourceFactoryDisplayList { + protected: + uint32_t GetCombineLERPValue(const char* valStr); +}; + +class ResourceFactoryBinaryDisplayListV0 final : public ResourceFactoryDisplayList, public Ship::ResourceFactoryBinary { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; + +class ResourceFactoryXMLDisplayListV0 final : public ResourceFactoryDisplayList, public Ship::ResourceFactoryXML { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/factory/LightFactory.h b/libultraship/include/fast/resource/factory/LightFactory.h new file mode 100644 index 000000000..acf699f50 --- /dev/null +++ b/libultraship/include/fast/resource/factory/LightFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ship/resource/Resource.h" +#include "ship/resource/ResourceFactoryBinary.h" + +namespace Fast { +class ResourceFactoryBinaryLightV0 final : public Ship::ResourceFactoryBinary { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; +} // namespace Fast \ No newline at end of file diff --git a/libultraship/include/fast/resource/factory/MatrixFactory.h b/libultraship/include/fast/resource/factory/MatrixFactory.h new file mode 100644 index 000000000..45425dadb --- /dev/null +++ b/libultraship/include/fast/resource/factory/MatrixFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ship/resource/Resource.h" +#include "ship/resource/ResourceFactoryBinary.h" + +namespace Fast { +class ResourceFactoryBinaryMatrixV0 final : public Ship::ResourceFactoryBinary { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/factory/TextureFactory.h b/libultraship/include/fast/resource/factory/TextureFactory.h new file mode 100644 index 000000000..f561fda7b --- /dev/null +++ b/libultraship/include/fast/resource/factory/TextureFactory.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ship/resource/Resource.h" +#include "ship/resource/ResourceFactoryBinary.h" + +namespace Fast { +class ResourceFactoryBinaryTextureV0 final : public Ship::ResourceFactoryBinary { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; + +class ResourceFactoryBinaryTextureV1 final : public Ship::ResourceFactoryBinary { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/factory/VertexFactory.h b/libultraship/include/fast/resource/factory/VertexFactory.h new file mode 100644 index 000000000..6333dbff4 --- /dev/null +++ b/libultraship/include/fast/resource/factory/VertexFactory.h @@ -0,0 +1,19 @@ +#pragma once + +#include "ship/resource/Resource.h" +#include "ship/resource/ResourceFactoryBinary.h" +#include "ship/resource/ResourceFactoryXML.h" + +namespace Fast { +class ResourceFactoryBinaryVertexV0 final : public Ship::ResourceFactoryBinary { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; + +class ResourceFactoryXMLVertexV0 final : public Ship::ResourceFactoryXML { + public: + std::shared_ptr ReadResource(std::shared_ptr file, + std::shared_ptr initData) override; +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/type/DisplayList.h b/libultraship/include/fast/resource/type/DisplayList.h new file mode 100644 index 000000000..b356534c8 --- /dev/null +++ b/libultraship/include/fast/resource/type/DisplayList.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "ship/resource/Resource.h" +#include "fast/ucodehandlers.h" +#include + +namespace Fast { +class DisplayList final : public Ship::Resource { + public: + using Resource::Resource; + + DisplayList(); + ~DisplayList(); + + Gfx* GetPointer() override; + size_t GetPointerSize() override; + + UcodeHandlers UCode; + std::vector Instructions; + std::vector Strings; +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/type/Light.h b/libultraship/include/fast/resource/type/Light.h new file mode 100644 index 000000000..12052589b --- /dev/null +++ b/libultraship/include/fast/resource/type/Light.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include "ship/resource/Resource.h" + +namespace Fast { + +/* + We have to keep the Pads since its probably an + issue from the sdk, because the documentation expects + RGBA, but 'Color' has space for RGB only. + https://ultra64.ca/files/documentation/online-manuals/functions_reference_manual_2.0i/gsp/gSPLight.html +*/ + +struct LightN64 { + uint8_t Color[3]; + int8_t Pad1; + uint8_t ColorCopy[3]; + int8_t Pad2; + uint8_t Direction[3]; + int8_t Pad3; +}; + +struct PointLightN64 { + uint8_t Color[3]; + uint8_t Unk0; + uint8_t ColorCopy[3]; + uint8_t Unk1; + int16_t Position[3]; + uint8_t Unk2; +}; + +union LightData { + LightN64 Light; + PointLightN64 PointLight; + long long int ForceAlignment[2]; +}; + +union AmbientData { + uint8_t Color[3]; + int8_t Pad1; + uint8_t ColorCopy[3]; + int8_t Pad2; +}; + +struct LightEntry { + AmbientData Ambient; + LightData Light; +}; + +class Light final : public Ship::Resource { + public: + using Resource::Resource; + + Light() : Resource(std::shared_ptr()) { + } + + LightEntry* GetPointer(); + size_t GetPointerSize(); + + private: + LightEntry mLight; + std::vector mLightData; +}; +} // namespace Fast \ No newline at end of file diff --git a/libultraship/include/fast/resource/type/Matrix.h b/libultraship/include/fast/resource/type/Matrix.h new file mode 100644 index 000000000..a820351bf --- /dev/null +++ b/libultraship/include/fast/resource/type/Matrix.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ship/resource/Resource.h" +#include "fast/types.h" + +namespace Fast { +class Matrix final : public Ship::Resource { + public: + using Resource::Resource; + + Matrix(); + + Mtx* GetPointer() override; + size_t GetPointerSize() override; + + Mtx Matrx; +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/type/Texture.h b/libultraship/include/fast/resource/type/Texture.h new file mode 100644 index 000000000..c0fd71057 --- /dev/null +++ b/libultraship/include/fast/resource/type/Texture.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ship/resource/Resource.h" + +#define TEX_FLAG_LOAD_AS_RAW (1 << 0) +#define TEX_FLAG_LOAD_AS_IMG (1 << 1) + +namespace Fast { +enum class TextureType { + Error = 0, + RGBA32bpp = 1, + RGBA16bpp = 2, + Palette4bpp = 3, + Palette8bpp = 4, + Grayscale4bpp = 5, + Grayscale8bpp = 6, + GrayscaleAlpha4bpp = 7, + GrayscaleAlpha8bpp = 8, + GrayscaleAlpha16bpp = 9, +}; + +class Texture final : public Ship::Resource { + public: + using Resource::Resource; + + Texture(); + + uint8_t* GetPointer() override; + size_t GetPointerSize() override; + + TextureType Type; + uint16_t Width, Height; + uint32_t Flags = 0; + float HByteScale = 1.0; + float VPixelScale = 1.0; + uint32_t ImageDataSize; + uint8_t* ImageData = nullptr; + + ~Texture(); +}; +} // namespace Fast diff --git a/libultraship/include/fast/resource/type/Vertex.h b/libultraship/include/fast/resource/type/Vertex.h new file mode 100644 index 000000000..3fec39a8f --- /dev/null +++ b/libultraship/include/fast/resource/type/Vertex.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ship/resource/Resource.h" +#include + +union Vtx; + +namespace Fast { +class Vertex final : public Ship::Resource { + public: + using Resource::Resource; + + Vertex(); + + Vtx* GetPointer() override; + size_t GetPointerSize() override; + + std::vector VertexList; +}; +} // namespace Fast diff --git a/libultraship/include/fast/types.h b/libultraship/include/fast/types.h new file mode 100644 index 000000000..a60cca0af --- /dev/null +++ b/libultraship/include/fast/types.h @@ -0,0 +1,25 @@ +#pragma once + +typedef int Mtx_t[4][4]; +typedef union { + Mtx_t m; + struct { + unsigned short int intPart[4][4]; + unsigned short int fracPart[4][4]; + }; + long long int forc_structure_alignment; +} MtxS; + +typedef float MtxF_t[4][4]; +typedef union { + MtxF_t mf; + struct { + float xx, yx, zx, wx, xy, yy, zy, wy, xz, yz, zz, wz, xw, yw, zw, ww; + }; +} MtxF; + +#ifndef GBI_FLOATS +typedef MtxS Mtx; +#else +typedef MtxF Mtx; +#endif diff --git a/libultraship/include/fast/ucodehandlers.h b/libultraship/include/fast/ucodehandlers.h new file mode 100644 index 000000000..330375e43 --- /dev/null +++ b/libultraship/include/fast/ucodehandlers.h @@ -0,0 +1,11 @@ +#pragma once + +typedef enum UcodeHandlers { + ucode_f3db, + ucode_f3d, + ucode_f3dex, + ucode_f3dexb, + ucode_f3dex2, + ucode_s2dex, + ucode_max, +} UcodeHandlers; diff --git a/libultraship/include/libultraship/bridge.h b/libultraship/include/libultraship/bridge.h new file mode 100644 index 000000000..e19b6cc30 --- /dev/null +++ b/libultraship/include/libultraship/bridge.h @@ -0,0 +1,10 @@ +#pragma once + +#include "libultraship/bridge/resourcebridge.h" +#include "libultraship/bridge/audiobridge.h" +#include "libultraship/bridge/controllerbridge.h" +#include "libultraship/bridge/windowbridge.h" +#include "libultraship/bridge/consolevariablebridge.h" +#include "libultraship/bridge/crashhandlerbridge.h" +#include "libultraship/bridge/gfxdebuggerbridge.h" +#include "libultraship/bridge/gfxbridge.h" diff --git a/libultraship/include/libultraship/bridge/audiobridge.h b/libultraship/include/libultraship/bridge/audiobridge.h new file mode 100644 index 000000000..153ff6110 --- /dev/null +++ b/libultraship/include/libultraship/bridge/audiobridge.h @@ -0,0 +1,23 @@ +#pragma once + +#include "stdint.h" +#include "stddef.h" +#include "ship/audio/AudioChannelsSetting.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t AudioPlayerBuffered(); +int32_t AudioPlayerGetDesiredBuffered(); +AudioChannelsSetting GetAudioChannels(); +int32_t GetNumAudioChannels(); +void AudioPlayerPlayFrame(const uint8_t* buf, size_t len); + +// Set audio channels configuration at runtime (stereo or 5.1 surround) +// This will reinitialize the audio backend without requiring a game restart +void SetAudioChannels(AudioChannelsSetting channels); + +#ifdef __cplusplus +}; +#endif diff --git a/libultraship/include/libultraship/bridge/consolevariablebridge.h b/libultraship/include/libultraship/bridge/consolevariablebridge.h new file mode 100644 index 000000000..32400b425 --- /dev/null +++ b/libultraship/include/libultraship/bridge/consolevariablebridge.h @@ -0,0 +1,42 @@ +#pragma once + +#include "stdint.h" +#include "libultraship/color.h" + +#ifdef __cplusplus +#include +#include "ship/config/ConsoleVariable.h" +std::shared_ptr CVarGet(const char* name); + +extern "C" { +#endif + +int32_t CVarGetInteger(const char* name, int32_t defaultValue); +float CVarGetFloat(const char* name, float defaultValue); +const char* CVarGetString(const char* name, const char* defaultValue); +Color_RGBA8 CVarGetColor(const char* name, Color_RGBA8 defaultValue); +Color_RGB8 CVarGetColor24(const char* name, Color_RGB8 defaultValue); + +void CVarSetInteger(const char* name, int32_t value); +void CVarSetFloat(const char* name, float value); +void CVarSetString(const char* name, const char* value); +void CVarSetColor(const char* name, Color_RGBA8 value); +void CVarSetColor24(const char* name, Color_RGB8 value); + +void CVarRegisterInteger(const char* name, int32_t defaultValue); +void CVarRegisterFloat(const char* name, float defaultValue); +void CVarRegisterString(const char* name, const char* defaultValue); +void CVarRegisterColor(const char* name, Color_RGBA8 defaultValue); +void CVarRegisterColor24(const char* name, Color_RGB8 defaultValue); + +void CVarClear(const char* name); +bool CVarExists(const char* name); +void CVarClearBlock(const char* name); +void CVarCopy(const char* from, const char* to); + +void CVarLoad(); +void CVarSave(); + +#ifdef __cplusplus +}; +#endif diff --git a/libultraship/include/libultraship/bridge/controllerbridge.h b/libultraship/include/libultraship/bridge/controllerbridge.h new file mode 100644 index 000000000..1b029406e --- /dev/null +++ b/libultraship/include/libultraship/bridge/controllerbridge.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void ControllerBlockGameInput(uint16_t inputBlockId); +void ControllerUnblockGameInput(uint16_t inputBlockId); + +#ifdef __cplusplus +}; +#endif diff --git a/libultraship/include/libultraship/bridge/crashhandlerbridge.h b/libultraship/include/libultraship/bridge/crashhandlerbridge.h new file mode 100644 index 000000000..f65c3e791 --- /dev/null +++ b/libultraship/include/libultraship/bridge/crashhandlerbridge.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +typedef void (*CrashHandlerCallback)(char*, size_t*); + +#ifdef __cplusplus +extern "C" { +#endif + +void CrashHandlerRegisterCallback(CrashHandlerCallback callback); + +#ifdef __cplusplus +} +#endif diff --git a/libultraship/include/libultraship/bridge/gfxbridge.h b/libultraship/include/libultraship/bridge/gfxbridge.h new file mode 100644 index 000000000..b47867122 --- /dev/null +++ b/libultraship/include/libultraship/bridge/gfxbridge.h @@ -0,0 +1,16 @@ +#pragma once + +#include "stdint.h" +#include "fast/ucodehandlers.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void GfxSetNativeDimensions(uint32_t width, uint32_t height); +void GfxGetPixelDepthPrepare(float x, float y); +uint16_t GfxGetPixelDepth(float x, float y); + +#ifdef __cplusplus +} +#endif diff --git a/libultraship/include/libultraship/bridge/gfxdebuggerbridge.h b/libultraship/include/libultraship/bridge/gfxdebuggerbridge.h new file mode 100644 index 000000000..24a3ee74e --- /dev/null +++ b/libultraship/include/libultraship/bridge/gfxdebuggerbridge.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void GfxDebuggerRequestDebugging(); +bool GfxDebuggerIsDebugging(); +bool GfxDebuggerIsDebuggingRequested(); +void GfxDebuggerDebugDisplayList(void* cmds); + +#ifdef __cplusplus +}; +#endif diff --git a/libultraship/include/libultraship/bridge/resourcebridge.h b/libultraship/include/libultraship/bridge/resourcebridge.h new file mode 100644 index 000000000..161dec64a --- /dev/null +++ b/libultraship/include/libultraship/bridge/resourcebridge.h @@ -0,0 +1,51 @@ +#pragma once + +#include "stdint.h" + +#ifdef __cplusplus +#include "fast/resource/type/Texture.h" +#include "ship/resource/Resource.h" +#include + +std::shared_ptr ResourceLoad(const char* name); +std::shared_ptr ResourceLoad(uint64_t crc); +template std::shared_ptr ResourceLoad(const char* name) { + return std::static_pointer_cast(ResourceLoad(name)); +} +template std::shared_ptr ResourceLoad(uint64_t crc) { + return std::static_pointer_cast(ResourceLoad(crc)); +} + +extern "C" { +#endif + +uint64_t ResourceGetCrcByName(const char* name); +const char* ResourceGetNameByCrc(uint64_t crc); +size_t ResourceGetSizeByName(const char* name); +size_t ResourceGetSizeByCrc(uint64_t crc); +uint8_t ResourceGetIsCustomByName(const char* name); +uint8_t ResourceGetIsCustomByCrc(uint64_t crc); +void* ResourceGetDataByName(const char* name); +void* ResourceGetDataByCrc(uint64_t crc); +uint16_t ResourceGetTexWidthByName(const char* name); +uint16_t ResourceGetTexWidthByCrc(uint64_t crc); +uint16_t ResourceGetTexHeightByName(const char* name); +uint16_t ResourceGetTexHeightByCrc(uint64_t crc); +size_t ResourceGetTexSizeByName(const char* name); +size_t ResourceGetTexSizeByCrc(uint64_t crc); +void ResourceLoadDirectory(const char* name); +void ResourceLoadDirectoryAsync(const char* name); +void ResourceDirtyDirectory(const char* name); +void ResourceDirtyByName(const char* name); +void ResourceDirtyByCrc(uint64_t crc); +void ResourceUnloadByName(const char* name); +void ResourceUnloadByCrc(uint64_t crc); +void ResourceUnloadDirectory(const char* name); +void ResourceClearCache(); +void ResourceGetGameVersions(uint32_t* versions, size_t versionsSize, size_t* versionsCount); +uint32_t ResourceHasGameVersion(uint32_t hash); +uint32_t IsResourceManagerLoaded(); + +#ifdef __cplusplus +}; +#endif diff --git a/libultraship/include/libultraship/bridge/windowbridge.h b/libultraship/include/libultraship/bridge/windowbridge.h new file mode 100644 index 000000000..623f16d3c --- /dev/null +++ b/libultraship/include/libultraship/bridge/windowbridge.h @@ -0,0 +1,19 @@ +#pragma once + +#include "stdint.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool WindowIsRunning(); +uint32_t WindowGetWidth(); +uint32_t WindowGetHeight(); +float WindowGetAspectRatio(); +int32_t WindowGetPosX(); +int32_t WindowGetPosY(); +bool WindowIsFullscreen(); + +#ifdef __cplusplus +}; +#endif diff --git a/libultraship/include/libultraship/classes.h b/libultraship/include/libultraship/classes.h new file mode 100644 index 000000000..ab2183cf1 --- /dev/null +++ b/libultraship/include/libultraship/classes.h @@ -0,0 +1,38 @@ +#pragma once +#ifdef __cplusplus + +#include "ship/resource/archive/ArchiveManager.h" +#include "ship/resource/archive/Archive.h" +#include "ship/resource/archive/OtrArchive.h" +#include "ship/resource/archive/O2rArchive.h" +#include "ship/resource/ResourceManager.h" +#include "ship/Context.h" +#include "ship/window/Window.h" +#include "ship/debug/Console.h" +#include "ship/debug/CrashHandler.h" +#include "ship/config/ConsoleVariable.h" +#include "ship/config/Config.h" +#include "ship/window/gui/ConsoleWindow.h" +#include "ship/window/gui/GameOverlay.h" +#include "ship/window/gui/Gui.h" +#include "ship/window/gui/GuiMenuBar.h" +#include "ship/window/gui/GuiElement.h" +#include "ship/window/gui/GuiWindow.h" +#include "ship/window/gui/InputEditorWindow.h" +#include "ship/window/gui/StatsWindow.h" +#include "ship/controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h" +#include "ship/controller/controldevice/controller/Controller.h" +#include "ship/controller/controldeck/ControlDeck.h" +#include "ship/utils/binarytools/BinaryReader.h" +#include "ship/utils/binarytools/MemoryStream.h" +#include "ship/utils/binarytools/BinaryWriter.h" +#include "ship/audio/Audio.h" +#include "ship/audio/AudioPlayer.h" +#if defined(_WIN32) +#include "ship/audio/WasapiAudioPlayer.h" +#endif +#include "ship/audio/SDLAudioPlayer.h" +#ifdef __APPLE__ +#include "ship/utils/AppleFolderManager.h" +#endif +#endif diff --git a/libultraship/include/libultraship/color.h b/libultraship/include/libultraship/color.h new file mode 100644 index 000000000..b1f169350 --- /dev/null +++ b/libultraship/include/libultraship/color.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// only use when necessary for alignment purposes +typedef union { + struct { +#ifdef IS_BIGENDIAN + uint8_t r, g, b, a; +#else + uint8_t a, b, g, r; +#endif + }; + uint32_t rgba; +} Color_RGBA8_u32; + +typedef struct { + float r, g, b, a; +} Color_RGBAf; + +typedef union { + struct { + uint16_t r : 5; + uint16_t g : 5; + uint16_t b : 5; + uint16_t a : 1; + }; + uint16_t rgba; +} Color_RGBA16; + +#ifdef __cplusplus +}; +#endif diff --git a/libultraship/include/libultraship/controller/controldeck/ControlDeck.h b/libultraship/include/libultraship/controller/controldeck/ControlDeck.h new file mode 100644 index 000000000..79bb57995 --- /dev/null +++ b/libultraship/include/libultraship/controller/controldeck/ControlDeck.h @@ -0,0 +1,30 @@ +#pragma once + +#include "ship/controller/controldeck/ControlDeck.h" +#include "ship/controller/controldeck/ControlPort.h" +#include +#include "ship/config/Config.h" +#include "ship/controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h" +#include "ship/controller/physicaldevice/ConnectedPhysicalDeviceManager.h" +#include "ship/controller/physicaldevice/GlobalSDLDeviceSettings.h" +#include "ship/controller/controldevice/controller/mapping/ControllerDefaultMappings.h" +#include "libultraship/libultra/controller.h" + +namespace LUS { +class ControlDeck final : public Ship::ControlDeck { + public: + ControlDeck(); + ControlDeck(std::vector additionalBitmasks); + ControlDeck(std::vector additionalBitmasks, + std::shared_ptr controllerDefaultMappings, + std::unordered_map buttonNames); + + OSContPad* GetPads(); + void WriteToPad(void* pad) override; + + private: + void WriteToOSContPad(OSContPad* pad); + + OSContPad* mPads; +}; +} // namespace LUS diff --git a/libultraship/include/libultraship/controller/controldevice/controller/Controller.h b/libultraship/include/libultraship/controller/controldevice/controller/Controller.h new file mode 100644 index 000000000..d5ec7df36 --- /dev/null +++ b/libultraship/include/libultraship/controller/controldevice/controller/Controller.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "libultraship/libultra/controller.h" +#include "ship/utils/color.h" +#include +#include "ship/controller/controldevice/controller/Controller.h" + +namespace LUS { +class Controller : public Ship::Controller { + public: + Controller(uint8_t portIndex, std::vector bitmasks); + + void ReadToPad(void* pad) override; + + private: + void ReadToOSContPad(OSContPad* pad); + + std::deque mPadBuffer; +}; +} // namespace LUS diff --git a/libultraship/include/libultraship/controller/controldevice/controller/mapping/ControllerDefaultMappings.h b/libultraship/include/libultraship/controller/controldevice/controller/mapping/ControllerDefaultMappings.h new file mode 100644 index 000000000..5c3dd5dd5 --- /dev/null +++ b/libultraship/include/libultraship/controller/controldevice/controller/mapping/ControllerDefaultMappings.h @@ -0,0 +1,39 @@ +#pragma once + +#include "ship/controller/controldevice/controller/mapping/ControllerDefaultMappings.h" + +namespace LUS { + +class ControllerDefaultMappings : public Ship::ControllerDefaultMappings { + public: + ControllerDefaultMappings( + std::unordered_map> + defaultKeyboardKeyToButtonMappings, + std::unordered_map>> + defaultKeyboardKeyToAxisDirectionMappings, + std::unordered_map> + defaultSDLButtonToButtonMappings, + std::unordered_map>> + defaultSDLButtonToAxisDirectionMappings, + std::unordered_map>> + defaultSDLAxisDirectionToButtonMappings, + std::unordered_map>>> + defaultSDLAxisDirectionToAxisDirectionMappings); + ControllerDefaultMappings(); + ~ControllerDefaultMappings(); + + private: + void + SetDefaultKeyboardKeyToButtonMappings(std::unordered_map> + defaultKeyboardKeyToButtonMappings) override; + + void SetDefaultSDLButtonToButtonMappings( + std::unordered_map> + defaultSDLButtonToButtonMappings) override; + + void SetDefaultSDLAxisDirectionToButtonMappings( + std::unordered_map>> + defaultSDLAxisDirectionToButtonMappings) override; +}; +} // namespace LUS diff --git a/libultraship/include/libultraship/libultra.h b/libultraship/include/libultraship/libultra.h new file mode 100644 index 000000000..59df02a6a --- /dev/null +++ b/libultraship/include/libultraship/libultra.h @@ -0,0 +1,27 @@ +#pragma once + +#include "libultra/abi.h" +#include "libultra/controller.h" +#include "libultra/convert.h" +#include "libultra/eeprom.h" +#include "libultra/exception.h" +#include "libultra/gbi.h" +#include "libultra/gs2dex.h" +#include "libultra/gu.h" +#include "libultra/os.h" +#include "libultra/internal.h" +#include "libultra/interrupt.h" +#include "libultra/mbi.h" +#include "libultra/message.h" +#include "libultra/motor.h" +#include "libultra/pfs.h" +#include "libultra/pi.h" +#include "libultra/printf.h" +#include "libultra/r4300.h" +#include "libultra/rcp.h" +#include "libultra/rdp.h" +#include "libultra/sptask.h" +#include "libultra/thread.h" +#include "libultra/time.h" +#include "libultra/types.h" +#include "libultra/vi.h" diff --git a/libultraship/include/libultraship/libultra/abi.h b/libultraship/include/libultraship/libultra/abi.h new file mode 100644 index 000000000..3258dd11d --- /dev/null +++ b/libultraship/include/libultraship/libultra/abi.h @@ -0,0 +1,471 @@ +#pragma once + +typedef unsigned int u32; + +/* Audio commands: */ +#define A_SPNOOP 0 +#define A_ADPCM 1 +#define A_CLEARBUFF 2 +#define A_UNK3 3f +#define A_ADDMIXER 4 +#define A_RESAMPLE 5 +#define A_RESAMPLE_ZOH 6 +#define A_FILTER 7 +#define A_SETBUFF 8 +#define A_DUPLICATE 9 +#define A_DMEMMOVE 10 +#define A_LOADADPCM 11 +#define A_MIXER 12 +#define A_INTERLEAVE 13 +#define A_HILOGAIN 14 +#define A_SETLOOP 15 +#define A_INTERL 17 +#define A_ENVSETUP1 18 +#define A_ENVMIXER 19 +#define A_LOADBUFF 20 +#define A_SAVEBUFF 21 +#define A_ENVSETUP2 22 +#define A_S8DEC 23 +#define A_UNK19 25 + +#define ACMD_SIZE 32 + +/* + * Audio flags + */ +#define A_INIT 0x01 +#define A_CONTINUE 0x00 +#define A_LOOP 0x02 +#define A_ADPCM_SHORT 0x04 +#define A_OUT 0x02 +#define A_LEFT 0x02 +#define A_RIGHT 0x00 +#define A_VOL 0x04 +#define A_RATE 0x00 +#define A_AUX 0x08 +#define A_NOAUX 0x00 +#define A_MAIN 0x00 +#define A_MIX 0x10 + +/* + * Data Structures. + */ + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 gain : 16; + u32 addr; +} Aadpcm; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 gain : 16; + u32 addr; +} Apolef; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 pad1 : 16; + u32 addr; +} Aenvelope; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 8; + u32 dmem : 16; + u32 pad2 : 16; + u32 count : 16; +} Aclearbuff; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 8; + u32 pad2 : 16; + u32 inL : 16; + u32 inR : 16; +} Ainterleave; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 24; + u32 addr; +} Aloadbuff; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 pad1 : 16; + u32 addr; +} Aenvmixer; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 gain : 16; + u32 dmemi : 16; + u32 dmemo : 16; +} Amixer; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 dmem2 : 16; + u32 addr; +} Apan; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 pitch : 16; + u32 addr; +} Aresample; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 pad1 : 16; + u32 addr; +} Areverb; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 24; + u32 addr; +} Asavebuff; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 24; + u32 pad2 : 2; + u32 number : 4; + u32 base : 24; +} Asegment; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 dmemin : 16; + u32 dmemout : 16; + u32 count : 16; +} Asetbuff; + +typedef struct { + u32 cmd : 8; + u32 flags : 8; + u32 vol : 16; + u32 voltgt : 16; + u32 volrate : 16; +} Asetvol; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 8; + u32 dmemin : 16; + u32 dmemout : 16; + u32 count : 16; +} Admemmove; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 8; + u32 count : 16; + u32 addr; +} Aloadadpcm; + +typedef struct { + u32 cmd : 8; + u32 pad1 : 8; + u32 pad2 : 16; + u32 addr; +} Asetloop; + +/* + * Generic Acmd Packet + */ + +typedef struct { + u32 w0; + u32 w1; +} Awords; + +typedef union { + Awords words; + Aadpcm adpcm; + Apolef polef; + Aclearbuff clearbuff; + Aenvelope envelope; + Ainterleave interleave; + Aloadbuff loadbuff; + Aenvmixer envmixer; + Aresample resample; + Areverb reverb; + Asavebuff savebuff; + Asegment segment; + Asetbuff setbuff; + Asetvol setvol; + Admemmove dmemmove; + Aloadadpcm loadadpcm; + Amixer mixer; + Asetloop setloop; + long long int force_union_align; /* dummy, force alignment */ +} Acmd; + +/* + * ADPCM State + */ +#define ADPCMVSIZE 8 +#define ADPCMFSIZE 16 +typedef short ADPCM_STATE[ADPCMFSIZE]; + +/* + * Pole filter state + */ +typedef short POLEF_STATE[4]; + +/* + * Resampler state + */ +typedef short RESAMPLE_STATE[16]; + +/* + * Resampler constants + */ +#define UNITY_PITCH 0x8000 +#define MAX_RATIO 1.99996 /* within .03 cents of +1 octave */ + +/* + * Enveloper/Mixer state + */ +typedef short ENVMIX_STATE[40]; + +/* + * Macros to assemble the audio command list + */ + +#define aADPCMdec(pkt, f, s) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_ADPCM, 24, 8) | _SHIFTL(f, 16, 8); \ + _a->words.w1 = (u32)(s); \ + } + +#define aPoleFilter(pkt, f, g, s) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_POLEF, 24, 8) | _SHIFTL(f, 16, 8) | _SHIFTL(g, 0, 16)); \ + _a->words.w1 = (u32)(s); \ + } + +#define aHiLoGain(pkt, gain, count, dmem, a4) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_HILOGAIN, 24, 8) | _SHIFTL(gain, 16, 8) | _SHIFTL(count, 0, 16)); \ + _a->words.w1 = _SHIFTL(dmem, 16, 16) | _SHIFTL(a4, 0, 16); \ + } + +#define aUnkCmd3(pkt, a1, a2, a3) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_UNK3, 24, 8) | _SHIFTL(a3, 0, 16); \ + _a->words.w1 = _SHIFTL(a1, 16, 16) | _SHIFTL(a2, 0, 16); \ + } + +#define aUnkCmd19(pkt, a1, a2, a3, a4) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_UNK19, 24, 8) | _SHIFTL(a1, 16, 8) | _SHIFTL(a2, 0, 16)); \ + _a->words.w1 = _SHIFTL(a3, 16, 16) | _SHIFTL(a4, 0, 16); \ + } + +#define aS8Dec(pkt, a1, a2) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_S8DEC, 24, 8) | _SHIFTL(a1, 16, 8); \ + _a->words.w1 = (u32)(a2); \ + } + +#define aClearBuffer(pkt, d, c) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_CLEARBUFF, 24, 8) | _SHIFTL(d, 0, 24); \ + _a->words.w1 = (u32)(c); \ + } + +#define aEnvMixer(pkt, dmemi, count, swapLR, x0, x1, x2, x3, m, bits) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (bits | _SHIFTL(dmemi >> 4, 16, 8) | _SHIFTL(count, 8, 8) | _SHIFTL(swapLR, 4, 1) | \ + _SHIFTL(x0, 3, 1) | _SHIFTL(x1, 2, 1) | _SHIFTL(x2, 1, 1) | _SHIFTL(x3, 0, 1)); \ + _a->words.w1 = (u32)(m); \ + } + +#define aInterleave(pkt, o, l, r, c) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_INTERLEAVE, 24, 8) | _SHIFTL(c >> 4, 16, 8) | _SHIFTL(o, 0, 16)); \ + _a->words.w1 = _SHIFTL(l, 16, 16) | _SHIFTL(r, 0, 16); \ + } + +#define aInterl(pkt, dmemi, dmemo, count) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_INTERL, 24, 8) | _SHIFTL(count, 0, 16)); \ + _a->words.w1 = _SHIFTL(dmemi, 16, 16) | _SHIFTL(dmemo, 0, 16); \ + } + +#define aLoadBuffer(pkt, s, d, c) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_LOADBUFF, 24, 8) | _SHIFTL((c) >> 4, 16, 8) | _SHIFTL(d, 0, 16)); \ + _a->words.w1 = (u32)(s); \ + } + +#define aMix(pkt, f, g, i, o) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_MIXER, 24, 8) | _SHIFTL(f, 16, 8) | _SHIFTL(g, 0, 16)); \ + _a->words.w1 = _SHIFTL(i, 16, 16) | _SHIFTL(o, 0, 16); \ + } + +#define aPan(pkt, f, d, s) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_PAN, 24, 8) | _SHIFTL(f, 16, 8) | _SHIFTL(d, 0, 16)); \ + _a->words.w1 = (u32)(s); \ + } + +#define aResample(pkt, f, p, s) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_RESAMPLE, 24, 8) | _SHIFTL(f, 16, 8) | _SHIFTL(p, 0, 16)); \ + _a->words.w1 = (u32)(s); \ + } + +#define aSaveBuffer(pkt, s, d, c) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_SAVEBUFF, 24, 8) | _SHIFTL((c) >> 4, 16, 8) | _SHIFTL(s, 0, 16)); \ + _a->words.w1 = (u32)(d); \ + } + +#define aSegment(pkt, s, b) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_SEGMENT, 24, 8); \ + _a->words.w1 = _SHIFTL(s, 24, 8) | _SHIFTL(b, 0, 24); \ + } + +#define aSetBuffer(pkt, f, i, o, c) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_SETBUFF, 24, 8) | _SHIFTL(f, 16, 8) | _SHIFTL(i, 0, 16)); \ + _a->words.w1 = _SHIFTL(o, 16, 16) | _SHIFTL(c, 0, 16); \ + } + +#define aSetVolume(pkt, f, v, t, r) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_SETVOL, 24, 8) | _SHIFTL(f, 16, 16) | _SHIFTL(v, 0, 16)); \ + _a->words.w1 = _SHIFTL(r, 0, 16) | _SHIFTL(t, 16, 16); \ + } + +#define aSetVolume32(pkt, f, v, tr) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_SETVOL, 24, 8) | _SHIFTL(f, 16, 16) | _SHIFTL(v, 0, 16)); \ + _a->words.w1 = (u32)(tr); \ + } + +#define aSetLoop(pkt, a) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_SETLOOP, 24, 8); \ + _a->words.w1 = (u32)(a); \ + } + +#define aDMEMMove(pkt, i, o, c) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_DMEMMOVE, 24, 8) | _SHIFTL(i, 0, 24); \ + _a->words.w1 = _SHIFTL(o, 16, 16) | _SHIFTL(c, 0, 16); \ + } + +#define aLoadADPCM(pkt, c, d) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_LOADADPCM, 24, 8) | _SHIFTL(c, 0, 24); \ + _a->words.w1 = (u32)d; \ + } + +#define aEnvSetup1(pkt, a, b, c, d) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_ENVSETUP1, 24, 8) | _SHIFTL(a, 16, 8) | _SHIFTL(b, 0, 16)); \ + _a->words.w1 = _SHIFTL(c, 16, 16) | _SHIFTL(d, 0, 16); \ + } + +#define aEnvSetup2(pkt, volLeft, volRight) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = _SHIFTL(A_ENVSETUP2, 24, 8); \ + _a->words.w1 = (_SHIFTL(volLeft, 16, 16) | _SHIFTL(volRight, 0, 16)); \ + } + +#define aFilter(pkt, f, countOrBuf, addr) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_FILTER, 24, 8) | _SHIFTL(f, 16, 8) | _SHIFTL(countOrBuf, 0, 16)); \ + _a->words.w1 = (u32)(addr); \ + } + +#define aDuplicate(pkt, numCopies, dmemSrc, dmemDest) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_DUPLICATE, 24, 8) | _SHIFTL(numCopies, 16, 8) | _SHIFTL(dmemSrc, 0, 16)); \ + _a->words.w1 = _SHIFTL(dmemDest, 16, 16) | _SHIFTL(0x80, 0, 16); \ + } + +#define aAddMixer(pkt, count, dmemi, dmemo, a4) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_ADDMIXER, 24, 8) | _SHIFTL(count >> 4, 16, 8) | _SHIFTL(a4, 0, 16)); \ + _a->words.w1 = _SHIFTL(dmemi, 16, 16) | _SHIFTL(dmemo, 0, 16); \ + } + +#define aResampleZoh(pkt, pitch, pitchAccu) \ + { \ + Acmd* _a = (Acmd*)pkt; \ + \ + _a->words.w0 = (_SHIFTL(A_RESAMPLE_ZOH, 24, 8) | _SHIFTL(pitch, 0, 16)); \ + _a->words.w1 = _SHIFTL(pitchAccu, 0, 16); \ + } diff --git a/libultraship/include/libultraship/libultra/controller.h b/libultraship/include/libultraship/libultra/controller.h new file mode 100644 index 000000000..a82cb3dd3 --- /dev/null +++ b/libultraship/include/libultraship/libultra/controller.h @@ -0,0 +1,162 @@ +#pragma once + +#include "stdint.h" + +#define SIAccessQueueSize 2 +#define BLOCKSIZE 32 +#define PFS_ONE_PAGE 8 + +#define PFS_PAGE_SIZE (BLOCKSIZE * PFS_ONE_PAGE) + +#define CONT_CMD_REQUEST_STATUS 0 +#define CONT_CMD_READ_BUTTON 1 +#define CONT_CMD_READ_MEMPACK 2 +#define CONT_CMD_WRITE_MEMPACK 3 +#define CONT_CMD_READ_EEPROM 4 +#define CONT_CMD_WRITE_EEPROM 5 +#define CONT_CMD_RESET 0xFF + +#define CONT_CMD_REQUEST_STATUS_TX 1 +#define CONT_CMD_READ_BUTTON_TX 1 +#define CONT_CMD_READ_MEMPACK_TX 3 +#define CONT_CMD_WRITE_MEMPACK_TX 35 +#define CONT_CMD_READ_EEPROM_TX 2 +#define CONT_CMD_WRITE_EEPROM_TX 10 +#define CONT_CMD_RESET_TX 1 + +#define CONT_CMD_REQUEST_STATUS_RX 3 +#define CONT_CMD_READ_BUTTON_RX 4 +#define CONT_CMD_READ_MEMPACK_RX 33 +#define CONT_CMD_WRITE_MEMPACK_RX 1 +#define CONT_CMD_READ_EEPROM_RX 8 +#define CONT_CMD_WRITE_EEPROM_RX 1 +#define CONT_CMD_RESET_RX 3 + +#define CONT_CMD_NOP 0xFF +#define CONT_CMD_END 0xFE // Indicates end of a command +#define CONT_CMD_EXE 1 // Set pif ram status byte to this to do a command + +#define CONT_ERR_NO_CONTROLLER PFS_ERR_NOPACK /* 1 */ +#define CONT_ERR_CONTRFAIL CONT_OVERRUN_ERROR /* 4 */ +#define CONT_ERR_INVALID PFS_ERR_INVALID /* 5 */ +#define CONT_ERR_DEVICE PFS_ERR_DEVICE /* 11 */ +#define CONT_ERR_NOT_READY 12 +#define CONT_ERR_VOICE_MEMORY 13 +#define CONT_ERR_VOICE_WORD 14 +#define CONT_ERR_VOICE_NO_RESPONSE 15 + +#define DIR_STATUS_EMPTY 0 +#define DIR_STATUS_UNKNOWN 1 +#define DIR_STATUS_OCCUPIED 2 + +#define PFS_FORCE 1 +#define PFS_DELETE 1 + +#define PFS_LABEL_AREA 7 + +#define PFS_ERR_NOPACK 1 + +#ifndef CONTROLLERBUTTONS_T +#define CONTROLLERBUTTONS_T uint16_t +#endif + +/* Buttons */ +#define BTN_CRIGHT 0x00001 +#define BTN_CLEFT 0x00002 +#define BTN_CDOWN 0x00004 +#define BTN_CUP 0x00008 +#define BTN_R 0x00010 +#define BTN_L 0x00020 +#define BTN_DRIGHT 0x00100 +#define BTN_DLEFT 0x00200 +#define BTN_DDOWN 0x00400 +#define BTN_DUP 0x00800 +#define BTN_START 0x01000 +#define BTN_Z 0x02000 +#define BTN_B 0x04000 +#define BTN_A 0x08000 +#define BTN_STICKLEFT 0x10000 +#define BTN_STICKRIGHT 0x20000 +#define BTN_STICKDOWN 0x40000 +#define BTN_STICKUP 0x80000 +#define BTN_VSTICKUP 0x100000 +#define BTN_VSTICKDOWN 0x200000 +#define BTN_VSTICKLEFT 0x400000 +#define BTN_VSTICKRIGHT 0x800000 + +typedef struct { + /* 0x00 */ int32_t ram[15]; + /* 0x3C */ int32_t status; +} OSPifRam; // size = 0x40 + +typedef struct { + /* 0x00 */ uint16_t type; + /* 0x02 */ uint8_t status; + /* 0x03 */ uint8_t err_no; +} OSContStatus; // size = 0x04 + +typedef struct { + /* 0x00 */ CONTROLLERBUTTONS_T button; + /* 0x02 */ int8_t stick_x; + /* 0x03 */ int8_t stick_y; + /* 0x04 */ uint8_t err_no; + /* 0x05 */ float gyro_x; + /* 0x09 */ float gyro_y; + /* 0x1C */ int8_t right_stick_x; + /* 0x20 */ int8_t right_stick_y; +} OSContPad; // size = 0x24 + +typedef struct { + /* 0x00 */ void* address; + /* 0x04 */ uint8_t databuffer[32]; + /* 0x24 */ uint8_t addressCrc; + /* 0x25 */ uint8_t dataCrc; + /* 0x26 */ uint8_t err_no; +} OSContRamIo; // size = 0x28 + +// Original name: __OSContRequesFormat +typedef struct { + /* 0x00 */ uint8_t align; + /* 0x01 */ uint8_t txsize; + /* 0x02 */ uint8_t rxsize; + /* 0x03 */ uint8_t poll; + /* 0x04 */ uint8_t typeh; + /* 0x05 */ uint8_t typel; + /* 0x06 */ uint8_t status; + /* 0x07 */ uint8_t align1; +} __OSContRequestHeader; // size = 0x8 + +// Original name: __OSContRequesHeaderFormatShort +typedef struct { + /* 0x00 */ uint8_t txsize; + /* 0x01 */ uint8_t rxsize; + /* 0x02 */ uint8_t poll; + /* 0x03 */ uint8_t typeh; + /* 0x04 */ uint8_t typel; + /* 0x05 */ uint8_t status; +} __OSContRequestHeaderAligned; // size = 0x6 + +// Original Name: __OSContRamReadFormat +typedef struct { + /* 0x00 */ uint8_t unk_00; + /* 0x01 */ uint8_t txsize; + /* 0x02 */ uint8_t rxsize; + /* 0x03 */ uint8_t poll; + /* 0x04 */ uint8_t hi; + /* 0x05 */ uint8_t lo; + /* 0x06 */ uint8_t data[BLOCKSIZE]; + /* 0x26 */ uint8_t datacrc; +} __OSContRamHeader; // size = 0x27 + +// Original name: __OSContReadFormat +typedef struct { + /* 0x00 */ uint8_t align; + /* 0x01 */ uint8_t txsize; + /* 0x02 */ uint8_t rxsize; + /* 0x03 */ uint8_t poll; + /* 0x04 */ uint16_t button; + /* 0x06 */ int8_t joyX; + /* 0x07 */ int8_t joyY; +} __OSContReadHeader; // size = 0x8 + +#include "os.h" diff --git a/libultraship/include/libultraship/libultra/convert.h b/libultraship/include/libultraship/libultra/convert.h new file mode 100644 index 000000000..48dd06f69 --- /dev/null +++ b/libultraship/include/libultraship/libultra/convert.h @@ -0,0 +1,15 @@ +#pragma once + +#define OS_CLOCK_RATE 62500000LL +#define OS_CPU_COUNTER (OS_CLOCK_RATE * 3 / 4) + +#define OS_NSEC_TO_CYCLES(n) (((u64)(n) * (OS_CPU_COUNTER / 15625000LL)) / (1000000000LL / 15625000LL)) +#define OS_USEC_TO_CYCLES(n) (((u64)(n) * (OS_CPU_COUNTER / 15625LL)) / (1000000LL / 15625LL)) +#define OS_CYCLES_TO_NSEC(c) (((u64)(c) * (1000000000LL / 15625000LL)) / (OS_CPU_COUNTER / 15625000LL)) +#define OS_CYCLES_TO_USEC(c) (((u64)(c) * (1000000LL / 15625LL)) / (OS_CPU_COUNTER / 15625LL)) + +#define OS_K0_TO_PHYSICAL(x) (x) +#define OS_K1_TO_PHYSICAL(x) (x) + +#define OS_PHYSICAL_TO_K0(x) (x) +#define OS_PHYSICAL_TO_K1(x) (x) diff --git a/libultraship/include/libultraship/libultra/eeprom.h b/libultraship/include/libultraship/libultra/eeprom.h new file mode 100644 index 000000000..b64c87e5c --- /dev/null +++ b/libultraship/include/libultraship/libultra/eeprom.h @@ -0,0 +1,18 @@ +#pragma once + +#include "message.h" + +#define EEPROM_TYPE_4K 0x01 +#define EEPROM_TYPE_16K 0x02 + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t osEepromProbe(OSMesgQueue*); +int32_t osEepromLongRead(OSMesgQueue*, uint8_t, uint8_t*, int32_t); +int32_t osEepromLongWrite(OSMesgQueue*, uint8_t, uint8_t*, int32_t); + +#ifdef __cplusplus +} +#endif diff --git a/libultraship/include/libultraship/libultra/exception.h b/libultraship/include/libultraship/libultra/exception.h new file mode 100644 index 000000000..7442b8073 --- /dev/null +++ b/libultraship/include/libultraship/libultra/exception.h @@ -0,0 +1,42 @@ +#pragma once + +#include "types.h" + +// Interrupt masks +#define OS_IM_NONE 0x00000001 +#define OS_IM_SW1 0x00000501 +#define OS_IM_SW2 0x00000601 +#define OS_IM_CART 0x00000C01 +#define OS_IM_PRENMI 0x00001401 +#define OS_IM_RDBWRITE 0x00002401 +#define OS_IM_RDBREAD 0x00004401 +#define OS_IM_COUNTER 0x00008401 +#define OS_IM_CPU 0x0000FF01 +#define OS_IM_SP 0x00010401 +#define OS_IM_SI 0x00020401 +#define OS_IM_AI 0x00040401 +#define OS_IM_VI 0x00080401 +#define OS_IM_PI 0x00100401 +#define OS_IM_DP 0x00200401 +#define OS_IM_ALL 0x003FFF01 + +#define RCP_IMASK 0x003F0000 +#define RCP_IMASKSHIFT 16 + +typedef u32 OSIntMask; +typedef u32 OSHWIntr; + +typedef struct { + /* 0x00 */ void* callback; + /* 0x04 */ void* sp; +} __osHwInt; // size = 0x08 + +OSIntMask osSetIntMask(OSIntMask); + +void __osSetHWIntrRoutine(OSHWIntr interrupt, s32 (*callback)(void), void* sp); +void __osGetHWIntrRoutine(OSHWIntr interrupt, s32 (**callbackOut)(void), void** spOut); + +void __osSetGlobalIntMask(OSHWIntr mask); +void __osResetGlobalIntMask(OSHWIntr mask); + +extern __osHwInt __osHwIntTable[]; diff --git a/libultraship/include/libultraship/libultra/gbi.h b/libultraship/include/libultraship/libultra/gbi.h new file mode 100644 index 000000000..ef3425335 --- /dev/null +++ b/libultraship/include/libultraship/libultra/gbi.h @@ -0,0 +1,4223 @@ +#pragma once + +#include "mbi.h" + +#ifdef _MSC_VER +#ifndef u8 +#define u8 uint8_t +#endif + +#ifndef u16 +#define u16 uint16_t +#endif + +#ifndef u32 +#define u32 uint32_t +#endif + +#ifndef u64 +#define u64 uint64_t +#endif + +#ifndef s8 +#define s8 int8_t +#endif + +#ifndef s16 +#define s16 int16_t +#endif + +#ifndef s32 +#define s32 int32_t +#endif + +#ifndef s64 +#define s64 int64_t +#endif +#endif + +/* To enable Fast3DEX grucode support, define F3DEX_GBI. */ + +/* Types */ + +/* Private macro to wrap other macros in do {...} while (0) */ +#define _DW(macro) \ + do { \ + macro \ + } while (0) + +#ifdef F3DEX_GBI_2 +#ifndef F3DEX_GBI +#define F3DEX_GBI +#endif +#define G_NOOP 0x00 +#define G_RDPHALF_2 0xf1 +#define G_SETOTHERMODE_H 0xe3 +#define G_SETOTHERMODE_L 0xe2 +#define G_RDPHALF_1 0xe1 +#define G_SPNOOP 0xe0 +#define G_ENDDL 0xdf +#define G_DL 0xde +#define G_LOAD_UCODE 0xdd +#define G_MOVEMEM 0xdc +#define G_MOVEWORD 0xdb +#define G_MTX 0xda +#define G_GEOMETRYMODE 0xd9 +#define G_POPMTX 0xd8 +#define G_TEXTURE 0xd7 +#define G_DMA_IO 0xd6 +#define G_SPECIAL_1 0xd5 +#define G_SPECIAL_2 0xd4 +#define G_SPECIAL_3 0xd3 + +#define G_VTX 0x01 +#define G_MODIFYVTX 0x02 +#define G_CULLDL 0x03 +#define G_BRANCH_Z 0x04 +#define G_TRI1 0x05 +#define G_TRI2 0x06 +#define G_QUAD 0x07 +#define G_LINE3D 0x08 +#else /* F3DEX_GBI_2 */ + +/* DMA commands: */ +#define G_SPNOOP 0 /* handle 0 gracefully */ +#define G_MTX 1 +#define G_RESERVED0 2 /* not implemeted */ +#define G_MOVEMEM 3 /* move a block of memory (up to 4 words) to dmem */ +#define G_VTX 4 +#define G_RESERVED1 5 /* not implemeted */ +#define G_DL 6 +#define G_RESERVED2 7 /* not implemeted */ +#define G_RESERVED3 8 /* not implemeted */ +#define G_SPRITE2D_BASE 9 /* sprite command */ + +/* IMMEDIATE commands: */ +#define G_IMMFIRST -65 +#define G_TRI1 (G_IMMFIRST - 0) +#define G_CULLDL (G_IMMFIRST - 1) +#define G_POPMTX (G_IMMFIRST - 2) +#define G_MOVEWORD (G_IMMFIRST - 3) +#define G_TEXTURE (G_IMMFIRST - 4) +#define G_SETOTHERMODE_H (G_IMMFIRST - 5) +#define G_SETOTHERMODE_L (G_IMMFIRST - 6) +#define G_ENDDL (G_IMMFIRST - 7) +#define G_SETGEOMETRYMODE (G_IMMFIRST - 8) +#define G_CLEARGEOMETRYMODE (G_IMMFIRST - 9) +#define G_LINE3D (G_IMMFIRST - 10) +#define G_RDPHALF_1 (G_IMMFIRST - 11) +#define G_RDPHALF_2 (G_IMMFIRST - 12) +#if (defined(F3DEX_GBI) || defined(F3DLP_GBI)) +#define G_MODIFYVTX (G_IMMFIRST - 13) +#define G_TRI2 (G_IMMFIRST - 14) +#define G_BRANCH_Z (G_IMMFIRST - 15) +#define G_LOAD_UCODE (G_IMMFIRST - 16) +#define G_QUAD (G_IMMFIRST - 10) +#else +#define G_RDPHALF_CONT (G_IMMFIRST - 13) +#endif + +/* We are overloading 2 of the immediate commands + to keep the byte alignment of dmem the same */ + +#define G_SPRITE2D_SCALEFLIP (G_IMMFIRST - 1) +#define G_SPRITE2D_DRAW (G_IMMFIRST - 2) + +/* RDP commands: */ +#define G_NOOP 0xc0 /* 0 */ + +#endif /* F3DEX_GBI_2 */ + +/* RDP commands: */ +#define G_SETCIMG 0xff /* -1 */ +#define G_SETZIMG 0xfe /* -2 */ +#define G_SETTIMG 0xfd /* -3 */ +#define G_SETCOMBINE 0xfc /* -4 */ +#define G_SETENVCOLOR 0xfb /* -5 */ +#define G_SETPRIMCOLOR 0xfa /* -6 */ +#define G_SETBLENDCOLOR 0xf9 /* -7 */ +#define G_SETFOGCOLOR 0xf8 /* -8 */ +#define G_SETFILLCOLOR 0xf7 /* -9 */ +#define G_FILLRECT 0xf6 /* -10 */ +#define G_SETTILE 0xf5 /* -11 */ +#define G_LOADTILE 0xf4 /* -12 */ +#define G_LOADBLOCK 0xf3 /* -13 */ +#define G_SETTILESIZE 0xf2 /* -14 */ +#define G_LOADTLUT 0xf0 /* -16 */ +#define G_RDPSETOTHERMODE 0xef /* -17 */ +#define G_SETPRIMDEPTH 0xee /* -18 */ +#define G_SETSCISSOR 0xed /* -19 */ +#define G_SETCONVERT 0xec /* -20 */ +#define G_SETKEYR 0xeb /* -21 */ +#define G_SETKEYGB 0xea /* -22 */ +#define G_RDPFULLSYNC 0xe9 /* -23 */ +#define G_RDPTILESYNC 0xe8 /* -24 */ +#define G_RDPPIPESYNC 0xe7 /* -25 */ +#define G_RDPLOADSYNC 0xe6 /* -26 */ +#define G_TEXRECTFLIP 0xe5 /* -27 */ +#define G_TEXRECT 0xe4 /* -28 */ + +// CUSTOM OTR COMMANDS +#define G_SETTIMG_OTR_HASH 0x20 +#define G_SETFB 0x21 +#define G_RESETFB 0x22 +#define G_SETTIMG_FB 0x23 +#define G_VTX_OTR_FILEPATH 0x24 +#define G_SETTIMG_OTR_FILEPATH 0x25 +#define G_TRI1_OTR 0x26 +#define G_DL_OTR_FILEPATH 0x27 +#define G_PUSHCD 0x28 +#define G_MTX_OTR_FILEPATH 0x29 +#define G_DL_OTR_HASH 0x31 +#define G_VTX_OTR_HASH 0x32 +#define G_MARKER 0x33 +#define G_INVALTEXCACHE 0x34 +#define G_BRANCH_Z_OTR 0x35 +#define G_MTX_OTR 0x36 +#define G_TEXRECT_WIDE 0x37 +#define G_FILLWIDERECT 0x38 +#define G_REGBLENDEDTEX 0x3f +#define G_MOVEMEM_OTR 0x42 + +/* GFX Effects */ + +// RDP Cmd +#define G_SETGRAYSCALE 0x39 +#define G_EXTRAGEOMETRYMODE 0x3a +#define G_COPYFB 0x3b +#define G_IMAGERECT 0x3c +#define G_DL_INDEX 0x3d +#define G_READFB 0x3e +#define G_SETINTENSITY 0x40 +#define G_LOAD_SHADER 0x43 +#define G_SETTILESIZE_INTERP 0x44 +#define G_SETTARGETINTERPINDEX 0x45 + +/* + * The following commands are the "generated" RDP commands; the user + * never sees them, the RSP microcode generates them. + * + * The layout of the bits is magical, to save work in the ucode. + * These id's are -56, -52, -54, -50, -55, -51, -53, -49, ... + * edge, shade, texture, zbuff bits: estz + */ +#define G_TRI_FILL 0xc8 /* fill triangle: 11001000 */ +#define G_TRI_SHADE 0xcc /* shade triangle: 11001100 */ +#define G_TRI_TXTR 0xca /* texture triangle: 11001010 */ +#define G_TRI_SHADE_TXTR 0xce /* shade, texture triangle: 11001110 */ +#define G_TRI_FILL_ZBUFF 0xc9 /* fill, zbuff triangle: 11001001 */ +#define G_TRI_SHADE_ZBUFF 0xcd /* shade, zbuff triangle: 11001101 */ +#define G_TRI_TXTR_ZBUFF 0xcb /* texture, zbuff triangle: 11001011 */ +#define G_TRI_SHADE_TXTR_ZBUFF 0xcf /* shade, txtr, zbuff trngl: 11001111 */ + +/* + * A TRI_FILL triangle is just the edges. You need to set the DP + * to use primcolor, in order to see anything. (it is NOT a triangle + * that gets rendered in 'fill mode'. Triangles can't be rendered + * in 'fill mode') + * + * A TRI_SHADE is a gouraud triangle that has colors interpolated. + * Flat-shaded triangles (from the software) are still gouraud shaded, + * it's just the colors are all the same and the deltas are 0. + * + * Other triangle types, and combinations are more obvious. + */ + +/* masks to build RDP triangle commands: */ +#define G_RDP_TRI_FILL_MASK 0x08 +#define G_RDP_TRI_SHADE_MASK 0x04 +#define G_RDP_TRI_TXTR_MASK 0x02 +#define G_RDP_TRI_ZBUFF_MASK 0x01 + +/* + * HACK: + * This is a dreadful hack. For version 1.0 hardware, there are still + * some 'bowtie' hangs. This parameter can be increased to avoid + * the hangs. Every increase of 4 chops one scanline off of every + * triangle. Values of 4,8,12 should be sufficient to avoid any + * bowtie hang. + * + * Change this value, then recompile ALL of your program (including static + * display lists!) + * + * THIS WILL BE REMOVED FOR HARDWARE VERSION 2.0! + */ +#define BOWTIE_VAL 0 + +/* gets added to RDP command, in order to test for addres fixup: */ +#define G_RDP_ADDR_FIXUP 3 /* |RDP cmds| <= this, do addr fixup */ +#ifdef _LANGUAGE_ASSEMBLY +#define G_RDP_TEXRECT_CHECK ((-1 * G_TEXRECTFLIP) & 0xff) +#endif + +/* macros for command parsing: */ +#define GDMACMD(x) (x) +#define GIMMCMD(x) (G_IMMFIRST - (x)) +#define GRDPCMD(x) (0xff - (x)) + +#define G_DMACMDSIZ 128 +#define G_IMMCMDSIZ 64 +#define G_RDPCMDSIZ 64 + +/* + * Coordinate shift values, number of bits of fraction + */ +#define G_TEXTURE_IMAGE_FRAC 2 +#define G_TEXTURE_SCALE_FRAC 16 +#define G_SCALE_FRAC 8 +#define G_ROTATE_FRAC 16 + +/* + * Parameters to graphics commands + */ + +/* + * Data packing macros + */ + +/* + * Maximum z-buffer value, used to initialize the z-buffer. + * Note : this number is NOT the viewport z-scale constant. + * See the comment next to G_MAXZ for more info. + */ +#define G_MAXFBZ 0x3fff /* 3b exp, 11b mantissa */ + +#define GPACK_RGBA5551(r, g, b, a) ((((r) << 8) & 0xf800) | (((g) << 3) & 0x7c0) | (((b) >> 2) & 0x3e) | ((a)&0x1)) +#define GPACK_ZDZ(z, dz) ((z) << 2 | (dz)) + +/* + * G_MTX: parameter flags + */ +#ifdef F3DEX_GBI_2 +#define G_MTX_MODELVIEW 0x00 /* matrix types */ +#define G_MTX_PROJECTION 0x04 +#define G_MTX_MUL 0x00 /* concat or load */ +#define G_MTX_LOAD 0x02 +#define G_MTX_NOPUSH 0x00 /* push or not */ +#define G_MTX_PUSH 0x01 +#else /* F3DEX_GBI_2 */ +#define G_MTX_MODELVIEW 0x00 /* matrix types */ +#define G_MTX_PROJECTION 0x01 +#define G_MTX_MUL 0x00 /* concat or load */ +#define G_MTX_LOAD 0x02 +#define G_MTX_NOPUSH 0x00 /* push or not */ +#define G_MTX_PUSH 0x04 +#endif /* F3DEX_GBI_2 */ + +/* + * flags for G_SETGEOMETRYMODE + * (this rendering state is maintained in RSP) + * + * DO NOT USE THE LOW 8 BITS OF GEOMETRYMODE: + * The weird bit-ordering is for the micro-code: the lower byte + * can be OR'd in with G_TRI_SHADE (11001100) to construct + * the triangle command directly. Don't break it... + * + * DO NOT USE THE HIGH 8 BITS OF GEOMETRYMODE: + * The high byte is OR'd with 0x703 to form the clip code mask. + * If it is set to 0x04, this will cause near clipping to occur. + * If it is zero, near clipping will not occur. + * + * Further explanation: + * G_SHADE is necessary in order to see the color that you passed + * down with the vertex. If G_SHADE isn't set, you need to set the DP + * appropriately and use primcolor to see anything. + * + * G_SHADING_SMOOTH enabled means use all 3 colors of the triangle. + * If it is not set, then do 'flat shading', where only one vertex color + * is used (and all 3 vertices are set to that same color by the ucode) + * See the man page for gSP1Triangle(). + * + */ +#define G_ZBUFFER 0x00000001 +#define G_SHADE 0x00000004 /* enable Gouraud interp */ + /* rest of low byte reserved for setup ucode */ +#ifdef F3DEX_GBI_2 +#define G_TEXTURE_ENABLE 0x00000000 /* Ignored */ +#define G_SHADING_SMOOTH 0x00200000 /* flat or smooth shaded */ +#define G_CULL_FRONT 0x00000200 +#define G_CULL_BACK 0x00000400 +#define G_CULL_BOTH 0x00000600 /* To make code cleaner */ +#else +#define G_TEXTURE_ENABLE 0x00000002 /* Microcode use only */ +#define G_SHADING_SMOOTH 0x00000200 /* flat or smooth shaded */ +#define G_CULL_FRONT 0x00001000 +#define G_CULL_BACK 0x00002000 +#define G_CULL_BOTH 0x00003000 /* To make code cleaner */ +#endif +#define G_FOG 0x00010000 +#define G_LIGHTING 0x00020000 +#define G_TEXTURE_GEN 0x00040000 +#define G_TEXTURE_GEN_LINEAR 0x00080000 +#define G_LOD 0x00100000 /* NOT IMPLEMENTED */ +#define G_LIGHTING_POSITIONAL 0x00400000 +#if (defined(F3DEX_GBI) || defined(F3DLP_GBI)) +#define G_CLIPPING 0x00800000 +#else +#define G_CLIPPING 0x00000000 +#endif + +#ifdef _LANGUAGE_ASSEMBLY +#define G_FOG_H (G_FOG / 0x10000) +#define G_LIGHTING_H (G_LIGHTING / 0x10000) +#define G_TEXTURE_GEN_H (G_TEXTURE_GEN / 0x10000) +#define G_TEXTURE_GEN_LINEAR_H (G_TEXTURE_GEN_LINEAR / 0x10000) +#define G_LOD_H (G_LOD / 0x10000) /* NOT IMPLEMENTED */ +#if (defined(F3DEX_GBI) || defined(F3DLP_GBI)) +#define G_CLIPPING_H (G_CLIPPING / 0x10000) +#endif +#endif + +/* + * G_EXTRAGEOMETRY flags: set extra custom geometry modes + */ +#define G_EX_INVERT_CULLING 0x00000001 +#define G_EX_ALWAYS_EXECUTE_BRANCH 0x00000002 + +/* Need these defined for Sprite Microcode */ +#ifdef _LANGUAGE_ASSEMBLY +#define G_TX_LOADTILE 7 +#define G_TX_RENDERTILE 0 + +#define G_TX_NOMIRROR 0 +#define G_TX_WRAP 0 +#define G_TX_MIRROR 0x1 +#define G_TX_CLAMP 0x2 +#define G_TX_NOMASK 0 +#define G_TX_NOLOD 0 +#endif + +/* + * G_SETIMG fmt: set image formats + */ +#define G_IM_FMT_RGBA 0 +#define G_IM_FMT_YUV 1 +#define G_IM_FMT_CI 2 +#define G_IM_FMT_IA 3 +#define G_IM_FMT_I 4 + +/* + * G_SETIMG siz: set image pixel size + */ +#define G_IM_SIZ_4b 0 +#define G_IM_SIZ_8b 1 +#define G_IM_SIZ_16b 2 +#define G_IM_SIZ_32b 3 +#define G_IM_SIZ_DD 5 + +#define G_IM_SIZ_4b_BYTES 0 +#define G_IM_SIZ_4b_TILE_BYTES G_IM_SIZ_4b_BYTES +#define G_IM_SIZ_4b_LINE_BYTES G_IM_SIZ_4b_BYTES + +#define G_IM_SIZ_8b_BYTES 1 +#define G_IM_SIZ_8b_TILE_BYTES G_IM_SIZ_8b_BYTES +#define G_IM_SIZ_8b_LINE_BYTES G_IM_SIZ_8b_BYTES + +#define G_IM_SIZ_16b_BYTES 2 +#define G_IM_SIZ_16b_TILE_BYTES G_IM_SIZ_16b_BYTES +#define G_IM_SIZ_16b_LINE_BYTES G_IM_SIZ_16b_BYTES + +#define G_IM_SIZ_32b_BYTES 4 +#define G_IM_SIZ_32b_TILE_BYTES 2 +#define G_IM_SIZ_32b_LINE_BYTES 2 + +#define G_IM_SIZ_4b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_8b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_16b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_32b_LOAD_BLOCK G_IM_SIZ_32b + +#define G_IM_SIZ_4b_SHIFT 2 +#define G_IM_SIZ_8b_SHIFT 1 +#define G_IM_SIZ_16b_SHIFT 0 +#define G_IM_SIZ_32b_SHIFT 0 + +#define G_IM_SIZ_4b_INCR 3 +#define G_IM_SIZ_8b_INCR 1 +#define G_IM_SIZ_16b_INCR 0 +#define G_IM_SIZ_32b_INCR 0 + +/* + * G_SETCOMBINE: color combine modes + */ +/* Color combiner constants: */ +#define G_CCMUX_COMBINED 0 +#define G_CCMUX_TEXEL0 1 +#define G_CCMUX_TEXEL1 2 +#define G_CCMUX_PRIMITIVE 3 +#define G_CCMUX_SHADE 4 +#define G_CCMUX_ENVIRONMENT 5 +#define G_CCMUX_CENTER 6 +#define G_CCMUX_SCALE 6 +#define G_CCMUX_COMBINED_ALPHA 7 +#define G_CCMUX_TEXEL0_ALPHA 8 +#define G_CCMUX_TEXEL1_ALPHA 9 +#define G_CCMUX_PRIMITIVE_ALPHA 10 +#define G_CCMUX_SHADE_ALPHA 11 +#define G_CCMUX_ENV_ALPHA 12 +#define G_CCMUX_LOD_FRACTION 13 +#define G_CCMUX_PRIM_LOD_FRAC 14 +#define G_CCMUX_NOISE 7 +#define G_CCMUX_K4 7 +#define G_CCMUX_K5 15 +#define G_CCMUX_1 6 +#define G_CCMUX_0 31 + +/* Alpha combiner constants: */ +#define G_ACMUX_COMBINED 0 +#define G_ACMUX_TEXEL0 1 +#define G_ACMUX_TEXEL1 2 +#define G_ACMUX_PRIMITIVE 3 +#define G_ACMUX_SHADE 4 +#define G_ACMUX_ENVIRONMENT 5 +#define G_ACMUX_LOD_FRACTION 0 +#define G_ACMUX_PRIM_LOD_FRAC 6 +#define G_ACMUX_1 6 +#define G_ACMUX_0 7 + +/* typical CC cycle 1 modes */ +#define G_CC_PRIMITIVE 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE +#define G_CC_SHADE 0, 0, 0, SHADE, 0, 0, 0, SHADE +#define G_CC_MODULATEI TEXEL0, 0, SHADE, 0, 0, 0, 0, SHADE +#define G_CC_MODULATEIA TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0 +#define G_CC_MODULATEIDECALA TEXEL0, 0, SHADE, 0, 0, 0, 0, TEXEL0 +#define G_CC_MODULATERGB G_CC_MODULATEI +#define G_CC_MODULATERGBA G_CC_MODULATEIA +#define G_CC_MODULATERGBDECALA G_CC_MODULATEIDECALA +#define G_CC_MODULATEI_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0 +#define G_CC_MODULATEIDECALA_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, TEXEL0 +#define G_CC_MODULATERGB_PRIM G_CC_MODULATEI_PRIM +#define G_CC_MODULATERGBA_PRIM G_CC_MODULATEIA_PRIM +#define G_CC_MODULATERGBDECALA_PRIM G_CC_MODULATEIDECALA_PRIM +#define G_CC_DECALRGB 0, 0, 0, TEXEL0, 0, 0, 0, SHADE +#define G_CC_DECALRGBA 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0 +#define G_CC_BLENDI ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDIA ENVIRONMENT, SHADE, TEXEL0, SHADE, TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDIDECALA ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_BLENDRGBA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDRGBDECALA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_ADDRGB 1, 0, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_ADDRGBDECALA 1, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_REFLECTRGB ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_REFLECTRGBDECALA ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_HILITERGB PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_HILITERGBA PRIMITIVE, SHADE, TEXEL0, SHADE, PRIMITIVE, SHADE, TEXEL0, SHADE +#define G_CC_HILITERGBDECALA PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_SHADEDECALA 0, 0, 0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_BLENDPE PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDPEDECALA PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, TEXEL0 + +/* oddball modes */ +#define _G_CC_BLENDPE ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, SHADE, 0 +#define _G_CC_BLENDPEDECALA ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, 0, 0, 0, TEXEL0 +#define _G_CC_TWOCOLORTEX PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +/* used for 1-cycle sparse mip-maps, primitive color has color of lowest LOD */ +#define _G_CC_SPARSEST PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0, PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0 +#define G_CC_TEMPLERP TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0 + +/* typical CC cycle 1 modes, usually followed by other cycle 2 modes */ +#define G_CC_TRILERP TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0, TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0 +#define G_CC_INTERFERENCE TEXEL0, 0, TEXEL1, 0, TEXEL0, 0, TEXEL1, 0 + +/* + * One-cycle color convert operation + */ +#define G_CC_1CYUV2RGB TEXEL0, K4, K5, TEXEL0, 0, 0, 0, SHADE + +/* + * NOTE: YUV2RGB expects TF step1 color conversion to occur in 2nd clock. + * Therefore, CC looks for step1 results in TEXEL1 + */ +#define G_CC_YUV2RGB TEXEL1, K4, K5, TEXEL1, 0, 0, 0, 0 + +/* typical CC cycle 2 modes */ +#define G_CC_PASS2 0, 0, 0, COMBINED, 0, 0, 0, COMBINED +#define G_CC_MODULATEI2 COMBINED, 0, SHADE, 0, 0, 0, 0, SHADE +#define G_CC_MODULATEIA2 COMBINED, 0, SHADE, 0, COMBINED, 0, SHADE, 0 +#define G_CC_MODULATERGB2 G_CC_MODULATEI2 +#define G_CC_MODULATERGBA2 G_CC_MODULATEIA2 +#define G_CC_MODULATEI_PRIM2 COMBINED, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM2 COMBINED, 0, PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0 +#define G_CC_MODULATERGB_PRIM2 G_CC_MODULATEI_PRIM2 +#define G_CC_MODULATERGBA_PRIM2 G_CC_MODULATEIA_PRIM2 +#define G_CC_DECALRGB2 0, 0, 0, COMBINED, 0, 0, 0, SHADE +/* + * ? +#define G_CC_DECALRGBA2 COMBINED, SHADE, COMBINED_ALPHA, SHADE, 0, 0, 0, SHADE +*/ +#define G_CC_BLENDI2 ENVIRONMENT, SHADE, COMBINED, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDIA2 ENVIRONMENT, SHADE, COMBINED, SHADE, COMBINED, 0, SHADE, 0 +#define G_CC_CHROMA_KEY2 TEXEL0, CENTER, SCALE, 0, 0, 0, 0, 0 +#define G_CC_HILITERGB2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, SHADE +#define G_CC_HILITERGBA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, ENVIRONMENT, COMBINED, TEXEL0, COMBINED +#define G_CC_HILITERGBDECALA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, TEXEL0 +#define G_CC_HILITERGBPASSA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, COMBINED + +/* + * G_SETOTHERMODE_L sft: shift count + */ +#define G_MDSFT_ALPHACOMPARE 0 +#define G_MDSFT_ZSRCSEL 2 +#define G_MDSFT_RENDERMODE 3 +#define G_MDSFT_BLENDER 16 + +/* + * G_SETOTHERMODE_H sft: shift count + */ +#define G_MDSFT_BLENDMASK 0 /* unsupported */ +#define G_MDSFT_ALPHADITHER 4 +#define G_MDSFT_RGBDITHER 6 + +#define G_MDSFT_COMBKEY 8 +#define G_MDSFT_TEXTCONV 9 +#define G_MDSFT_TEXTFILT 12 +#define G_MDSFT_TEXTLUT 14 +#define G_MDSFT_TEXTLOD 16 +#define G_MDSFT_TEXTDETAIL 17 +#define G_MDSFT_TEXTPERSP 19 +#define G_MDSFT_CYCLETYPE 20 +#define G_MDSFT_COLORDITHER 22 /* unsupported in HW 2.0 */ +#define G_MDSFT_PIPELINE 23 + +/* G_SETOTHERMODE_H gPipelineMode */ +#define G_PM_1PRIMITIVE (1 << G_MDSFT_PIPELINE) +#define G_PM_NPRIMITIVE (0 << G_MDSFT_PIPELINE) + +/* G_SETOTHERMODE_H gSetCycleType */ +#define G_CYC_1CYCLE (0 << G_MDSFT_CYCLETYPE) +#define G_CYC_2CYCLE (1 << G_MDSFT_CYCLETYPE) +#define G_CYC_COPY (2 << G_MDSFT_CYCLETYPE) +#define G_CYC_FILL (3 << G_MDSFT_CYCLETYPE) + +/* G_SETOTHERMODE_H gSetTexturePersp */ +#define G_TP_NONE (0 << G_MDSFT_TEXTPERSP) +#define G_TP_PERSP (1 << G_MDSFT_TEXTPERSP) + +/* G_SETOTHERMODE_H gSetTextureDetail */ +#define G_TD_CLAMP (0 << G_MDSFT_TEXTDETAIL) +#define G_TD_SHARPEN (1 << G_MDSFT_TEXTDETAIL) +#define G_TD_DETAIL (2 << G_MDSFT_TEXTDETAIL) + +/* G_SETOTHERMODE_H gSetTextureLOD */ +#define G_TL_TILE (0 << G_MDSFT_TEXTLOD) +#define G_TL_LOD (1 << G_MDSFT_TEXTLOD) + +/* G_SETOTHERMODE_H gSetTextureLUT */ +#define G_TT_NONE (0 << G_MDSFT_TEXTLUT) +#define G_TT_RGBA16 (2 << G_MDSFT_TEXTLUT) +#define G_TT_IA16 (3 << G_MDSFT_TEXTLUT) + +/* G_SETOTHERMODE_H gSetTextureFilter */ +#define G_TF_POINT (0 << G_MDSFT_TEXTFILT) +#define G_TF_AVERAGE (3 << G_MDSFT_TEXTFILT) +#define G_TF_BILERP (2 << G_MDSFT_TEXTFILT) + +/* G_SETOTHERMODE_H gSetTextureConvert */ +#define G_TC_CONV (0 << G_MDSFT_TEXTCONV) +#define G_TC_FILTCONV (5 << G_MDSFT_TEXTCONV) +#define G_TC_FILT (6 << G_MDSFT_TEXTCONV) + +/* G_SETOTHERMODE_H gSetCombineKey */ +#define G_CK_NONE (0 << G_MDSFT_COMBKEY) +#define G_CK_KEY (1 << G_MDSFT_COMBKEY) + +/* G_SETOTHERMODE_H gSetColorDither */ +#define G_CD_MAGICSQ (0 << G_MDSFT_RGBDITHER) +#define G_CD_BAYER (1 << G_MDSFT_RGBDITHER) +#define G_CD_NOISE (2 << G_MDSFT_RGBDITHER) + +#ifndef _HW_VERSION_1 +#define G_CD_DISABLE (3 << G_MDSFT_RGBDITHER) +#define G_CD_ENABLE G_CD_NOISE /* HW 1.0 compatibility mode */ +#else +#define G_CD_ENABLE (1 << G_MDSFT_COLORDITHER) +#define G_CD_DISABLE (0 << G_MDSFT_COLORDITHER) +#endif + +/* G_SETOTHERMODE_H gSetAlphaDither */ +#define G_AD_PATTERN (0 << G_MDSFT_ALPHADITHER) +#define G_AD_NOTPATTERN (1 << G_MDSFT_ALPHADITHER) +#define G_AD_NOISE (2 << G_MDSFT_ALPHADITHER) +#define G_AD_DISABLE (3 << G_MDSFT_ALPHADITHER) + +/* G_SETOTHERMODE_L gSetAlphaCompare */ +#define G_AC_NONE (0 << G_MDSFT_ALPHACOMPARE) +#define G_AC_THRESHOLD (1 << G_MDSFT_ALPHACOMPARE) +#define G_AC_DITHER (3 << G_MDSFT_ALPHACOMPARE) + +/* G_SETOTHERMODE_L gSetDepthSource */ +#define G_ZS_PIXEL (0 << G_MDSFT_ZSRCSEL) +#define G_ZS_PRIM (1 << G_MDSFT_ZSRCSEL) + +/* G_SETOTHERMODE_L gSetRenderMode */ +#define AA_EN 0x8 +#define Z_CMP 0x10 +#define Z_UPD 0x20 +#define IM_RD 0x40 +#define CLR_ON_CVG 0x80 +#define CVG_DST_CLAMP 0 +#define CVG_DST_WRAP 0x100 +#define CVG_DST_FULL 0x200 +#define CVG_DST_SAVE 0x300 +#define ZMODE_OPA 0 +#define ZMODE_INTER 0x400 +#define ZMODE_XLU 0x800 +#define ZMODE_DEC 0xc00 +#define CVG_X_ALPHA 0x1000 +#define ALPHA_CVG_SEL 0x2000 +#define FORCE_BL 0x4000 +#define TEX_EDGE 0x0000 /* used to be 0x8000 */ + +#define G_BL_CLR_IN 0 +#define G_BL_CLR_MEM 1 +#define G_BL_CLR_BL 2 +#define G_BL_CLR_FOG 3 +#define G_BL_1MA 0 +#define G_BL_A_MEM 1 +#define G_BL_A_IN 0 +#define G_BL_A_FOG 1 +#define G_BL_A_SHADE 2 +#define G_BL_1 2 +#define G_BL_0 3 + +#define GBL_c1(m1a, m1b, m2a, m2b) (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18 +#define GBL_c2(m1a, m1b, m2a, m2b) (m1a) << 28 | (m1b) << 24 | (m2a) << 20 | (m2b) << 16 + +#define RM_AA_ZB_OPA_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_SURF(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_DECAL(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ALPHA_CVG_SEL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_DECAL(clk) \ + AA_EN | Z_CMP | CVG_DST_WRAP | ALPHA_CVG_SEL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_DECAL(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ALPHA_CVG_SEL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | ALPHA_CVG_SEL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_INTER(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_XLU_LINE(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_DEC_LINE(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_SAVE | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_TEX_EDGE(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_TEX_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_INTER | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_SUB_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_PCL_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | G_AC_DITHER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_TEX_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_SUB_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_OPA_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_OPA_SURF(clk) \ + AA_EN | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_XLU_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_XLU_LINE(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_DEC_LINE(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_TEX_EDGE(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_SUB_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_PCL_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_OPA_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_TEX_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_SUB_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OPA_SURF(clk) \ + Z_CMP | Z_UPD | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_ZB_XLU_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_XLU | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OPA_DECAL(clk) \ + Z_CMP | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_ZB_XLU_DECAL(clk) \ + Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_CLD_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_XLU | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OVL_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_DEC | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_PCL_SURF(clk) \ + Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_OPA_SURF(clk) CVG_DST_CLAMP | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_XLU_SURF(clk) \ + IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_TEX_EDGE(clk) \ + CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | TEX_EDGE | AA_EN | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_CLD_SURF(clk) \ + IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_PCL_SURF(clk) \ + CVG_DST_FULL | FORCE_BL | ZMODE_OPA | G_AC_DITHER | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_ADD(clk) \ + IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1) + +#define RM_NOOP(clk) GBL_c##clk(0, 0, 0, 0) + +#define RM_VISCVG(clk) IM_RD | FORCE_BL | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_BL, G_BL_A_MEM) + +/* for rendering to an 8-bit framebuffer */ +#define RM_OPA_CI(clk) CVG_DST_CLAMP | ZMODE_OPA | GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define G_RM_AA_ZB_OPA_SURF RM_AA_ZB_OPA_SURF(1) +#define G_RM_AA_ZB_OPA_SURF2 RM_AA_ZB_OPA_SURF(2) +#define G_RM_AA_ZB_XLU_SURF RM_AA_ZB_XLU_SURF(1) +#define G_RM_AA_ZB_XLU_SURF2 RM_AA_ZB_XLU_SURF(2) +#define G_RM_AA_ZB_OPA_DECAL RM_AA_ZB_OPA_DECAL(1) +#define G_RM_AA_ZB_OPA_DECAL2 RM_AA_ZB_OPA_DECAL(2) +#define G_RM_AA_ZB_XLU_DECAL RM_AA_ZB_XLU_DECAL(1) +#define G_RM_AA_ZB_XLU_DECAL2 RM_AA_ZB_XLU_DECAL(2) +#define G_RM_AA_ZB_OPA_INTER RM_AA_ZB_OPA_INTER(1) +#define G_RM_AA_ZB_OPA_INTER2 RM_AA_ZB_OPA_INTER(2) +#define G_RM_AA_ZB_XLU_INTER RM_AA_ZB_XLU_INTER(1) +#define G_RM_AA_ZB_XLU_INTER2 RM_AA_ZB_XLU_INTER(2) +#define G_RM_AA_ZB_XLU_LINE RM_AA_ZB_XLU_LINE(1) +#define G_RM_AA_ZB_XLU_LINE2 RM_AA_ZB_XLU_LINE(2) +#define G_RM_AA_ZB_DEC_LINE RM_AA_ZB_DEC_LINE(1) +#define G_RM_AA_ZB_DEC_LINE2 RM_AA_ZB_DEC_LINE(2) +#define G_RM_AA_ZB_TEX_EDGE RM_AA_ZB_TEX_EDGE(1) +#define G_RM_AA_ZB_TEX_EDGE2 RM_AA_ZB_TEX_EDGE(2) +#define G_RM_AA_ZB_TEX_INTER RM_AA_ZB_TEX_INTER(1) +#define G_RM_AA_ZB_TEX_INTER2 RM_AA_ZB_TEX_INTER(2) +#define G_RM_AA_ZB_SUB_SURF RM_AA_ZB_SUB_SURF(1) +#define G_RM_AA_ZB_SUB_SURF2 RM_AA_ZB_SUB_SURF(2) +#define G_RM_AA_ZB_PCL_SURF RM_AA_ZB_PCL_SURF(1) +#define G_RM_AA_ZB_PCL_SURF2 RM_AA_ZB_PCL_SURF(2) +#define G_RM_AA_ZB_OPA_TERR RM_AA_ZB_OPA_TERR(1) +#define G_RM_AA_ZB_OPA_TERR2 RM_AA_ZB_OPA_TERR(2) +#define G_RM_AA_ZB_TEX_TERR RM_AA_ZB_TEX_TERR(1) +#define G_RM_AA_ZB_TEX_TERR2 RM_AA_ZB_TEX_TERR(2) +#define G_RM_AA_ZB_SUB_TERR RM_AA_ZB_SUB_TERR(1) +#define G_RM_AA_ZB_SUB_TERR2 RM_AA_ZB_SUB_TERR(2) + +#define G_RM_RA_ZB_OPA_SURF RM_RA_ZB_OPA_SURF(1) +#define G_RM_RA_ZB_OPA_SURF2 RM_RA_ZB_OPA_SURF(2) +#define G_RM_RA_ZB_OPA_DECAL RM_RA_ZB_OPA_DECAL(1) +#define G_RM_RA_ZB_OPA_DECAL2 RM_RA_ZB_OPA_DECAL(2) +#define G_RM_RA_ZB_OPA_INTER RM_RA_ZB_OPA_INTER(1) +#define G_RM_RA_ZB_OPA_INTER2 RM_RA_ZB_OPA_INTER(2) + +#define G_RM_AA_OPA_SURF RM_AA_OPA_SURF(1) +#define G_RM_AA_OPA_SURF2 RM_AA_OPA_SURF(2) +#define G_RM_AA_XLU_SURF RM_AA_XLU_SURF(1) +#define G_RM_AA_XLU_SURF2 RM_AA_XLU_SURF(2) +#define G_RM_AA_XLU_LINE RM_AA_XLU_LINE(1) +#define G_RM_AA_XLU_LINE2 RM_AA_XLU_LINE(2) +#define G_RM_AA_DEC_LINE RM_AA_DEC_LINE(1) +#define G_RM_AA_DEC_LINE2 RM_AA_DEC_LINE(2) +#define G_RM_AA_TEX_EDGE RM_AA_TEX_EDGE(1) +#define G_RM_AA_TEX_EDGE2 RM_AA_TEX_EDGE(2) +#define G_RM_AA_SUB_SURF RM_AA_SUB_SURF(1) +#define G_RM_AA_SUB_SURF2 RM_AA_SUB_SURF(2) +#define G_RM_AA_PCL_SURF RM_AA_PCL_SURF(1) +#define G_RM_AA_PCL_SURF2 RM_AA_PCL_SURF(2) +#define G_RM_AA_OPA_TERR RM_AA_OPA_TERR(1) +#define G_RM_AA_OPA_TERR2 RM_AA_OPA_TERR(2) +#define G_RM_AA_TEX_TERR RM_AA_TEX_TERR(1) +#define G_RM_AA_TEX_TERR2 RM_AA_TEX_TERR(2) +#define G_RM_AA_SUB_TERR RM_AA_SUB_TERR(1) +#define G_RM_AA_SUB_TERR2 RM_AA_SUB_TERR(2) + +#define G_RM_RA_OPA_SURF RM_RA_OPA_SURF(1) +#define G_RM_RA_OPA_SURF2 RM_RA_OPA_SURF(2) + +#define G_RM_ZB_OPA_SURF RM_ZB_OPA_SURF(1) +#define G_RM_ZB_OPA_SURF2 RM_ZB_OPA_SURF(2) +#define G_RM_ZB_XLU_SURF RM_ZB_XLU_SURF(1) +#define G_RM_ZB_XLU_SURF2 RM_ZB_XLU_SURF(2) +#define G_RM_ZB_OPA_DECAL RM_ZB_OPA_DECAL(1) +#define G_RM_ZB_OPA_DECAL2 RM_ZB_OPA_DECAL(2) +#define G_RM_ZB_XLU_DECAL RM_ZB_XLU_DECAL(1) +#define G_RM_ZB_XLU_DECAL2 RM_ZB_XLU_DECAL(2) +#define G_RM_ZB_CLD_SURF RM_ZB_CLD_SURF(1) +#define G_RM_ZB_CLD_SURF2 RM_ZB_CLD_SURF(2) +#define G_RM_ZB_OVL_SURF RM_ZB_OVL_SURF(1) +#define G_RM_ZB_OVL_SURF2 RM_ZB_OVL_SURF(2) +#define G_RM_ZB_PCL_SURF RM_ZB_PCL_SURF(1) +#define G_RM_ZB_PCL_SURF2 RM_ZB_PCL_SURF(2) + +#define G_RM_OPA_SURF RM_OPA_SURF(1) +#define G_RM_OPA_SURF2 RM_OPA_SURF(2) +#define G_RM_XLU_SURF RM_XLU_SURF(1) +#define G_RM_XLU_SURF2 RM_XLU_SURF(2) +#define G_RM_CLD_SURF RM_CLD_SURF(1) +#define G_RM_CLD_SURF2 RM_CLD_SURF(2) +#define G_RM_TEX_EDGE RM_TEX_EDGE(1) +#define G_RM_TEX_EDGE2 RM_TEX_EDGE(2) +#define G_RM_PCL_SURF RM_PCL_SURF(1) +#define G_RM_PCL_SURF2 RM_PCL_SURF(2) +#define G_RM_ADD RM_ADD(1) +#define G_RM_ADD2 RM_ADD(2) +#define G_RM_NOOP RM_NOOP(1) +#define G_RM_NOOP2 RM_NOOP(2) +#define G_RM_VISCVG RM_VISCVG(1) +#define G_RM_VISCVG2 RM_VISCVG(2) +#define G_RM_OPA_CI RM_OPA_CI(1) +#define G_RM_OPA_CI2 RM_OPA_CI(2) + +#define G_RM_FOG_SHADE_A GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA) +#define G_RM_FOG_PRIM_A GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_IN, G_BL_1MA) +#define G_RM_PASS GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +/* + * G_SETCONVERT: K0-5 + */ +#define G_CV_K0 175 +#define G_CV_K1 -43 +#define G_CV_K2 -89 +#define G_CV_K3 222 +#define G_CV_K4 114 +#define G_CV_K5 42 + +/* + * G_SETSCISSOR: interlace mode + */ +#define G_SC_NON_INTERLACE 0 +#define G_SC_ODD_INTERLACE 3 +#define G_SC_EVEN_INTERLACE 2 + +/* flags to inhibit pushing of the display list (on branch) */ +#define G_DL_PUSH 0x00 +#define G_DL_NOPUSH 0x01 + +#if defined(_MSC_VER) || defined(__GNUC__) +#ifndef _LANGUAGE_C +#define _LANGUAGE_C +#endif +#endif + +/* + * BEGIN C-specific section: (typedef's) + */ +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/* + * Data Structures + * + * NOTE: + * The DMA transfer hardware requires 64-bit aligned, 64-bit multiple- + * sized transfers. This important hardware optimization is unfortunately + * reflected in the programming interface, with some structures + * padded and alignment enforced. + * + * Since structures are aligned to the boundary of the "worst-case" + * element, we can't depend on the C compiler to align things + * properly. + * + * 64-bit structure alignment is enforced by wrapping structures with + * unions that contain a dummy "long long int". Why this works is + * explained in the ANSI C Spec, or on page 186 of the second edition + * of K&R, "The C Programming Language". + * + * The price we pay for this is a little awkwardness referencing the + * structures through the union. There is no memory penalty, since + * all the structures are at least 64-bits the dummy alignment field + * does not increase the size of the union. + * + * Static initialization of these union structures works because + * the ANSI C spec states that static initialization for unions + * works by using the first union element. We put the dummy alignment + * field last for this reason. + * + * (it's possible a newer 64-bit compiler from MIPS might make this + * easier with a flag, but we can't wait for it...) + * + */ + +/* + * Vertex (set up for use with colors) + */ +typedef struct { +#ifndef GBI_FLOATS + short ob[3]; /* x, y, z */ +#else + float ob[3]; /* x, y, z */ +#endif + unsigned short flag; + short tc[2]; /* texture coord */ + unsigned char cn[4]; /* color & alpha */ +} Vtx_t; + +/* + * Vertex (set up for use with normals) + */ +typedef struct { +#ifndef GBI_FLOATS + short ob[3]; /* x, y, z */ +#else + float ob[3]; /* x, y, z */ +#endif + unsigned short flag; + short tc[2]; /* texture coord */ + signed char n[3]; /* normal */ + unsigned char a; /* alpha */ +} Vtx_tn; + +typedef union Vtx { + Vtx_t v; /* Use this one for colors */ + Vtx_tn n; /* Use this one for normals */ + long long int force_structure_alignment; +} Vtx; + +/* + * Sprite structure + */ + +typedef struct { + void* SourceImagePointer; + void* TlutPointer; + short Stride; + short SubImageWidth; + short SubImageHeight; + char SourceImageType; + char SourceImageBitSize; + short SourceImageOffsetS; + short SourceImageOffsetT; + /* 20 bytes for above */ + + /* padding to bring structure size to 64 bit allignment */ + char dummy[4]; + +} uSprite_t; + +typedef union { + uSprite_t s; + + /* Need to make sure this is 64 bit aligned */ + long long int force_structure_allignment[3]; +} uSprite; + +/* + * Triangle face + */ +typedef struct { + unsigned char flag; + unsigned char v[3]; +} Tri; + +/* + * Viewport + */ + +/* + * + * This magic value is the maximum INTEGER z-range of the hardware + * (there are also 16-bits of fraction, which are introduced during + * any transformations). This is not just a good idea, it's the law. + * Feeding the hardware eventual z-coordinates (after any transforms + * or scaling) bigger than this, will not work. + * + * This number is DIFFERENT than G_MAXFBZ, which is the maximum value + * you want to use to initialize the z-buffer. + * + * The reason these are different is mildly interesting, but too long + * to explain here. It is basically the result of optimizations in the + * hardware. A more generic API might hide this detail from the users, + * but we don't have the ucode to do that... + * + */ +#define G_MAXZ 0x03ff /* 10 bits of integer screen-Z precision */ + +/* + * The viewport structure elements have 2 bits of fraction, necessary + * to accomodate the sub-pixel positioning scaling for the hardware. + * This can also be exploited to handle odd-sized viewports. + * + * Accounting for these fractional bits, using the default projection + * and viewing matrices, the viewport structure is initialized thusly: + * + * (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, G_MAXZ, 0, + * (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, 0, 0, + */ +typedef struct { + short vscale[4]; /* scale, 2 bits fraction */ + short vtrans[4]; /* translate, 2 bits fraction */ + /* both the above arrays are padded to 64-bit boundary */ +} Vp_t; + +typedef union { + Vp_t vp; + long long int force_structure_alignment; +} Vp; + +/* + * MOVEMEM indices + * + * Each of these indexes an entry in a dmem table + * which points to a 1-4 word block of dmem in + * which to store a 1-4 word DMA. + * + */ +#ifdef F3DEX_GBI_2 +/* 0,4 are reserved by G_MTX */ +#define G_MV_MMTX 2 +#define G_MV_PMTX 6 +#define G_MV_VIEWPORT 8 +#define G_MV_LIGHT 10 +#define G_MV_POINT 12 +#define G_MV_MATRIX 14 /* NOTE: this is in moveword table */ +#define G_MVO_LOOKATX (0 * 24) +#define G_MVO_LOOKATY (1 * 24) +#define G_MVO_L0 (2 * 24) +#define G_MVO_L1 (3 * 24) +#define G_MVO_L2 (4 * 24) +#define G_MVO_L3 (5 * 24) +#define G_MVO_L4 (6 * 24) +#define G_MVO_L5 (7 * 24) +#define G_MVO_L6 (8 * 24) +#define G_MVO_L7 (9 * 24) +#else /* F3DEX_GBI_2 */ +#define G_MV_VIEWPORT 0x80 +#define G_MV_LOOKATY 0x82 +#define G_MV_LOOKATX 0x84 +#define G_MV_L0 0x86 +#define G_MV_L1 0x88 +#define G_MV_L2 0x8a +#define G_MV_L3 0x8c +#define G_MV_L4 0x8e +#define G_MV_L5 0x90 +#define G_MV_L6 0x92 +#define G_MV_L7 0x94 +#define G_MV_TXTATT 0x96 +#define G_MV_MATRIX_1 0x9e /* NOTE: this is in moveword table */ +#define G_MV_MATRIX_2 0x98 +#define G_MV_MATRIX_3 0x9a +#define G_MV_MATRIX_4 0x9c +#endif /* F3DEX_GBI_2 */ + +/* + * MOVEWORD indices + * + * Each of these indexes an entry in a dmem table + * which points to a word in dmem in dmem where + * an immediate word will be stored. + * + */ +#define G_MW_MATRIX 0x00 /* NOTE: also used by movemem */ +#define G_MW_NUMLIGHT 0x02 +#define G_MW_CLIP 0x04 +#define G_MW_SEGMENT 0x06 +#define G_MW_SEGMENT_INTERP 0x07 +#define G_MW_FOG 0x08 +#define G_MW_LIGHTCOL 0x0a +#ifdef F3DEX_GBI_2 +#define G_MW_FORCEMTX 0x0c +#else /* F3DEX_GBI_2 */ +#define G_MW_POINTS 0x0c +#endif /* F3DEX_GBI_2 */ +#define G_MW_PERSPNORM 0x0e + +/* + * These are offsets from the address in the dmem table + */ +#define G_MWO_NUMLIGHT 0x00 +#define G_MWO_CLIP_RNX 0x04 +#define G_MWO_CLIP_RNY 0x0c +#define G_MWO_CLIP_RPX 0x14 +#define G_MWO_CLIP_RPY 0x1c +#define G_MWO_SEGMENT_0 0x00 +#define G_MWO_SEGMENT_1 0x01 +#define G_MWO_SEGMENT_2 0x02 +#define G_MWO_SEGMENT_3 0x03 +#define G_MWO_SEGMENT_4 0x04 +#define G_MWO_SEGMENT_5 0x05 +#define G_MWO_SEGMENT_6 0x06 +#define G_MWO_SEGMENT_7 0x07 +#define G_MWO_SEGMENT_8 0x08 +#define G_MWO_SEGMENT_9 0x09 +#define G_MWO_SEGMENT_A 0x0a +#define G_MWO_SEGMENT_B 0x0b +#define G_MWO_SEGMENT_C 0x0c +#define G_MWO_SEGMENT_D 0x0d +#define G_MWO_SEGMENT_E 0x0e +#define G_MWO_SEGMENT_F 0x0f +#define G_MWO_FOG 0x00 +#define G_MWO_aLIGHT_1 0x00 +#define G_MWO_bLIGHT_1 0x04 +#ifdef F3DEX_GBI_2 +#define G_MWO_aLIGHT_2 0x18 +#define G_MWO_bLIGHT_2 0x1c +#define G_MWO_aLIGHT_3 0x30 +#define G_MWO_bLIGHT_3 0x34 +#define G_MWO_aLIGHT_4 0x48 +#define G_MWO_bLIGHT_4 0x4c +#define G_MWO_aLIGHT_5 0x60 +#define G_MWO_bLIGHT_5 0x64 +#define G_MWO_aLIGHT_6 0x78 +#define G_MWO_bLIGHT_6 0x7c +#define G_MWO_aLIGHT_7 0x90 +#define G_MWO_bLIGHT_7 0x94 +#define G_MWO_aLIGHT_8 0xa8 +#define G_MWO_bLIGHT_8 0xac +#else +#define G_MWO_aLIGHT_2 0x20 +#define G_MWO_bLIGHT_2 0x24 +#define G_MWO_aLIGHT_3 0x40 +#define G_MWO_bLIGHT_3 0x44 +#define G_MWO_aLIGHT_4 0x60 +#define G_MWO_bLIGHT_4 0x64 +#define G_MWO_aLIGHT_5 0x80 +#define G_MWO_bLIGHT_5 0x84 +#define G_MWO_aLIGHT_6 0xa0 +#define G_MWO_bLIGHT_6 0xa4 +#define G_MWO_aLIGHT_7 0xc0 +#define G_MWO_bLIGHT_7 0xc4 +#define G_MWO_aLIGHT_8 0xe0 +#define G_MWO_bLIGHT_8 0xe4 +#endif +#define G_MWO_MATRIX_XX_XY_I 0x00 +#define G_MWO_MATRIX_XZ_XW_I 0x04 +#define G_MWO_MATRIX_YX_YY_I 0x08 +#define G_MWO_MATRIX_YZ_YW_I 0x0c +#define G_MWO_MATRIX_ZX_ZY_I 0x10 +#define G_MWO_MATRIX_ZZ_ZW_I 0x14 +#define G_MWO_MATRIX_WX_WY_I 0x18 +#define G_MWO_MATRIX_WZ_WW_I 0x1c +#define G_MWO_MATRIX_XX_XY_F 0x20 +#define G_MWO_MATRIX_XZ_XW_F 0x24 +#define G_MWO_MATRIX_YX_YY_F 0x28 +#define G_MWO_MATRIX_YZ_YW_F 0x2c +#define G_MWO_MATRIX_ZX_ZY_F 0x30 +#define G_MWO_MATRIX_ZZ_ZW_F 0x34 +#define G_MWO_MATRIX_WX_WY_F 0x38 +#define G_MWO_MATRIX_WZ_WW_F 0x3c +#define G_MWO_POINT_RGBA 0x10 +#define G_MWO_POINT_ST 0x14 +#define G_MWO_POINT_XYSCREEN 0x18 +#define G_MWO_POINT_ZSCREEN 0x1c + +/* + * Light structure. + * + * Note: only directional (infinite) lights are currently supported. + * + * Note: the weird order is for the DMEM alignment benefit of + * the microcode. + * + */ + +typedef struct { + unsigned char col[3]; /* diffuse light value (rgba) */ + char pad1; + unsigned char colc[3]; /* copy of diffuse light value (rgba) */ + char pad2; + signed char dir[3]; /* direction of light (normalized) */ + char pad3; +} Light_t; + +typedef struct { + unsigned char col[3]; + unsigned char unk3; + unsigned char colc[3]; + unsigned char unk7; + short pos[3]; + unsigned char unkE; +} PointLight_t; + +typedef struct { + unsigned char col[3]; /* ambient light value (rgba) */ + char pad1; + unsigned char colc[3]; /* copy of ambient light value (rgba) */ + char pad2; +} Ambient_t; + +typedef struct { + int x1, y1, x2, y2; /* texture offsets for highlight 1/2 */ +} Hilite_t; + +typedef union { + Light_t l; + PointLight_t p; + long long int force_structure_alignment[2]; +} Light; + +typedef union { + Ambient_t l; + long long int force_structure_alignment[1]; +} Ambient; + +typedef struct { + Ambient a; + Light l[7]; +} Lightsn; + +typedef struct { + Ambient a; + Light l[1]; +} Lights0; + +typedef struct { + Ambient a; + Light l[1]; +} Lights1; + +typedef struct { + Ambient a; + Light l[2]; +} Lights2; + +typedef struct { + Ambient a; + Light l[3]; +} Lights3; + +typedef struct { + Ambient a; + Light l[4]; +} Lights4; + +typedef struct { + Ambient a; + Light l[5]; +} Lights5; + +typedef struct { + Ambient a; + Light l[6]; +} Lights6; + +typedef struct { + Ambient a; + Light l[7]; +} Lights7; + +typedef struct { + Light l[2]; +} LookAt; + +typedef union { + Hilite_t h; + long int force_structure_alignment[4]; +} Hilite; + +#define gdSPDefLights0(ar, ag, ab) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { \ + { { 0, 0, 0 }, 0, { 0, 0, 0 }, 0, { 0, 0, 0 }, 0 } \ + } \ + } \ + } +#define gdSPDefLights1(ar, ag, ab, r1, g1, b1, x1, y1, z1) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { \ + { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } \ + } \ + } \ + } +#define gdSPDefLights2(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } }, { \ + { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } \ + } \ + } \ + } +#define gdSPDefLights3(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } }, \ + { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } }, { \ + { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } \ + } \ + } \ + } +#define gdSPDefLights4(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \ + x4, y4, z4) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } }, \ + { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } }, \ + { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } }, { \ + { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 } \ + } \ + } \ + } +#define gdSPDefLights5(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \ + x4, y4, z4, r5, g5, b5, x5, y5, z5) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } }, \ + { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } }, \ + { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } }, \ + { { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 } }, { \ + { { r5, g5, b5 }, 0, { r5, g5, b5 }, 0, { x5, y5, z5 }, 0 } \ + } \ + } \ + } + +#define gdSPDefLights6(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \ + x4, y4, z4, r5, g5, b5, x5, y5, z5, r6, g6, b6, x6, y6, z6) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } }, \ + { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } }, \ + { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } }, \ + { { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 } }, \ + { { { r5, g5, b5 }, 0, { r5, g5, b5 }, 0, { x5, y5, z5 }, 0 } }, { \ + { { r6, g6, b6 }, 0, { r6, g6, b6 }, 0, { x6, y6, z6 }, 0 } \ + } \ + } \ + } + +#define gdSPDefLights7(ar, ag, ab, r1, g1, b1, x1, y1, z1, r2, g2, b2, x2, y2, z2, r3, g3, b3, x3, y3, z3, r4, g4, b4, \ + x4, y4, z4, r5, g5, b5, x5, y5, z5, r6, g6, b6, x6, y6, z6, r7, g7, b7, x7, y7, z7) \ + { \ + { { { ar, ag, ab }, 0, { ar, ag, ab }, 0 } }, { \ + { { { r1, g1, b1 }, 0, { r1, g1, b1 }, 0, { x1, y1, z1 }, 0 } }, \ + { { { r2, g2, b2 }, 0, { r2, g2, b2 }, 0, { x2, y2, z2 }, 0 } }, \ + { { { r3, g3, b3 }, 0, { r3, g3, b3 }, 0, { x3, y3, z3 }, 0 } }, \ + { { { r4, g4, b4 }, 0, { r4, g4, b4 }, 0, { x4, y4, z4 }, 0 } }, \ + { { { r5, g5, b5 }, 0, { r5, g5, b5 }, 0, { x5, y5, z5 }, 0 } }, \ + { { { r6, g6, b6 }, 0, { r6, g6, b6 }, 0, { x6, y6, z6 }, 0 } }, { \ + { { r7, g7, b7 }, 0, { r7, g7, b7 }, 0, { x7, y7, z7 }, 0 } \ + } \ + } \ + } + +#define gdSPDefLookAt(rightx, righty, rightz, upx, upy, upz) \ + { \ + { \ + { { { 0, 0, 0 }, 0, { 0, 0, 0 }, 0, { rightx, righty, rightz }, 0 } }, { \ + { { 0, 0x80, 0 }, 0, { 0, 0x80, 0 }, 0, { upx, upy, upz }, 0 } \ + } \ + } \ + } + +/* + * Graphics DMA Packet + */ +typedef struct { + int cmd : 8; + unsigned int par : 8; + unsigned int len : 16; + unsigned int addr; +} Gdma; + +/* + * Graphics Immediate Mode Packet types + */ +typedef struct { + int cmd : 8; + int pad : 24; + Tri tri; +} Gtri; + +typedef struct { + int cmd : 8; + int pad1 : 24; + int pad2 : 24; + unsigned char param : 8; +} Gpopmtx; + +/* + * typedef struct { + * int cmd:8; + * int pad0:24; + * int pad1:4; + * int number:4; + * int base:24; + * } Gsegment; + */ +typedef struct { + int cmd : 8; + int pad0 : 8; + int mw_index : 8; + int number : 8; + int pad1 : 8; + int base : 24; +} Gsegment; + +typedef struct { + int cmd : 8; + int pad0 : 8; + int sft : 8; + int len : 8; + unsigned int data : 32; +} GsetothermodeL; + +typedef struct { + int cmd : 8; + int pad0 : 8; + int sft : 8; + int len : 8; + unsigned int data : 32; +} GsetothermodeH; + +typedef struct { + unsigned char cmd; + unsigned char lodscale; + unsigned char tile; + unsigned char on; + unsigned short s; + unsigned short t; +} Gtexture; + +typedef struct { + int cmd : 8; + int pad : 24; + Tri line; +} Gline3D; + +typedef struct { + int cmd : 8; + int pad1 : 24; + short int pad2; + short int scale; +} Gperspnorm; + +/* + * RDP Packet types + */ +typedef struct { + int cmd : 8; + unsigned int fmt : 3; + unsigned int siz : 2; + unsigned int pad : 7; + unsigned int wd : 12; /* really only 10 bits, extra */ + unsigned int dram; /* to account for 1024 */ +} Gsetimg; + +typedef struct { + int cmd : 8; + unsigned int muxs0 : 24; + unsigned int muxs1 : 32; +} Gsetcombine; + +typedef struct { + int cmd : 8; + unsigned char pad; + unsigned char prim_min_level; + unsigned char prim_level; + unsigned long color; +} Gsetcolor; + +typedef struct { + int cmd : 8; + int x0 : 10; + int x0frac : 2; + int y0 : 10; + int y0frac : 2; + unsigned int pad : 8; + int x1 : 10; + int x1frac : 2; + int y1 : 10; + int y1frac : 2; +} Gfillrect; + +typedef struct { + int cmd : 8; + unsigned int fmt : 3; + unsigned int siz : 2; + unsigned int pad0 : 1; + unsigned int line : 9; + unsigned int tmem : 9; + unsigned int pad1 : 5; + unsigned int tile : 3; + unsigned int palette : 4; + unsigned int ct : 1; + unsigned int mt : 1; + unsigned int maskt : 4; + unsigned int shiftt : 4; + unsigned int cs : 1; + unsigned int ms : 1; + unsigned int masks : 4; + unsigned int shifts : 4; +} Gsettile; + +typedef struct { + int cmd : 8; + unsigned int sl : 12; + unsigned int tl : 12; + int pad : 5; + unsigned int tile : 3; + unsigned int sh : 12; + unsigned int th : 12; +} Gloadtile; + +typedef Gloadtile Gloadblock; + +typedef Gloadtile Gsettilesize; + +typedef Gloadtile Gloadtlut; + +typedef struct { + unsigned int cmd : 8; /* command */ + unsigned int xl : 12; /* X coordinate of upper left */ + unsigned int yl : 12; /* Y coordinate of upper left */ + unsigned int pad1 : 5; /* Padding */ + unsigned int tile : 3; /* Tile descriptor index */ + unsigned int xh : 12; /* X coordinate of lower right */ + unsigned int yh : 12; /* Y coordinate of lower right */ + unsigned int s : 16; /* S texture coord at top left */ + unsigned int t : 16; /* T texture coord at top left */ + unsigned int dsdx : 16; /* Change in S per change in X */ + unsigned int dtdy : 16; /* Change in T per change in Y */ +} Gtexrect; + +#define MakeTexRect(xh, yh, flip, tile, xl, yl, s, t, dsdx, dtdy) \ + G_TEXRECT, xh, yh, 0, flip, 0, tile, xl, yl, s, t, dsdx, dtdy + +/* + * Textured rectangles are 128 bits not 64 bits + */ +typedef struct { + unsigned long w0; + unsigned long w1; + unsigned long w2; + unsigned long w3; +} TexRect; + +#ifdef USE_GBI_TRACE +/* + * Trace structure + */ +typedef struct { + const char* file; + int idx; + bool valid; +} Trace; + +#define MakeTrace() \ + , { \ + __FILE__, __LINE__, true \ + } +#else +#define MakeTrace() +#endif +/* + * Generic Gfx Packet + */ +typedef struct { + uintptr_t w0; + uintptr_t w1; +#ifdef USE_GBI_TRACE + Trace trace; +#endif +} Gwords; + +#ifdef __cplusplus +#ifdef USE_GBI_TRACE +static_assert((sizeof(Gwords) == 2 * sizeof(void*) + sizeof(Trace)), "Display list size is bad"); +#else +static_assert((sizeof(Gwords) == 2 * sizeof(void*)), "Display list size is bad"); +#endif +#endif + +/* + * This union is the fundamental type of the display list. + * It is, by law, exactly 64 bits in size. + */ +typedef union Gfx { + Gwords words; +#if !defined(F3D_OLD) && IS_BIG_ENDIAN && !IS_64_BIT + Gdma dma; + Gtri tri; + Gline3D line; + Gpopmtx popmtx; + Gsegment segment; + GsetothermodeH setothermodeH; + GsetothermodeL setothermodeL; + Gtexture texture; + Gperspnorm perspnorm; + Gsetimg setimg; + Gsetcombine setcombine; + Gsetcolor setcolor; + Gfillrect fillrect; /* use for setscissor also */ + Gsettile settile; + Gloadtile loadtile; /* use for loadblock also, th is dxt */ + Gsettilesize settilesize; + Gloadtlut loadtlut; +#endif + long long int force_structure_alignment; +} Gfx; + +/* + * Macros to assemble the graphics display list + */ + +/* + * OTR macros + */ + +#define gsSP1TriangleOTR(v0, v1, v2, flag) \ + { _SHIFTL(G_TRI1_OTR, 24, 8) | __gsSP1Triangle_w1f(v0, v1, v2, flag), 0 } + +/* + * DMA macros + */ +#define gDma0p(pkt, c, s, l) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24); \ + _g->words.w1 = (uintptr_t)(s); \ + }) + +#define gsDma0p(c, s, l) \ + { _SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24), (uintptr_t)(s) } + +#ifdef USE_GBI_TRACE +#define gDma1p(pkt, c, s, l, p) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | _SHIFTL((l), 0, 16)); \ + _g->words.w1 = (uintptr_t)(s); \ + _g->words.trace.file = __FILE__; \ + _g->words.trace.idx = __LINE__; \ + _g->words.trace.valid = true; \ + }) +#else +#define gDma1p(pkt, c, s, l, p) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | _SHIFTL((l), 0, 16)); \ + _g->words.w1 = (uintptr_t)(s); \ + }) +#endif +#define gsDma1p(c, s, l, p) \ + { (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | _SHIFTL((l), 0, 16)), (uintptr_t)(s)MakeTrace() } + +#ifdef USE_GBI_TRACE +#define gDma2p(pkt, c, adrs, len, idx, ofs) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = \ + (_SHIFTL((c), 24, 8) | _SHIFTL(((len)-1) / 8, 19, 5) | _SHIFTL((ofs) / 8, 8, 8) | _SHIFTL((idx), 0, 8)); \ + _g->words.w1 = (uintptr_t)(adrs); \ + _g->words.trace.file = __FILE__; \ + _g->words.trace.idx = __LINE__; \ + _g->words.trace.valid = true; \ + }) +#else +#define gDma2p(pkt, c, adrs, len, idx, ofs) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = \ + (_SHIFTL((c), 24, 8) | _SHIFTL(((len)-1) / 8, 19, 5) | _SHIFTL((ofs) / 8, 8, 8) | _SHIFTL((idx), 0, 8)); \ + _g->words.w1 = (uintptr_t)(adrs); \ + }) +#endif + +#define gsDma2p(c, adrs, len, idx, ofs) \ + { \ + (_SHIFTL((c), 24, 8) | _SHIFTL(((len)-1) / 8, 19, 5) | _SHIFTL((ofs) / 8, 8, 8) | _SHIFTL((idx), 0, 8)), \ + (uintptr_t)(adrs)MakeTrace() \ + } + +#define gSPNoOp(pkt) gDma0p(pkt, G_SPNOOP, 0, 0) +#define gsSPNoOp() gsDma0p(G_SPNOOP, 0, 0) + +#ifdef F3DEX_GBI_2 +#define gSPMatrix(pkt, m, p) gDma2p((pkt), G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH, 0) +#define gsSPMatrix(m, p) gsDma2p(G_MTX, (m), sizeof(Mtx), (p) ^ G_MTX_PUSH, 0) +#else /* F3DEX_GBI_2 */ +#define gSPMatrix(pkt, m, p) gDma1p(pkt, G_MTX, m, sizeof(Mtx), p) +#define gsSPMatrix(m, p) gsDma1p(G_MTX, m, sizeof(Mtx), p) +#endif /* F3DEX_GBI_2 */ + +#if defined(F3DEX_GBI_2) +/* + * F3DEX_GBI_2: G_VTX GBI format was changed. + * + * +--------+----+---+---+----+------+-+ + * G_VTX | cmd:8 |0000| n:8 |0000|v0+n:7|0| + * +-+---+--+----+---+---+----+------+-+ + * | |seg| address | + * +-+---+-----------------------------+ + */ +#ifdef USE_GBI_TRACE +#define __gSPVertex(pkt, v, n, v0) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_VTX, 24, 8) | _SHIFTL((n), 12, 8) | _SHIFTL((v0) + (n), 1, 7); \ + _g->words.w1 = (uintptr_t)(v); \ + _g->words.trace.file = __FILE__; \ + _g->words.trace.idx = __LINE__; \ + _g->words.trace.valid = true; \ + }) +#else +#define __gSPVertex(pkt, v, n, v0) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_VTX, 24, 8) | _SHIFTL((n), 12, 8) | _SHIFTL((v0) + (n), 1, 7); \ + _g->words.w1 = (uintptr_t)(v); \ + }) +#endif +#define gsSPVertex(v, n, v0) \ + { (_SHIFTL(G_VTX, 24, 8) | _SHIFTL((n), 12, 8) | _SHIFTL((v0) + (n), 1, 7)), (uintptr_t)(v)MakeTrace() } + +#elif (defined(F3DEX_GBI) || defined(F3DLP_GBI)) + /* + * F3DEX_GBI: G_VTX GBI format was changed to support 64 vertice. + * + * +--------+--------+------+----------+ + * G_VTX | cmd:8 | v0:8 | n:6 |length:10 | + * +-+---+--+--------+------+----------+ + * | |seg| address | + * +-+---+-----------------------------+ + */ +#define __gSPVertex(pkt, v, n, v0) gDma1p((pkt), G_VTX, (v), ((n) << 10) | (sizeof(Vtx) * (n)-1), (v0)*2) +#define gsSPVertex(v, n, v0) gsDma1p(G_VTX, (v), ((n) << 10) | (sizeof(Vtx) * (n)-1), (v0)*2) +#else +#define __gSPVertex(pkt, v, n, v0) gDma1p(pkt, G_VTX, v, sizeof(Vtx) * (n), ((n)-1) << 4 | (v0)) +#define gsSPVertex(v, n, v0) gsDma1p(G_VTX, v, sizeof(Vtx) * (n), ((n)-1) << 4 | (v0)) +#endif + +#ifdef F3DEX_GBI_2 +#define gSPViewport(pkt, v) gDma2p((pkt), G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT, 0) +#define gsSPViewport(v) gsDma2p(G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT, 0) +#else /* F3DEX_GBI_2 */ +#define gSPViewport(pkt, v) gDma1p((pkt), G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT) +#define gsSPViewport(v) gsDma1p(G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT) +#endif /* F3DEX_GBI_2 */ + +#define gsSPPushCD(pkt, dl) gDma1p(pkt, G_PUSHCD, dl, 0, G_DL_PUSH) +#define __gSPDisplayList(pkt, dl) gDma1p(pkt, G_DL, dl, 0, G_DL_PUSH) +#define gsSPDisplayList(dl) gsDma1p(G_DL, dl, 0, G_DL_PUSH) +#define gsSPDisplayListOTRHash(dl) gsDma1p(G_DL_OTR_HASH, dl, 0, G_DL_PUSH) +#define gsSPDisplayListOTRFilePath(dl) gsDma1p(G_DL_OTR_FILEPATH, dl, 0, G_DL_PUSH) +#define gsSPDisplayListIndex(dl) gsDma1p(G_DL_INDEX, dl, 0, G_DL_PUSH) + +#define gSPBranchList(pkt, dl) gDma1p(pkt, G_DL, dl, 0, G_DL_NOPUSH) +#define gsSPBranchList(dl) gsDma1p(G_DL, dl, 0, G_DL_NOPUSH) +#define gsSPBranchListOTRHash(dl) gsDma1p(G_DL_OTR_HASH, dl, 0, G_DL_NOPUSH) +#define gsSPBranchListOTRFilePath(dl) gsDma1p(G_DL_OTR_FILEPATH, dl, 0, G_DL_NOPUSH) +#define gsSPBranchListIndex(dl) gsDma1p(G_DL_INDEX, dl, 0, G_DL_NOPUSH) + +#define gSPSprite2DBase(pkt, s) gDma1p(pkt, G_SPRITE2D_BASE, s, sizeof(uSprite), 0) +#define gsSPSprite2DBase(s) gsDma1p(G_SPRITE2D_BASE, s, sizeof(uSprite), 0) + +/* + * RSP short command (no DMA required) macros + */ +#define gImmp0(pkt, c) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ + }) + +#define gsImmp0(c) \ + { _SHIFTL((c), 24, 8) } + +#define gImmp1(pkt, c, p0) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ + _g->words.w1 = (uintptr_t)(p0); \ + }) + +#define gsImmp1(c, p0) \ + { _SHIFTL((c), 24, 8), (uintptr_t)(p0) } + +#define gImmp2(pkt, c, p0, p1) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ + _g->words.w1 = _SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8); \ + }) + +#define gsImmp2(c, p0, p1) \ + { _SHIFTL((c), 24, 8), _SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8) } + +#define gImmp3(pkt, c, p0, p1, p2) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ + _g->words.w1 = (_SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8) | _SHIFTL((p2), 0, 8)); \ + }) + +#define gsImmp3(c, p0, p1, p2) \ + { _SHIFTL((c), 24, 8), (_SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8) | _SHIFTL((p2), 0, 8)) } + +#define gImmp21(pkt, c, p0, p1, dat) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | _SHIFTL((p1), 0, 8)); \ + _g->words.w1 = (uintptr_t)(dat); \ + }) + +#define gsImmp21(c, p0, p1, dat) \ + { _SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | _SHIFTL((p1), 0, 8), (uintptr_t)(dat) } + +#ifdef F3DEX_GBI_2 +#define gMoveWd(pkt, index, offset, data) gDma1p((pkt), G_MOVEWORD, data, offset, index) +#define gsMoveWd(index, offset, data) gsDma1p(G_MOVEWORD, data, offset, index) +#else /* F3DEX_GBI_2 */ +#define gMoveWd(pkt, index, offset, data) gImmp21((pkt), G_MOVEWORD, offset, index, data) +#define gsMoveWd(index, offset, data) gsImmp21(G_MOVEWORD, offset, index, data) +#endif /* F3DEX_GBI_2 */ + +/* Sprite immediate macros, there is also a sprite dma macro above */ + +#define gSPSprite2DScaleFlip(pkt, sx, sy, fx, fy) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SPRITE2D_SCALEFLIP, 24, 8) | _SHIFTL((fx), 8, 8) | _SHIFTL((fy), 0, 8)); \ + _g->words.w1 = (_SHIFTL((sx), 16, 16) | _SHIFTL((sy), 0, 16)); \ + }) + +#define gsSPSprite2DScaleFlip(sx, sy, fx, fy) \ + { \ + (_SHIFTL(G_SPRITE2D_SCALEFLIP, 24, 8) | _SHIFTL((fx), 8, 8) | _SHIFTL((fy), 0, 8)), \ + (_SHIFTL((sx), 16, 16) | _SHIFTL((sy), 0, 16)) \ + } + +#define gSPSprite2DDraw(pkt, px, py) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SPRITE2D_DRAW, 24, 8)); \ + _g->words.w1 = (_SHIFTL((px), 16, 16) | _SHIFTL((py), 0, 16)); \ + }) + +#define gsSPSprite2DDraw(px, py) \ + { (_SHIFTL(G_SPRITE2D_DRAW, 24, 8)), (_SHIFTL((px), 16, 16) | _SHIFTL((py), 0, 16)) } + +/* + * Note: the SP1Triangle() and line macros multiply the vertex indices + * by 10, this is an optimization for the microcode. + */ +#if (defined(F3DLP_GBI) || defined(F3DEX_GBI)) +#define __gsSP1Triangle_w1(v0, v1, v2) (_SHIFTL((v0)*2, 16, 8) | _SHIFTL((v1)*2, 8, 8) | _SHIFTL((v2)*2, 0, 8)) +#define __gsSP1Triangle_w1f(v0, v1, v2, flag) \ + (((flag) == 0) ? __gsSP1Triangle_w1(v0, v1, v2) \ + : ((flag) == 1) ? __gsSP1Triangle_w1(v1, v2, v0) \ + : __gsSP1Triangle_w1(v2, v0, v1)) +#define __gsSPLine3D_w1(v0, v1, wd) (_SHIFTL((v0)*2, 16, 8) | _SHIFT((v1)*2, 8, 8) | _SHIFT((wd), 0, 8)) +#define __gsSPLine3D_w1f(v0, v1, wd, flag) (((flag) == 0) ? __gsSPLine3D_w1(v0, v1, wd) : __gsSPLine3D_w1(v1, v0, wd)) +#define __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag) \ + (((flag) == 0) ? __gsSP1Triangle_w1(v0, v1, v2) \ + : ((flag) == 1) ? __gsSP1Triangle_w1(v1, v2, v3) \ + : ((flag) == 2) ? __gsSP1Triangle_w1(v2, v3, v0) \ + : __gsSP1Triangle_w1(v3, v0, v1)) +#define __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag) \ + (((flag) == 0) ? __gsSP1Triangle_w1(v0, v2, v3) \ + : ((flag) == 1) ? __gsSP1Triangle_w1(v1, v3, v0) \ + : ((flag) == 2) ? __gsSP1Triangle_w1(v2, v0, v1) \ + : __gsSP1Triangle_w1(v3, v1, v2)) +#else +#define __gsSP1Triangle_w1f(v0, v1, v2, flag) \ + (_SHIFTL((flag), 24, 8) | _SHIFTL((v0)*10, 16, 8) | _SHIFTL((v1)*10, 8, 8) | _SHIFTL((v2)*10, 0, 8)) +#define __gsSPLine3D_w1f(v0, v1, wd, flag) \ + (_SHIFTL((flag), 24, 8) | _SHIFTL((v0)*10, 16, 8) | _SHIFTL((v1)*10, 8, 8) | _SHIFTL((wd), 0, 8)) +#endif + +#ifdef F3DEX_GBI_2 +/*** + *** 1 Triangle + ***/ +#define gSP1Triangle(pkt, v0, v1, v2, flag) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_TRI1, 24, 8) | __gsSP1Triangle_w1f(v0, v1, v2, flag); \ + _g->words.w1 = 0; \ + }) +#define gsSP1Triangle(v0, v1, v2, flag) \ + { _SHIFTL(G_TRI1, 24, 8) | __gsSP1Triangle_w1f(v0, v1, v2, flag), 0 } + +/*** + *** Line + ***/ +#define gSPLine3D(pkt, v0, v1, flag) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, 0, flag); \ + _g->words.w1 = 0; \ + }) +#define gsSPLine3D(v0, v1, flag) \ + { _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, 0, flag), 0 } + +/*** + *** LineW + ***/ +/* these macros are the same as SPLine3D, except they have an + * additional parameter for width. The width is added to the "minimum" + * thickness, which is 1.5 pixels. The units for width are in + * half-pixel units, so a width of 1 translates to (.5 + 1.5) or + * a 2.0 pixels wide line. + */ +#define gSPLineW3D(pkt, v0, v1, wd, flag) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, wd, flag); \ + _g->words.w1 = 0; \ + }) +#define gsSPLineW3D(v0, v1, wd, flag) \ + { _SHIFTL(G_LINE3D, 24, 8) | __gsSPLine3D_w1f(v0, v1, wd, flag), 0 } + +/*** + *** 1 Quadrangle + ***/ +#define gSP1Quadrangle(pkt, v0, v1, v2, v3, flag) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_QUAD, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)); \ + _g->words.w1 = __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag); \ + }) + +#define gsSP1Quadrangle(v0, v1, v2, v3, flag) \ + { \ + (_SHIFTL(G_QUAD, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)), \ + __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag) \ + } +#else /* F3DEX_GBI_2 */ + +/*** + *** 1 Triangle + ***/ +#define gSP1Triangle(pkt, v0, v1, v2, flag) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_TRI1, 24, 8); \ + _g->words.w1 = __gsSP1Triangle_w1f(v0, v1, v2, flag); \ + } +#define gsSP1Triangle(v0, v1, v2, flag) \ + { _SHIFTL(G_TRI1, 24, 8), __gsSP1Triangle_w1f(v0, v1, v2, flag) } + +/*** + *** Line + ***/ +#define gSPLine3D(pkt, v0, v1, flag) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8); \ + _g->words.w1 = __gsSPLine3D_w1f(v0, v1, 0, flag); \ + } +#define gsSPLine3D(v0, v1, flag) \ + { _SHIFTL(G_LINE3D, 24, 8), __gsSPLine3D_w1f(v0, v1, 0, flag) } + +/*** + *** LineW + ***/ +/* these macros are the same as SPLine3D, except they have an + * additional parameter for width. The width is added to the "minimum" + * thickness, which is 1.5 pixels. The units for width are in + * half-pixel units, so a width of 1 translates to (.5 + 1.5) or + * a 2.0 pixels wide line. + */ +#define gSPLineW3D(pkt, v0, v1, wd, flag) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8); \ + _g->words.w1 = __gsSPLine3D_w1f(v0, v1, wd, flag); \ + } +#define gsSPLineW3D(v0, v1, wd, flag) \ + { _SHIFTL(G_LINE3D, 24, 8), __gsSPLine3D_w1f(v0, v1, wd, flag) } + +/*** + *** 1 Quadrangle + ***/ +#define gSP1Quadrangle(pkt, v0, v1, v2, v3, flag) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)); \ + _g->words.w1 = __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag); \ + } + +#define gsSP1Quadrangle(v0, v1, v2, v3, flag) \ + { \ + (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)), \ + __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag) \ + } +#endif /* F3DEX_GBI_2 */ + +#if (defined(F3DLP_GBI) || defined(F3DEX_GBI)) +/*** + *** 2 Triangles + ***/ +#define gSP2Triangles(pkt, v00, v01, v02, flag0, v10, v11, v12, flag1) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Triangle_w1f(v00, v01, v02, flag0)); \ + _g->words.w1 = __gsSP1Triangle_w1f(v10, v11, v12, flag1); \ + }) + +#define gsSP2Triangles(v00, v01, v02, flag0, v10, v11, v12, flag1) \ + { (_SHIFTL(G_TRI2, 24, 8) | __gsSP1Triangle_w1f(v00, v01, v02, flag0)), __gsSP1Triangle_w1f(v10, v11, v12, flag1) } + +#endif /* F3DEX_GBI/F3DLP_GBI */ + +#if (defined(F3DEX_GBI) || defined(F3DLP_GBI)) +#define gSPCullDisplayList(pkt, vstart, vend) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_CULLDL, 24, 8) | _SHIFTL((vstart)*2, 0, 16); \ + _g->words.w1 = _SHIFTL((vend)*2, 0, 16); \ + }) + +#define gsSPCullDisplayList(vstart, vend) \ + { _SHIFTL(G_CULLDL, 24, 8) | _SHIFTL((vstart)*2, 0, 16), _SHIFTL((vend)*2, 0, 16) } + +#else +#define gSPCullDisplayList(pkt, vstart, vend) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_CULLDL, 24, 8) | ((0x0f & (vstart)) * 40); \ + _g->words.w1 = (unsigned int)((0x0f & ((vend) + 1)) * 40); \ + } + +#define gsSPCullDisplayList(vstart, vend) \ + { _SHIFTL(G_CULLDL, 24, 8) | ((0x0f & (vstart)) * 40), ((0x0f & ((vend) + 1)) * 40) } +#endif + +#define __gSPSegmentInterp(pkt, segment, base) gMoveWd(pkt, G_MW_SEGMENT_INTERP, segment, base) +#define __gSPSegment(pkt, segment, base) gMoveWd(pkt, G_MW_SEGMENT, (segment)*4, base) +#define gsSPSegment(segment, base) gsMoveWd(G_MW_SEGMENT, (segment)*4, base) + +/* + * Clipping Macros + */ +#define FR_NEG_FRUSTRATIO_1 0x00000001 +#define FR_POS_FRUSTRATIO_1 0x0000ffff +#define FR_NEG_FRUSTRATIO_2 0x00000002 +#define FR_POS_FRUSTRATIO_2 0x0000fffe +#define FR_NEG_FRUSTRATIO_3 0x00000003 +#define FR_POS_FRUSTRATIO_3 0x0000fffd +#define FR_NEG_FRUSTRATIO_4 0x00000004 +#define FR_POS_FRUSTRATIO_4 0x0000fffc +#define FR_NEG_FRUSTRATIO_5 0x00000005 +#define FR_POS_FRUSTRATIO_5 0x0000fffb +#define FR_NEG_FRUSTRATIO_6 0x00000006 +#define FR_POS_FRUSTRATIO_6 0x0000fffa +/* + * r should be one of: FRUSTRATIO_1, FRUSTRATIO_2, FRUSTRATIO_3, ... FRUSTRATIO_6 + */ +#define gSPClipRatio(pkt, r) \ + _DW({ \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RNX, FR_NEG_##r); \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RNY, FR_NEG_##r); \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RPX, FR_POS_##r); \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RPY, FR_POS_##r); \ + }) + +#define gsSPClipRatio(r) \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNX, FR_NEG_##r), gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNY, FR_NEG_##r), \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPX, FR_POS_##r), gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPY, FR_POS_##r) + +/* + * Insert values into Matrix + * + * where = element of matrix (byte offset) + * num = new element (32 bit value replacing 2 int or 2 frac matrix + * componants + */ +#ifdef F3DEX_GBI_2 +#define gSPInsertMatrix(pkt, where, num) ERROR !!gSPInsertMatrix is no longer supported. +#define gsSPInsertMatrix(where, num) ERROR !!gsSPInsertMatrix is no longer supported. +#else +#define gSPInsertMatrix(pkt, where, num) gMoveWd(pkt, G_MW_MATRIX, where, num) +#define gsSPInsertMatrix(where, num) gsMoveWd(G_MW_MATRIX, where, num) +#endif + +/* + * Load new matrix directly + * + * mptr = pointer to matrix + */ +#ifdef F3DEX_GBI_2 +#define gSPForceMatrix(pkt, mptr) \ + _DW({ \ + gDma2p((pkt), G_MOVEMEM, (mptr), sizeof(Mtx), G_MV_MATRIX, 0); \ + gMoveWd((pkt), G_MW_FORCEMTX, 0, 0x00010000); \ + }) +#define gsSPForceMatrix(mptr) \ + gsDma2p(G_MOVEMEM, (mptr), sizeof(Mtx), G_MV_MATRIX, 0), gsMoveWd(G_MW_FORCEMTX, 0, 0x00010000) + +#else /* F3DEX_GBI_2 */ +#define gSPForceMatrix(pkt, mptr) \ + { \ + gDma1p(pkt, G_MOVEMEM, mptr, 16, G_MV_MATRIX_1); \ + gDma1p(pkt, G_MOVEMEM, (char*)(mptr) + 16, 16, G_MV_MATRIX_2); \ + gDma1p(pkt, G_MOVEMEM, (char*)(mptr) + 32, 16, G_MV_MATRIX_3); \ + gDma1p(pkt, G_MOVEMEM, (char*)(mptr) + 48, 16, G_MV_MATRIX_4); \ + } +#define gsSPForceMatrix(mptr) \ + gsDma1p(G_MOVEMEM, mptr, 16, G_MV_MATRIX_1), gsDma1p(G_MOVEMEM, (char*)(mptr) + 16, 16, G_MV_MATRIX_2), \ + gsDma1p(G_MOVEMEM, (char*)(mptr) + 32, 16, G_MV_MATRIX_3), \ + gsDma1p(G_MOVEMEM, (char*)(mptr) + 48, 16, G_MV_MATRIX_4) +#endif /* F3DEX_GBI_2 */ + +/* + * Insert values into Points + * + * point = point number 0-15 + * where = which element of point to modify (byte offset into point) + * num = new value (32 bit) + */ +#if (defined(F3DEX_GBI) || defined(F3DLP_GBI)) +#define gSPModifyVertex(pkt, vtx, where, val) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = (_SHIFTL(G_MODIFYVTX, 24, 8) | _SHIFTL((where), 16, 8) | _SHIFTL((vtx)*2, 0, 16)); \ + _g->words.w1 = (unsigned int)(val); \ + }) +#define gsSPModifyVertex(vtx, where, val) \ + { _SHIFTL(G_MODIFYVTX, 24, 8) | _SHIFTL((where), 16, 8) | _SHIFTL((vtx)*2, 0, 16), (unsigned int)(val) } +#else +#define gSPModifyVertex(pkt, vtx, where, val) gMoveWd(pkt, G_MW_POINTS, (vtx)*40 + (where), val) +#define gsSPModifyVertex(vtx, where, val) gsMoveWd(G_MW_POINTS, (vtx)*40 + (where), val) +#endif + +#if (defined(F3DEX_GBI) || defined(F3DLP_GBI)) +/* + * gSPBranchLessZ Branch DL if (vtx.z) less than or equal (zval). + * + * dl = DL branch to + * vtx = Vertex + * zval = Screen depth + * near = Near plane + * far = Far plane + * flag = G_BZ_PERSP or G_BZ_ORTHO + */ + +#define G_BZ_PERSP 0 +#define G_BZ_ORTHO 1 + +#define G_DEPTOZSrg(zval, near, far, flag, zmin, zmax) \ + (((unsigned int)FTOFIX32(((flag) == G_BZ_PERSP \ + ? (1.0f - (float)(near) / (float)(zval)) / (1.0f - (float)(near) / (float)(far)) \ + : ((float)(zval) - (float)(near)) / ((float)(far) - (float)(near))))) * \ + (((int)((zmax) - (zmin))) & ~1) + \ + (int)FTOFIX32(zmin)) + +#define G_DEPTOZS(zval, near, far, flag) G_DEPTOZSrg(zval, near, far, flag, 0, G_MAXZ) + +#define gSPBranchLessZrg(pkt, dl, vtx, zval, near, far, flag, zmin, zmax) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8); \ + _g->words.w1 = (uintptr_t)(dl); \ + _g = (Gfx*)(pkt); \ + _g->words.w0 = (_SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12)); \ + _g->words.w1 = G_DEPTOZSrg(zval, near, far, flag, zmin, zmax); \ + }) + +#define gsSPBranchLessZrg(dl, vtx, zval, near, far, flag, zmin, zmax) \ + { \ + _SHIFTL(G_RDPHALF_1, 24, 8), \ + (uintptr_t)(dl), \ + }, \ + { \ + _SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12), \ + G_DEPTOZSrg(zval, near, far, flag, zmin, zmax), \ + } + +#define gSPBranchLessZ(pkt, dl, vtx, zval, near, far, flag) \ + gSPBranchLessZrg(pkt, dl, vtx, zval, near, far, flag, 0, G_MAXZ) +#define gsSPBranchLessZ(dl, vtx, zval, near, far, flag) gsSPBranchLessZrg(dl, vtx, zval, near, far, flag, 0, G_MAXZ) + +/* + * gSPBranchLessZraw Branch DL if (vtx.z) less than or equal (raw zval). + * + * dl = DL branch to + * vtx = Vertex + * zval = Raw value of screen depth + */ +#define gSPBranchLessZraw(pkt, dl, vtx, zval) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8); \ + _g->words.w1 = (uintptr_t)(dl); \ + _g = (Gfx*)(pkt); \ + _g->words.w0 = (_SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12)); \ + _g->words.w1 = (uintptr_t)(zval); \ + }) + +#define gsSPBranchLessZraw(dl, vtx, zval) \ + { \ + _SHIFTL(G_RDPHALF_1, 24, 8), \ + (uintptr_t)(dl), \ + }, \ + { \ + _SHIFTL(G_BRANCH_Z, 24, 8) | _SHIFTL((vtx)*5, 12, 12) | _SHIFTL((vtx)*2, 0, 12), (uintptr_t)(zval), \ + } + +/* + * gSPLoadUcode RSP loads specified ucode. + * + * uc_start = ucode text section start + * uc_dstart = ucode data section start + */ +#define gSPLoadUcodeEx(pkt, uc_start, uc_dstart, uc_dsize) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8); \ + _g->words.w1 = (uintptr_t)(uc_dstart); \ + _g = (Gfx*)(pkt); \ + _g->words.w0 = (_SHIFTL(G_LOAD_UCODE, 24, 8) | _SHIFTL((int)(uc_dsize)-1, 0, 16)); \ + _g->words.w1 = (uintptr_t)(uc_start); \ + }) + +#define gsSPLoadUcodeEx(uc_start, uc_dstart, uc_dsize) \ + { \ + _SHIFTL(G_RDPHALF_1, 24, 8), \ + (uintptr_t)(uc_dstart), \ + }, \ + { \ + _SHIFTL(G_LOAD_UCODE, 24, 8) | _SHIFTL((int)(uc_dsize)-1, 0, 16), (uintptr_t)(uc_start), \ + } + +#define gSPLoadUcode(pkt, uc_index) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_LOAD_UCODE, 24, 8) | _SHIFTL(uc_index, 0, 16); \ + }) + +#define gSPLoadUcodeL(pkt, uc_index) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_LOAD_UCODE, 24, 8) | _SHIFTL(uc_index, 0, 16); \ + }) + +#endif + +#ifdef F3DEX_GBI_2 +/* + * gSPDma_io DMA to/from DMEM/IMEM for DEBUG. + */ +#define gSPDma_io(pkt, flag, dmem, dram, size) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_DMA_IO, 24, 8) | _SHIFTL((flag), 23, 1) | _SHIFTL((dmem) / 8, 13, 10) | \ + _SHIFTL((size)-1, 0, 12); \ + _g->words.w1 = (uintptr_t)(dram); \ + }) + +#define gsSPDma_io(flag, dmem, dram, size) \ + { \ + _SHIFTL(G_DMA_IO, 24, 8) | _SHIFTL((flag), 23, 1) | _SHIFTL((dmem) / 8, 13, 10) | _SHIFTL((size)-1, 0, 12), \ + (uintptr_t)(dram) \ + } + +#define gSPDmaRead(pkt, dmem, dram, size) gSPDma_io((pkt), 0, (dmem), (dram), (size)) +#define gsSPDmaRead(dmem, dram, size) gsSPDma_io(0, (dmem), (dram), (size)) +#define gSPDmaWrite(pkt, dmem, dram, size) gSPDma_io((pkt), 1, (dmem), (dram), (size)) +#define gsSPDmaWrite(dmem, dram, size) gsSPDma_io(1, (dmem), (dram), (size)) +#endif + +/* + * Lighting Macros + */ +#ifdef F3DEX_GBI_2 +#define NUML(n) ((n)*24) +#else +#define NUML(n) (((n) + 1) * 32 + 0x80000000) +#endif +#define NUMLIGHTS_0 1 +#define NUMLIGHTS_1 1 +#define NUMLIGHTS_2 2 +#define NUMLIGHTS_3 3 +#define NUMLIGHTS_4 4 +#define NUMLIGHTS_5 5 +#define NUMLIGHTS_6 6 +#define NUMLIGHTS_7 7 +/* + * n should be one of: NUMLIGHTS_0, NUMLIGHTS_1, ..., NUMLIGHTS_7 + * NOTE: in addition to the number of directional lights specified, + * there is always 1 ambient light + */ +#define gSPNumLights(pkt, n) gMoveWd(pkt, G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) +#define gsSPNumLights(n) gsMoveWd(G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) + +#define LIGHT_1 1 +#define LIGHT_2 2 +#define LIGHT_3 3 +#define LIGHT_4 4 +#define LIGHT_5 5 +#define LIGHT_6 6 +#define LIGHT_7 7 +#define LIGHT_8 8 +/* + * l should point to a Light struct + * n should be one of: LIGHT_1, LIGHT_2, ..., LIGHT_8 + * NOTE: the highest numbered light is always the ambient light (eg if there are + * 3 directional lights defined: gsSPNumLights(NUMLIGHTS_3), then lights + * LIGHT_1 through LIGHT_3 will be the directional lights and light + * LIGHT_4 will be the ambient light. + */ +#ifdef F3DEX_GBI_2 +#define gSPLight(pkt, l, n) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, (n)*24 + 24) +#define gsSPLight(l, n) gsDma2p(G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, (n)*24 + 24) +#else /* F3DEX_GBI_2 */ +#define gSPLight(pkt, l, n) gDma1p(pkt, G_MOVEMEM, l, sizeof(Light), ((n)-1) * 2 + G_MV_L0) +#define gsSPLight(l, n) gsDma1p(G_MOVEMEM, l, sizeof(Light), ((n)-1) * 2 + G_MV_L0) +#endif /* F3DEX_GBI_2 */ + +/* + * gSPLightColor changes color of light without recalculating light direction + * col is a 32 bit word with r,g,b,a (alpha is ignored) + * n should be one of LIGHT_1, LIGHT_2, ..., LIGHT_8 + */ +#define gSPLightColor(pkt, n, col) \ + _DW({ \ + gMoveWd(pkt, G_MW_LIGHTCOL, G_MWO_a##n, col); \ + gMoveWd(pkt, G_MW_LIGHTCOL, G_MWO_b##n, col); \ + }) +#define gsSPLightColor(n, col) gsMoveWd(G_MW_LIGHTCOL, G_MWO_a##n, col), gsMoveWd(G_MW_LIGHTCOL, G_MWO_b##n, col) + +/* These macros use a structure "name" which is init'd with the gdSPDefLights macros*/ + +#define gSPSetLights0(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_0); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.a, 2); \ + }) +#define gsSPSetLights0(name) gsSPNumLights(NUMLIGHTS_0), gsSPLight(&name.l[0], 1), gsSPLight(&name.a, 2) + +#define gSPSetLights1(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_1); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.a, 2); \ + }) +#define gsSPSetLights1(name) gsSPNumLights(NUMLIGHTS_1), gsSPLight(&name.l[0], 1), gsSPLight(&name.a, 2) + +#define gSPSetLights2(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_2); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.l[1], 2); \ + gSPLight(pkt, &name.a, 3); \ + }) +#define gsSPSetLights2(name) \ + gsSPNumLights(NUMLIGHTS_2), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.a, 3) + +#define gSPSetLights3(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_3); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.l[1], 2); \ + gSPLight(pkt, &name.l[2], 3); \ + gSPLight(pkt, &name.a, 4); \ + }) +#define gsSPSetLights3(name) \ + gsSPNumLights(NUMLIGHTS_3), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \ + gsSPLight(&name.a, 4) + +#define gSPSetLights4(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_4); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.l[1], 2); \ + gSPLight(pkt, &name.l[2], 3); \ + gSPLight(pkt, &name.l[3], 4); \ + gSPLight(pkt, &name.a, 5); \ + }) +#define gsSPSetLights4(name) \ + gsSPNumLights(NUMLIGHTS_4), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \ + gsSPLight(&name.l[3], 4), gsSPLight(&name.a, 5) + +#define gSPSetLights5(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_5); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.l[1], 2); \ + gSPLight(pkt, &name.l[2], 3); \ + gSPLight(pkt, &name.l[3], 4); \ + gSPLight(pkt, &name.l[4], 5); \ + gSPLight(pkt, &name.a, 6); \ + }) + +#define gsSPSetLights5(name) \ + gsSPNumLights(NUMLIGHTS_5), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \ + gsSPLight(&name.l[3], 4), gsSPLight(&name.l[4], 5), gsSPLight(&name.a, 6) + +#define gSPSetLights6(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_6); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.l[1], 2); \ + gSPLight(pkt, &name.l[2], 3); \ + gSPLight(pkt, &name.l[3], 4); \ + gSPLight(pkt, &name.l[4], 5); \ + gSPLight(pkt, &name.l[5], 6); \ + gSPLight(pkt, &name.a, 7); \ + }) + +#define gsSPSetLights6(name) \ + gsSPNumLights(NUMLIGHTS_6), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \ + gsSPLight(&name.l[3], 4), gsSPLight(&name.l[4], 5), gsSPLight(&name.l[5], 6), gsSPLight(&name.a, 7) + +#define gSPSetLights7(pkt, name) \ + _DW({ \ + gSPNumLights(pkt, NUMLIGHTS_7); \ + gSPLight(pkt, &name.l[0], 1); \ + gSPLight(pkt, &name.l[1], 2); \ + gSPLight(pkt, &name.l[2], 3); \ + gSPLight(pkt, &name.l[3], 4); \ + gSPLight(pkt, &name.l[4], 5); \ + gSPLight(pkt, &name.l[5], 6); \ + gSPLight(pkt, &name.l[6], 7); \ + gSPLight(pkt, &name.a, 8); \ + }) + +#define gsSPSetLights7(name) \ + gsSPNumLights(NUMLIGHTS_7), gsSPLight(&name.l[0], 1), gsSPLight(&name.l[1], 2), gsSPLight(&name.l[2], 3), \ + gsSPLight(&name.l[3], 4), gsSPLight(&name.l[4], 5), gsSPLight(&name.l[5], 6), gsSPLight(&name.l[6], 7), \ + gsSPLight(&name.a, 8) + +/* + * Reflection/Hiliting Macros + */ +#ifdef F3DEX_GBI_2 +#define gSPLookAtX(pkt, l) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATX) +#define gsSPLookAtX(l) gsDma2p(G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATX) +#define gSPLookAtY(pkt, l) gDma2p((pkt), G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATY) +#define gsSPLookAtY(l) gsDma2p(G_MOVEMEM, (l), sizeof(Light), G_MV_LIGHT, G_MVO_LOOKATY) +#else /* F3DEX_GBI_2 */ +#define gSPLookAtX(pkt, l) gDma1p(pkt, G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATX) +#define gsSPLookAtX(l) gsDma1p(G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATX) +#define gSPLookAtY(pkt, l) gDma1p(pkt, G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATY) +#define gsSPLookAtY(l) gsDma1p(G_MOVEMEM, l, sizeof(Light), G_MV_LOOKATY) +#endif /* F3DEX_GBI_2 */ + +#define gSPLookAt(pkt, la) \ + _DW({ \ + gSPLookAtX(pkt, la); \ + gSPLookAtY(pkt, (char*)(la) + 16); \ + }) +#define gsSPLookAt(la) gsSPLookAtX(la), gsSPLookAtY((char*)(la) + 16) + +#define gDPSetHilite1Tile(pkt, tile, hilite, width, height) \ + gDPSetTileSize(pkt, tile, (hilite)->h.x1 & 0xfff, (hilite)->h.y1 & 0xfff, \ + ((((width)-1) * 4) + (hilite)->h.x1) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y1) & 0xfff) +#define gsDPSetHilite1Tile(tile, hilite, width, height) \ + gsDPSetTileSize(tile, (hilite)->h.x1 & 0xfff, (hilite)->h.y1 & 0xfff, \ + ((((width)-1) * 4) + (hilite)->h.x1) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y1) & 0xfff) + +#define gDPSetHilite2Tile(pkt, tile, hilite, width, height) \ + gDPSetTileSize(pkt, tile, (hilite)->h.x2 & 0xfff, (hilite)->h.y2 & 0xfff, \ + ((((width)-1) * 4) + (hilite)->h.x2) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y2) & 0xfff) +#define gsDPSetHilite2Tile(tile, hilite, width, height) \ + gsDPSetTileSize(tile, (hilite)->h.x2 & 0xfff, (hilite)->h.y2 & 0xfff, \ + ((((width)-1) * 4) + (hilite)->h.x2) & 0xfff, ((((height)-1) * 4) + (hilite)->h.y2) & 0xfff) + +/* + * FOG macros + * fm = z multiplier + * fo = z offset + * FOG FORMULA: alpha(fog) = (eyespace z) * fm + fo CLAMPED 0 to 255 + * note: (eyespace z) ranges -1 to 1 + * + * Alternate method of setting fog: + * min, max: range 0 to 1000: 0=nearplane, 1000=farplane + * min is where fog begins (usually less than max and often 0) + * max is where fog is thickest (usually 1000) + * + */ +#define gSPFogFactor(pkt, fm, fo) gMoveWd(pkt, G_MW_FOG, G_MWO_FOG, (_SHIFTL(fm, 16, 16) | _SHIFTL(fo, 0, 16))) + +#define gsSPFogFactor(fm, fo) gsMoveWd(G_MW_FOG, G_MWO_FOG, (_SHIFTL(fm, 16, 16) | _SHIFTL(fo, 0, 16))) + +#define gSPFogPosition(pkt, min, max) \ + gMoveWd(pkt, G_MW_FOG, G_MWO_FOG, \ + (_SHIFTL((128000 / ((max) - (min))), 16, 16) | _SHIFTL(((500 - (min)) * 256 / ((max) - (min))), 0, 16))) + +#define gsSPFogPosition(min, max) \ + gsMoveWd(G_MW_FOG, G_MWO_FOG, \ + (_SHIFTL((128000 / ((max) - (min))), 16, 16) | _SHIFTL(((500 - (min)) * 256 / ((max) - (min))), 0, 16))) + +#ifdef F3DEX_GBI_2 +/* + * Macros to turn texture on/off + */ +#define gSPTexture(pkt, s, t, level, tile, on) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | \ + _SHIFTL((tile), 8, 3) | _SHIFTL((on), 1, 7)); \ + _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)); \ + }) +#define gsSPTexture(s, t, level, tile, on) \ + { \ + (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \ + _SHIFTL((on), 1, 7)), \ + (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)) \ + } +/* + * Different version of SPTexture macro, has an additional parameter + * which is currently reserved in the microcode. + */ +#define gSPTextureL(pkt, s, t, level, xparam, tile, on) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | \ + _SHIFTL((tile), 8, 3) | _SHIFTL((on), 1, 7)); \ + _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)); \ + }) +#define gsSPTextureL(s, t, level, xparam, tile, on) \ + { \ + (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \ + _SHIFTL((on), 1, 7)), \ + (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)) \ + } +#else +/* + * Macros to turn texture on/off + */ +#define gSPTexture(pkt, s, t, level, tile, on) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | \ + _SHIFTL((tile), 8, 3) | _SHIFTL((on), 0, 8)); \ + _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)); \ + } +#define gsSPTexture(s, t, level, tile, on) \ + { \ + (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL(BOWTIE_VAL, 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \ + _SHIFTL((on), 0, 8)), \ + (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)) \ + } +/* + * Different version of SPTexture macro, has an additional parameter + * which is currently reserved in the microcode. + */ +#define gSPTextureL(pkt, s, t, level, xparam, tile, on) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | \ + _SHIFTL((tile), 8, 3) | _SHIFTL((on), 0, 8)); \ + _g->words.w1 = (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)); \ + } +#define gsSPTextureL(s, t, level, xparam, tile, on) \ + { \ + (_SHIFTL(G_TEXTURE, 24, 8) | _SHIFTL((xparam), 16, 8) | _SHIFTL((level), 11, 3) | _SHIFTL((tile), 8, 3) | \ + _SHIFTL((on), 0, 8)), \ + (_SHIFTL((s), 16, 16) | _SHIFTL((t), 0, 16)) \ + } +#endif + +#define gSPPerspNormalize(pkt, s) gMoveWd(pkt, G_MW_PERSPNORM, 0, (s)) +#define gsSPPerspNormalize(s) gsMoveWd(G_MW_PERSPNORM, 0, (s)) + +#ifdef F3DEX_GBI_2 +#define gSPPopMatrixN(pkt, n, num) gDma2p((pkt), G_POPMTX, (num)*64, 64, 2, 0) +#define gsSPPopMatrixN(n, num) gsDma2p(G_POPMTX, (num)*64, 64, 2, 0) +#define gSPPopMatrix(pkt, n) gSPPopMatrixN((pkt), (n), 1) +#define gsSPPopMatrix(n) gsSPPopMatrixN((n), 1) +#else /* F3DEX_GBI_2 */ +#define gSPPopMatrix(pkt, n) gImmp1(pkt, G_POPMTX, n) +#define gsSPPopMatrix(n) gsImmp1(G_POPMTX, n) +#endif /* F3DEX_GBI_2 */ + +#define gSPEndDisplayList(pkt) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_ENDDL, 24, 8); \ + _g->words.w1 = 0; \ + }) + +#define gsSPEndDisplayList() \ + { _SHIFTL(G_ENDDL, 24, 8), 0 } + +#define __gSPInvalidateTexCache(pkt, addr) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_INVALTEXCACHE, 24, 8); \ + _g->words.w1 = addr; \ + }) + +#define gsSPInvalidateTexCache() \ + { _SHIFTL(G_INVALTEXCACHE, 24, 8), 0 } + +#define gSPRegisterBlendedTex(pkt, timg, mask, replc) \ + { \ + Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt); \ + \ + _g0->words.w0 = _SHIFTL(G_REGBLENDEDTEX, 24, 8); \ + _g0->words.w1 = (uintptr_t)timg; \ + _g1->words.w0 = (uintptr_t)mask; \ + _g1->words.w1 = (uintptr_t)replc; \ + } + +#define gsSPSetFB(pkt, fb) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETFB, 24, 8); \ + _g->words.w1 = fb; \ + } + +#define gsSPResetFB(pkt) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_RESETFB, 24, 8); \ + _g->words.w1 = 0; \ + } + +// Copy a framebuffer's texture to another framebuffer's in the GPU +#define gDPCopyFB(pkt, dst, src, once, copiedPtr) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_COPYFB, 24, 8) | _SHIFTL(dst, 11, 11) | _SHIFTL(src, 0, 11) | _SHIFTL(once, 22, 1); \ + _g->words.w1 = (uintptr_t)copiedPtr; \ + } + +// Read the framebuffer's texture to a cpu memory location as RGBA16 +#define gDPReadFB(pkt, src, rgba16buf, ulx, uly, width, height, bswap) \ + { \ + Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt); \ + \ + _g0->words.w0 = _SHIFTL(G_READFB, 24, 8) | _SHIFTL(bswap, 8, 1) | _SHIFTL(src, 0, 8); \ + _g0->words.w1 = (uintptr_t)rgba16buf; \ + _g1->words.w0 = _SHIFTL(uly, 16, 16) | _SHIFTL(ulx, 0, 16); \ + _g1->words.w1 = _SHIFTL(height, 16, 16) | _SHIFTL(width, 0, 16); \ + } + +#define gDPImageRectangle(pkt, x0, y0, s0, t0, x1, y1, s1, t1, tile, iw, ih) \ + { \ + Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt), *_g2 = (Gfx*)(pkt); \ + \ + _g0->words.w0 = _SHIFTL(G_IMAGERECT, 24, 8) | _SHIFTL((tile), 0, 3); \ + _g0->words.w1 = _SHIFTL((iw), 16, 16) | _SHIFTL((ih), 0, 16); \ + _g1->words.w0 = _SHIFTL((x0), 16, 16) | _SHIFTL((y0), 0, 16); \ + _g1->words.w1 = _SHIFTL((s0), 16, 16) | _SHIFTL((t0), 0, 16); \ + _g2->words.w0 = _SHIFTL((x1), 16, 16) | _SHIFTL((y1), 0, 16); \ + _g2->words.w1 = _SHIFTL((s1), 16, 16) | _SHIFTL((t1), 0, 16); \ + } + +#define gSPGrayscale(pkt, state) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETGRAYSCALE, 24, 8); \ + _g->words.w1 = state; \ + } + +#define gsSPGrayscale(state) \ + { (_SHIFTL(G_SETGRAYSCALE, 24, 8)), (state) } + +#define gsSPLoadShader(shader, type) gsDma1p(G_LOAD_SHADER, shader, 0, type) +#define gsSPUnloadShader() gsDma1p(G_LOAD_SHADER, 0, 0, 0) + +#define gSPLoadShader(pkt, shader, type) gDma1p(pkt, G_LOAD_SHADER, shader, 0, type) +#define gSPUnloadShader(pkt) gDma1p(pkt, G_LOAD_SHADER, 0, 0, 0) + +#define gSPExtraGeometryMode(pkt, c, s) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_EXTRAGEOMETRYMODE, 24, 8) | _SHIFTL(~(u32)(c), 0, 24); \ + _g->words.w1 = (u32)(s); \ + }) + +#define gSPSetExtraGeometryMode(pkt, word) gSPExtraGeometryMode((pkt), 0, word) +#define gSPClearExtraGeometryMode(pkt, word) gSPExtraGeometryMode((pkt), word, 0) + +#ifdef F3DEX_GBI_2 +/* + * One gSPGeometryMode(pkt,c,s) GBI is equal to these two GBIs. + * + * gSPClearGeometryMode(pkt,c) + * gSPSetGeometryMode(pkt,s) + * + * gSPLoadGeometryMode(pkt, word) sets GeometryMode directly. + */ +#define gSPGeometryMode(pkt, c, s) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = _SHIFTL(G_GEOMETRYMODE, 24, 8) | _SHIFTL(~(u32)(c), 0, 24); \ + _g->words.w1 = (u32)(s); \ + }) + +#define gsSPGeometryMode(c, s) \ + { (_SHIFTL(G_GEOMETRYMODE, 24, 8) | _SHIFTL(~(u32)(c), 0, 24)), (u32)(s) } +#define gSPSetGeometryMode(pkt, word) gSPGeometryMode((pkt), 0, (word)) +#define gsSPSetGeometryMode(word) gsSPGeometryMode(0, (word)) +#define gSPClearGeometryMode(pkt, word) gSPGeometryMode((pkt), (word), 0) +#define gsSPClearGeometryMode(word) gsSPGeometryMode((word), 0) +#define gSPLoadGeometryMode(pkt, word) gSPGeometryMode((pkt), -1, (word)) +#define gsSPLoadGeometryMode(word) gsSPGeometryMode(-1, (word)) + +#else /* F3DEX_GBI_2 */ +#define gSPSetGeometryMode(pkt, word) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETGEOMETRYMODE, 24, 8); \ + _g->words.w1 = (unsigned int)(word); \ + } + +#define gsSPSetGeometryMode(word) \ + { _SHIFTL(G_SETGEOMETRYMODE, 24, 8), (unsigned int)(word) } + +#define gSPClearGeometryMode(pkt, word) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8); \ + _g->words.w1 = (unsigned int)(word); \ + } + +#define gsSPClearGeometryMode(word) \ + { _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8), (unsigned int)(word) } +#endif /* F3DEX_GBI_2 */ + +#ifdef F3DEX_GBI_2 +#define gSPSetOtherMode(pkt, cmd, sft, len, data) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + _g->words.w0 = (_SHIFTL(cmd, 24, 8) | _SHIFTL(32 - (sft) - (len), 8, 8) | _SHIFTL((len)-1, 0, 8)); \ + _g->words.w1 = (unsigned int)(data); \ + }) + +#define gsSPSetOtherMode(cmd, sft, len, data) \ + { _SHIFTL(cmd, 24, 8) | _SHIFTL(32 - (sft) - (len), 8, 8) | _SHIFTL((len)-1, 0, 8), (unsigned int)(data) } +#else +#define gSPSetOtherMode(pkt, cmd, sft, len, data) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | _SHIFTL(len, 0, 8)); \ + _g->words.w1 = (unsigned int)(data); \ + } + +#define gsSPSetOtherMode(cmd, sft, len, data) \ + { _SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | _SHIFTL(len, 0, 8), (unsigned int)(data) } +#endif + +/* + * RDP setothermode register commands - register shadowed in RSP + */ +#define gDPPipelineMode(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) +#define gsDPPipelineMode(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) + +#define gDPSetCycleType(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_CYCLETYPE, 2, type) +#define gsDPSetCycleType(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_CYCLETYPE, 2, type) + +#define gDPSetTexturePersp(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTPERSP, 1, type) +#define gsDPSetTexturePersp(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTPERSP, 1, type) + +#define gDPSetTextureDetail(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTDETAIL, 2, type) +#define gsDPSetTextureDetail(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTDETAIL, 2, type) + +#define gDPSetTextureLOD(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTLOD, 1, type) +#define gsDPSetTextureLOD(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTLOD, 1, type) + +#define gDPSetTextureLUT(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTLUT, 2, type) +#define gsDPSetTextureLUT(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTLUT, 2, type) + +#define gDPSetTextureFilter(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTFILT, 2, type) +#define gsDPSetTextureFilter(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTFILT, 2, type) + +#define gDPSetTextureConvert(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTCONV, 3, type) +#define gsDPSetTextureConvert(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTCONV, 3, type) + +#define gDPSetCombineKey(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_COMBKEY, 1, type) +#define gsDPSetCombineKey(type) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_COMBKEY, 1, type) + +#ifndef _HW_VERSION_1 +#define gDPSetColorDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_RGBDITHER, 2, mode) +#define gsDPSetColorDither(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_RGBDITHER, 2, mode) +#else +#define gDPSetColorDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_COLORDITHER, 1, mode) +#define gsDPSetColorDither(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_COLORDITHER, 1, mode) +#endif + +#ifndef _HW_VERSION_1 +#define gDPSetAlphaDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 2, mode) +#define gsDPSetAlphaDither(mode) gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 2, mode) +#endif + +#define gDPSetDither(pkt, mode) gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 4, mode) + +/* 'blendmask' is not supported anymore. + * The bits are reserved for future use. + * Fri May 26 13:45:55 PDT 1995 + */ +#define gDPSetBlendMask(pkt, mask) gDPNoOp(pkt) +#define gsDPSetBlendMask(mask) gsDPNoOp() + +#define gDPSetAlphaCompare(pkt, type) gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_ALPHACOMPARE, 2, type) +#define gsDPSetAlphaCompare(type) gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_ALPHACOMPARE, 2, type) + +#define gDPSetDepthSource(pkt, src) gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_ZSRCSEL, 1, src) +#define gsDPSetDepthSource(src) gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_ZSRCSEL, 1, src) + +#define gDPSetRenderMode(pkt, c0, c1) gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_RENDERMODE, 29, (c0) | (c1)) +#define gsDPSetRenderMode(c0, c1) gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_RENDERMODE, 29, (c0) | (c1)) + +#define gSetImage(pkt, cmd, fmt, siz, width, i) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12); \ + _g->words.w1 = (uintptr_t)(i); \ + }) + +#define gsSetImage(cmd, fmt, siz, width, i) \ + { _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12), (uintptr_t)(i) } + +#define gDPSetColorImage(pkt, f, s, w, i) gSetImage(pkt, G_SETCIMG, f, s, w, i) +#define gsDPSetColorImage(f, s, w, i) gsSetImage(G_SETCIMG, f, s, w, i) + +/* use these for new code */ +#define gDPSetDepthImage(pkt, i) gSetImage(pkt, G_SETZIMG, 0, 0, 1, i) +#define gsDPSetDepthImage(i) gsSetImage(G_SETZIMG, 0, 0, 1, i) +/* kept for compatibility */ +#define gDPSetMaskImage(pkt, i) gDPSetDepthImage(pkt, i) +#define gsDPSetMaskImage(i) gsDPSetDepthImage(i) + +#define gDPSetTextureImage(pkt, f, s, w, i) gSetImage(pkt, G_SETTIMG, f, s, w, i) +#define gsDPSetTextureImage(f, s, w, i) gsSetImage(G_SETTIMG, f, s, w, i) +#define gDPSetTextureImageFB(pkt, f, s, w, i) gSetImage(pkt, G_SETTIMG_FB, f, s, w, i) + +/* + * RDP macros + */ + +#define gDPSetCombine(pkt, muxs0, muxs1) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24); \ + _g->words.w1 = (unsigned int)(muxs1); \ + }) + +#define gsDPSetCombine(muxs0, muxs1) \ + { _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24), (unsigned int)(muxs1) } + +#define GCCc0w0(saRGB0, mRGB0, saA0, mA0) \ + (_SHIFTL((saRGB0), 20, 4) | _SHIFTL((mRGB0), 15, 5) | _SHIFTL((saA0), 12, 3) | _SHIFTL((mA0), 9, 3)) + +#define GCCc1w0(saRGB1, mRGB1) (_SHIFTL((saRGB1), 5, 4) | _SHIFTL((mRGB1), 0, 5)) + +#define GCCc0w1(sbRGB0, aRGB0, sbA0, aA0) \ + (_SHIFTL((sbRGB0), 28, 4) | _SHIFTL((aRGB0), 15, 3) | _SHIFTL((sbA0), 12, 3) | _SHIFTL((aA0), 9, 3)) + +#define GCCc1w1(sbRGB1, saA1, mA1, aRGB1, sbA1, aA1) \ + (_SHIFTL((sbRGB1), 24, 4) | _SHIFTL((saA1), 21, 3) | _SHIFTL((mA1), 18, 3) | _SHIFTL((aRGB1), 6, 3) | \ + _SHIFTL((sbA1), 3, 3) | _SHIFTL((aA1), 0, 3)) + +#define gDPSetCombineLERP(pkt, a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = \ + _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(G_CCMUX_##a0, G_CCMUX_##c0, G_ACMUX_##Aa0, G_ACMUX_##Ac0) | \ + GCCc1w0(G_CCMUX_##a1, G_CCMUX_##c1), \ + 0, 24); \ + _g->words.w1 = (unsigned int)(GCCc0w1(G_CCMUX_##b0, G_CCMUX_##d0, G_ACMUX_##Ab0, G_ACMUX_##Ad0) | \ + GCCc1w1(G_CCMUX_##b1, G_ACMUX_##Aa1, G_ACMUX_##Ac1, G_CCMUX_##d1, G_ACMUX_##Ab1, \ + G_ACMUX_##Ad1)); \ + }) + +#define gsDPSetCombineLERP(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ + { \ + _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(G_CCMUX_##a0, G_CCMUX_##c0, G_ACMUX_##Aa0, G_ACMUX_##Ac0) | \ + GCCc1w0(G_CCMUX_##a1, G_CCMUX_##c1), \ + 0, 24), \ + (unsigned int)(GCCc0w1(G_CCMUX_##b0, G_CCMUX_##d0, G_ACMUX_##Ab0, G_ACMUX_##Ad0) | \ + GCCc1w1(G_CCMUX_##b1, G_ACMUX_##Aa1, G_ACMUX_##Ac1, G_CCMUX_##d1, G_ACMUX_##Ab1, \ + G_ACMUX_##Ad1)) \ + } + +#define gsDPSetCombineLERP_NoMacros(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ + { \ + _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(GCCc0w0(a0, c0, Aa0, Ac0) | GCCc1w0(a1, c1), 0, 24), \ + (unsigned int)(GCCc0w1(b0, d0, Ab0, Ad0) | GCCc1w1(b1, Aa1, Ac1, d1, Ab1, Ad1)) \ + } + +/* + * SetCombineMode macros are NOT redunant. It allow the C preprocessor + * to substitute single parameter which includes commas in the token and + * rescan for higher parameter count macro substitution. + * + * eg. gsDPSetCombineMode(G_CC_MODULATE, G_CC_MODULATE) turns into + * gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0, + * TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0) + */ + +/* +#if _MSC_VER +#define gDPSetCombineMode(pkt, a, b) gDPNoParam(pkt, G_NOOP) +#else +#define gDPSetCombineMode(pkt, a, b) gDPSetCombineLERP(pkt, a, b) +//#define gDPSetCombineMode(pkt, a, b) gDPSetCombineLERP(pkt, ##a, b) +#endif +*/ + +#if defined(_MSC_VER) +#define CALL_2(A, B) A B +#define CALL_3(A, B, C) A B C + +#define gDPSetCombineMode(pkt, a, b) CALL_2(gDPSetCombineLERP, (pkt, a, b)) +#define gsDPSetCombineMode(a, b) CALL_2(gsDPSetCombineLERP, (a, b)) +#else +#define gDPSetCombineMode(pkt, a, b) gDPSetCombineLERP(pkt, a, b) +#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b) +#endif + +#if defined(_MSC_VER) || defined(__GNUC__) +#define CALL_2(A, B) A B +#define CALL_3(A, B, C) A B C + +// #define gsDPSetCombineMode(a, b) CALL_2(gsDPSetCombineLERP, (a, b)) +// #define gsDPSetCombineMode(a, b) _SHIFTL(0, 24, 8), 0 +#else +#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b) +#endif + +#define gDPSetColor(pkt, c, d) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(c, 24, 8); \ + _g->words.w1 = (unsigned int)(d); \ + }) + +#define gsDPSetColor(c, d) \ + { _SHIFTL(c, 24, 8), (unsigned int)(d) } + +#define DPRGBColor(pkt, cmd, r, g, b, a) \ + gDPSetColor(pkt, cmd, (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8))) +#define sDPRGBColor(cmd, r, g, b, a) \ + gsDPSetColor(cmd, (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8))) + +#define gDPSetGrayscaleColor(pkt, r, g, b, lerp) DPRGBColor(pkt, G_SETINTENSITY, r, g, b, lerp) +#define gsDPSetGrayscaleColor(r, g, b, a) sDPRGBColor(G_SETINTENSITY, r, g, b, a) +#define gDPSetEnvColor(pkt, r, g, b, a) DPRGBColor(pkt, G_SETENVCOLOR, r, g, b, a) +#define gsDPSetEnvColor(r, g, b, a) sDPRGBColor(G_SETENVCOLOR, r, g, b, a) +#define gDPSetBlendColor(pkt, r, g, b, a) DPRGBColor(pkt, G_SETBLENDCOLOR, r, g, b, a) +#define gsDPSetBlendColor(r, g, b, a) sDPRGBColor(G_SETBLENDCOLOR, r, g, b, a) +#define gDPSetFogColor(pkt, r, g, b, a) DPRGBColor(pkt, G_SETFOGCOLOR, r, g, b, a) +#define gsDPSetFogColor(r, g, b, a) sDPRGBColor(G_SETFOGCOLOR, r, g, b, a) +#define gDPSetFillColor(pkt, d) gDPSetColor(pkt, G_SETFILLCOLOR, (d)) +#define gsDPSetFillColor(d) gsDPSetColor(G_SETFILLCOLOR, (d)) +#define gDPSetPrimDepth(pkt, z, dz) gDPSetColor(pkt, G_SETPRIMDEPTH, _SHIFTL(z, 16, 16) | _SHIFTL(dz, 0, 16)) +#define gsDPSetPrimDepth(z, dz) gsDPSetColor(G_SETPRIMDEPTH, _SHIFTL(z, 16, 16) | _SHIFTL(dz, 0, 16)) + +#define gDPSetPrimColor(pkt, m, l, r, g, b, a) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SETPRIMCOLOR, 24, 8) | _SHIFTL(m, 8, 8) | _SHIFTL(l, 0, 8)); \ + _g->words.w1 = (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)); \ + }) + +#define gsDPSetPrimColor(m, l, r, g, b, a) \ + { \ + (_SHIFTL(G_SETPRIMCOLOR, 24, 8) | _SHIFTL(m, 8, 8) | _SHIFTL(l, 0, 8)), \ + (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)) \ + } + +/* + * gDPSetOtherMode (This is for expert user.) + * + * This command makes all othermode parameters set. + * Do not use this command in the same DL with another g*SPSetOtherMode DLs. + * + * [Usage] + * gDPSetOtherMode(pkt, modeA, modeB) + * + * 'modeA' is described all parameters of GroupA GBI command. + * 'modeB' is also described all parameters of GroupB GBI command. + * + * GroupA: + * gDPPipelineMode, gDPSetCycleType, gSPSetTexturePersp, + * gDPSetTextureDetail, gDPSetTextureLOD, gDPSetTextureLUT, + * gDPSetTextureFilter, gDPSetTextureConvert, gDPSetCombineKey, + * gDPSetColorDither, gDPSetAlphaDither + * + * GroupB: + * gDPSetAlphaCompare, gDPSetDepthSource, gDPSetRenderMode + * + * Use 'OR' operation to get modeA and modeB. + * + * modeA = G_PM_* | G_CYC_* | G_TP_* | G_TD_* | G_TL_* | G_TT_* | G_TF_* + * G_TC_* | G_CK_* | G_CD_* | G_AD_*; + * + * modeB = G_AC_* | G_ZS_* | G_RM_* | G_RM_*2; + */ +#define gDPSetOtherMode(pkt, mode0, mode1) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_RDPSETOTHERMODE, 24, 8) | _SHIFTL(mode0, 0, 24); \ + _g->words.w1 = (unsigned int)(mode1); \ + }) + +#define gsDPSetOtherMode(mode0, mode1) \ + { _SHIFTL(G_RDPSETOTHERMODE, 24, 8) | _SHIFTL(mode0, 0, 24), (unsigned int)(mode1) } + +/* + * Texturing macros + */ + +/* These are also defined defined above for Sprite Microcode */ + +#define G_TX_LOADTILE 7 +#define G_TX_RENDERTILE 0 + +#define G_TX_NOMIRROR 0 +#define G_TX_WRAP 0 +#define G_TX_MIRROR 0x1 +#define G_TX_CLAMP 0x2 +#define G_TX_NOMASK 0 +#define G_TX_NOLOD 0 + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +/* + * Dxt is the inverse of the number of 64-bit words in a line of + * the texture being loaded using the load_block command. If + * there are any 1's to the right of the 11th fractional bit, + * dxt should be rounded up. The following macros accomplish + * this. The 4b macros are a special case since 4-bit textures + * are loaded as 8-bit textures. Dxt is fixed point 1.11. RJM + */ +#define G_TX_DXT_FRAC 11 + +/* + * For RCP 2.0, the maximum number of texels that can be loaded + * using a load_block command is 2048. In order to load the total + * 4kB of Tmem, change the texel size when loading to be G_IM_SIZ_16b, + * then change the tile to the proper texel size after the load. + * The g*DPLoadTextureBlock macros already do this, so this change + * will be transparent if you use these macros. If you use + * the g*DPLoadBlock macros directly, you will need to handle this + * tile manipulation yourself. RJM. + */ + +#define G_TX_LDBLK_MAX_TXL 4095 + +#define TXL2WORDS(txls, b_txl) MAX(1, ((txls) * (b_txl) / 8)) +#define CALC_DXT(width, b_txl) (((1 << G_TX_DXT_FRAC) + TXL2WORDS(width, b_txl) - 1) / TXL2WORDS(width, b_txl)) + +#define TXL2WORDS_4b(txls) MAX(1, ((txls) / 16)) +#define CALC_DXT_4b(width) (((1 << G_TX_DXT_FRAC) + TXL2WORDS_4b(width) - 1) / TXL2WORDS_4b(width)) + +#define gDPLoadTileGeneric(pkt, c, tile, uls, ult, lrs, lrt) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(c, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12); \ + _g->words.w1 = _SHIFTL(tile, 24, 3) | _SHIFTL(lrs, 12, 12) | _SHIFTL(lrt, 0, 12); \ + }) + +#define gsDPLoadTileGeneric(c, tile, uls, ult, lrs, lrt) \ + { \ + _SHIFTL(c, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12), \ + _SHIFTL(tile, 24, 3) | _SHIFTL(lrs, 12, 12) | _SHIFTL(lrt, 0, 12) \ + } + +#define gDPSetInterpolation(pkt, index) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = G_SETTARGETINTERPINDEX << 24; \ + _g->words.w1 = index; \ + }) + +#define __gDPSetTileSizeInterp(pkt, t, uls, ult, lrs, lrt) \ + gDPLoadTileGeneric(pkt, G_SETTILESIZE_INTERP, t, uls, ult, lrs, lrt) +#define gDPSetTileSize(pkt, t, uls, ult, lrs, lrt) gDPLoadTileGeneric(pkt, G_SETTILESIZE, t, uls, ult, lrs, lrt) +#define gsDPSetTileSize(t, uls, ult, lrs, lrt) gsDPLoadTileGeneric(G_SETTILESIZE, t, uls, ult, lrs, lrt) +#define gDPLoadTile(pkt, t, uls, ult, lrs, lrt) gDPLoadTileGeneric(pkt, G_LOADTILE, t, uls, ult, lrs, lrt) +#define gsDPLoadTile(t, uls, ult, lrs, lrt) gsDPLoadTileGeneric(G_LOADTILE, t, uls, ult, lrs, lrt) + +#define gDPSetTile(pkt, fmt, siz, line, tmem, tile, palette, cmt, maskt, shiftt, cms, masks, shifts) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL(line, 9, 9) | \ + _SHIFTL(tmem, 0, 9); \ + _g->words.w1 = _SHIFTL(tile, 24, 3) | _SHIFTL(palette, 20, 4) | _SHIFTL(cmt, 18, 2) | _SHIFTL(maskt, 14, 4) | \ + _SHIFTL(shiftt, 10, 4) | _SHIFTL(cms, 8, 2) | _SHIFTL(masks, 4, 4) | _SHIFTL(shifts, 0, 4); \ + }) + +#define gsDPSetTile(fmt, siz, line, tmem, tile, palette, cmt, maskt, shiftt, cms, masks, shifts) \ + { \ + (_SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | _SHIFTL(siz, 19, 2) | _SHIFTL(line, 9, 9) | \ + _SHIFTL(tmem, 0, 9)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(palette, 20, 4) | _SHIFTL(cmt, 18, 2) | _SHIFTL(maskt, 14, 4) | \ + _SHIFTL(shiftt, 10, 4) | _SHIFTL(cms, 8, 2) | _SHIFTL(masks, 4, 4) | _SHIFTL(shifts, 0, 4)) \ + } + +/* + * For RCP 2.0, the maximum number of texels that can be loaded + * using a load_block command is 2048. In order to load the total + * 4kB of Tmem, change the texel size when loading to be G_IM_SIZ_16b, + * then change the tile to the proper texel size after the load. + * The g*DPLoadTextureBlock macros already do this, so this change + * will be transparent if you use these macros. If you use + * the g*DPLoadBlock macros directly, you will need to handle this + * tile manipulation yourself. RJM. + */ +#define gDPLoadBlock(pkt, tile, uls, ult, lrs, dxt) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_LOADBLOCK, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL((MIN(lrs, G_TX_LDBLK_MAX_TXL)), 12, 12) | _SHIFTL(dxt, 0, 12)); \ + }) + +#define gsDPLoadBlock(tile, uls, ult, lrs, dxt) \ + { \ + (_SHIFTL(G_LOADBLOCK, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL((MIN(lrs, G_TX_LDBLK_MAX_TXL)), 12, 12) | _SHIFTL(dxt, 0, 12)) \ + } + +#define gDPLoadTLUTCmd(pkt, tile, count) \ + _DW({ \ + Gfx* _g = (Gfx*)pkt; \ + \ + _g->words.w0 = _SHIFTL(G_LOADTLUT, 24, 8); \ + _g->words.w1 = _SHIFTL((tile), 24, 3) | _SHIFTL((count), 14, 10); \ + }) + +#define gsDPLoadTLUTCmd(tile, count) \ + { _SHIFTL(G_LOADTLUT, 24, 8), _SHIFTL((tile), 24, 3) | _SHIFTL((count), 14, 10) } + +#define gDPLoadTextureBlock(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#define gDPLoadTextureBlockYuv(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*1) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* Load fix rww 27jun95 */ +/* The S at the end means odd lines are already word Swapped */ + +#define gDPLoadTextureBlockS(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* + * Allow tmem address and render tile to be specified. + * The S at the end means odd lines are already word Swapped + */ +#define gDPLoadMultiBlockS(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, \ + shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#define gDPLoadTextureBlockYuvS(pkt, timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*1) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* + * allows tmem address to be specified + */ +#define _gDPLoadTextureBlock(pkt, timg, tmem, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, G_TX_RENDERTILE, pal, cmt, maskt, \ + shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* + * allows tmem address and render tile to be specified + */ +#define _gDPLoadTextureBlockTile(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, \ + shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* + * allows tmem address and render tile to be specified + */ +#define gDPLoadMultiBlock(pkt, timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, \ + shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_LINE_BYTES) + 7) >> 3, tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#define gsDPLoadTextureBlock(timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* Here is the static form of the pre-swapped texture block loading */ +/* See gDPLoadTextureBlockS() for reference. Basically, just don't + calculate DxT, use 0 */ + +#define gsDPLoadTextureBlockS(timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * Allow tmem address to be specified + */ +#define _gsDPLoadTextureBlock(timg, tmem, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * Allow tmem address and render_tile to be specified + */ +#define _gsDPLoadTextureBlockTile(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, \ + shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * Allow tmem address and render_tile to be specified, useful when loading + * mutilple tiles at a time. + */ +#define gsDPLoadMultiBlock(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * Allows tmem and render tile to be specified. Useful when loading + * several tiles at a time. + * + * Here is the static form of the pre-swapped texture block loading + * See gDPLoadTextureBlockS() for reference. Basically, just don't + * calculate DxT, use 0 + */ + +#define gsDPLoadMultiBlockS(timg, tmem, rtile, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + siz##_INCR) >> siz##_SHIFT) - 1, 0), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width)*siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +#define gDPLoadTextureBlock_4b(pkt, timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* Load fix rww 27jun95 */ +/* The S at the end means odd lines are already word Swapped */ + +#define gDPLoadTextureBlock_4bS(pkt, timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* + * 4-bit load block. Useful when loading multiple tiles + */ +#define gDPLoadMultiBlock_4b(pkt, timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* + * 4-bit load block. Allows tmem and render tile to be specified. Useful when + * loading multiple tiles. The S means odd lines are already word swapped. + */ +#define gDPLoadMultiBlock_4bS(pkt, timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, \ + masks, shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#define _gDPLoadTextureBlock_4b(pkt, timg, tmem, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#define gsDPLoadTextureBlock_4b(timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +#define gsDPLoadTextureBlock_4bS(timg, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0), gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * 4-bit load block. Allows tmem address and render tile to be specified. + * Useful when loading multiple tiles. + */ +#define gsDPLoadMultiBlock_4b(timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * 4-bit load block. Allows tmem address and render tile to be specified. + * Useful when loading multiple tiles. S means odd lines are already swapped. + */ +#define gsDPLoadMultiBlock_4bS(timg, tmem, rtile, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, 0), gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * Allows tmem address to be specified + */ +#define _gsDPLoadTextureBlock_4b(timg, tmem, fmt, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width) * (height) + 3) >> 2) - 1, CALC_DXT_4b(width)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width) >> 1) + 7) >> 3), tmem, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +#ifndef _HW_VERSION_1 + +#define gDPLoadTextureTile(pkt, timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, \ + shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz, width, timg); \ + gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt, \ + maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, \ + maskt, shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#else /******** WORKAROUND hw 1 load tile bug ********/ + +#define gDPLoadTextureTile(pkt, timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, \ + shifts, shiftt) \ + \ + { \ + int _loadtile_i, _loadtile_nw; \ + Gfx* _loadtile_temp = pkt; \ + guDPLoadTextureTile(_loadtile_temp, timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, \ + maskt, shifts, shiftt); \ + _loadtile_nw = guGetDPLoadTextureTileSz(ult, lrt) - 1; \ + for (_loadtile_i = 0; _loadtile_i < _loadtile_nw; _loadtile_i++) \ + pkt; \ + } + +#endif /* HW_VERSION_1 */ + +/* + * Load texture tile. Allows tmem address and render tile to be specified. + * Useful for loading multiple tiles. + */ +#define gDPLoadMultiTile(pkt, timg, tmem, rtile, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, \ + maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, siz, width, timg); \ + gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt, \ + maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, \ + shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#define gsDPLoadTextureTile(timg, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, \ + shiftt) \ + \ + gsDPSetTextureImage(fmt, siz, width, timg), \ + gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt, maskt, \ + shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile(G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, \ + maskt, shiftt, cms, masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC) + +/* + * Load texture tile. Allows tmem address and render tile to be specified. + * Useful for loading multiple tiles. + */ +#define gsDPLoadMultiTile(timg, tmem, rtile, fmt, siz, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, \ + shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz, width, timg), \ + gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_TILE_BYTES) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt, \ + maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile(G_TX_LOADTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, (((((lrs) - (uls) + 1) * siz##_LINE_BYTES) + 7) >> 3), tmem, rtile, pal, cmt, maskt, \ + shiftt, cms, masks, shifts), \ + gsDPSetTileSize(rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC) + +#define gDPLoadTextureTile_4b(pkt, timg, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, \ + shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_8b, ((width) >> 1), timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt, maskt, \ + shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC), \ + (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, \ + maskt, shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC); \ + }) + +/* + * Load texture tile. Allows tmem address and render tile to be specified. + * Useful for loading multiple tiles. + */ +#define gDPLoadMultiTile_4b(pkt, timg, tmem, rtile, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, \ + maskt, shifts, shiftt) \ + _DW({ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_8b, ((width) >> 1), timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt, maskt, \ + shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile(pkt, G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC), \ + (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, \ + shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC); \ + }) + +#define gsDPLoadTextureTile_4b(timg, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, shifts, \ + shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_8b, ((width) >> 1), timg), \ + gsDPSetTile(fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_LOADTILE, 0, cmt, maskt, \ + shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile(G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC), \ + (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), 0, G_TX_RENDERTILE, pal, cmt, maskt, \ + shiftt, cms, masks, shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC) + +/* + * Load texture tile. Allows tmem address and render tile to be specified. + * Useful for loading multiple tiles. + */ +#define gsDPLoadMultiTile_4b(timg, tmem, rtile, fmt, width, height, uls, ult, lrs, lrt, pal, cms, cmt, masks, maskt, \ + shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_8b, ((width) >> 1), timg), \ + gsDPSetTile(fmt, G_IM_SIZ_8b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, G_TX_LOADTILE, 0, cmt, maskt, \ + shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile(G_TX_LOADTILE, (uls) << (G_TEXTURE_IMAGE_FRAC - 1), (ult) << (G_TEXTURE_IMAGE_FRAC), \ + (lrs) << (G_TEXTURE_IMAGE_FRAC - 1), (lrt) << (G_TEXTURE_IMAGE_FRAC)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, (((((lrs) - (uls) + 1) >> 1) + 7) >> 3), tmem, rtile, pal, cmt, maskt, shiftt, \ + cms, masks, shifts), \ + gsDPSetTileSize(rtile, (uls) << G_TEXTURE_IMAGE_FRAC, (ult) << G_TEXTURE_IMAGE_FRAC, \ + (lrs) << G_TEXTURE_IMAGE_FRAC, (lrt) << G_TEXTURE_IMAGE_FRAC) + +/* + * Load a 16-entry palette (for 4-bit CI textures) + * Assumes a 16 entry tlut is being loaded, palette # is 0-15 + */ +#ifndef _HW_VERSION_1 + +#define gDPLoadTLUT_pal16(pkt, pal, dram) \ + _DW({ \ + gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram); \ + gDPTileSync(pkt); \ + gDPSetTile(pkt, 0, 0, 0, (256 + (((pal)&0xf) * 16)), G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \ + gDPLoadSync(pkt); \ + gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, 15); \ + gDPPipeSync(pkt); \ + }) + +#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */ + +#define gDPLoadTLUT_pal16(pkt, pal, dram) \ + \ + _gDPLoadTextureBlock(pkt, dram, (256 + (((pal)&0xf) * 16)), G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 16, 1, pal, 0, 0, 0, \ + 0, 0, 0) + +#endif /* _HW_VERSION_1 */ + +/* + * Load a 16-entry palette (for 4-bit CI textures) + * Assumes a 16 entry tlut is being loaded, palette # is 0-15 + */ +#ifndef _HW_VERSION_1 + +#define gsDPLoadTLUT_pal16(pal, dram) \ + \ + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram), gsDPTileSync(), \ + gsDPSetTile(0, 0, 0, (256 + (((pal)&0xf) * 16)), G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0), gsDPLoadSync(), \ + gsDPLoadTLUTCmd(G_TX_LOADTILE, 15), gsDPPipeSync() + +#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */ + +#define gsDPLoadTLUT_pal16(pal, dram) \ + \ + _gsDPLoadTextureBlock(dram, (256 + (((pal)&0xf) * 16)), G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 16, 1, pal, 0, 0, 0, 0, \ + 0, 0) + +#endif /* _HW_VERSION_1 */ + +/* + * Load a 256-entry palette (for 8-bit CI textures) + * Assumes a 256 entry tlut is being loaded, palette # is not used + */ +#ifndef _HW_VERSION_1 + +#define gDPLoadTLUT_pal256(pkt, dram) \ + _DW({ \ + gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram); \ + gDPTileSync(pkt); \ + gDPSetTile(pkt, 0, 0, 0, 256, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \ + gDPLoadSync(pkt); \ + gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, 255); \ + gDPPipeSync(pkt); \ + }) + +#define gDPLoadTLUT_pal128(pkt, pal, dram) \ + _DW({ \ + gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram); \ + gDPTileSync(pkt); \ + gDPSetTile(pkt, 0, 0, 0, 256 + ((pal)&1) * 128, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \ + gDPLoadSync(pkt); \ + gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, 127); \ + gDPPipeSync(pkt); \ + }) + +#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */ + +#define gDPLoadTLUT_pal256(pkt, dram) \ + \ + _gDPLoadTextureBlock(pkt, dram, 256, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 256, 1, 0, 0, 0, 0, 0, 0, 0) + +#endif /* _HW_VERSION_1 */ + +#ifndef _HW_VERSION_1 + +#define gsDPLoadTLUT_pal256(dram) \ + \ + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram), gsDPTileSync(), \ + gsDPSetTile(0, 0, 0, 256, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0), gsDPLoadSync(), \ + gsDPLoadTLUTCmd(G_TX_LOADTILE, 255), gsDPPipeSync() + +#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */ + +#define gsDPLoadTLUT_pal256(dram) \ + \ + _gsDPLoadTextureBlock(dram, 256, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4 * 256, 1, 0, 0, 0, 0, 0, 0, 0) + +#endif /* _HW_VERSION_1 */ + +#ifndef _HW_VERSION_1 + +#define gDPLoadTLUT(pkt, count, tmemaddr, dram) \ + _DW({ \ + gDPSetTextureImage(pkt, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram); \ + gDPTileSync(pkt); \ + gDPSetTile(pkt, 0, 0, 0, tmemaddr, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); \ + gDPLoadSync(pkt); \ + gDPLoadTLUTCmd(pkt, G_TX_LOADTILE, ((count)-1)); \ + gDPPipeSync(pkt); \ + }) + +#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */ + +#define gDPLoadTLUT(pkt, count, tmemaddr, dram) \ + \ + _gDPLoadTextureBlock(pkt, dram, tmemaddr, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4, count, 0, 0, 0, 0, 0, 0, 0) + +#endif /* _HW_VERSION_1 */ + +#ifndef _HW_VERSION_1 + +#define gsDPLoadTLUT(count, tmemaddr, dram) \ + \ + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, dram), gsDPTileSync(), \ + gsDPSetTile(0, 0, 0, tmemaddr, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0), gsDPLoadSync(), \ + gsDPLoadTLUTCmd(G_TX_LOADTILE, ((count)-1)), gsDPPipeSync() + +#else /* **** WORKAROUND hardware 1 load_tlut bug ****** */ +#define gsDPLoadTLUT(count, tmemaddr, dram) \ + \ + _gsDPLoadTextureBlock(dram, tmemaddr, G_IM_FMT_RGBA, G_IM_SIZ_16b, 4, count, 0, 0, 0, 0, 0, 0, 0) + +#endif /* _HW_VERSION_1 */ + +#define gDPSetScissor(pkt, mode, ulx, uly, lrx, lry) \ + _DW({ \ + Gfx* _g = (Gfx*)pkt; \ + \ + _g->words.w0 = _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((float)(ulx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(uly)*4.0F), 0, 12); \ + _g->words.w1 = _SHIFTL(mode, 24, 2) | _SHIFTL((int)((float)(lrx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(lry)*4.0F), 0, 12); \ + }) + +#define gDPSetScissorFrac(pkt, mode, ulx, uly, lrx, lry) \ + _DW({ \ + Gfx* _g = (Gfx*)pkt; \ + \ + _g->words.w0 = _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((ulx)), 12, 12) | _SHIFTL((int)((uly)), 0, 12); \ + _g->words.w1 = _SHIFTL(mode, 24, 2) | _SHIFTL((int)((lrx)), 12, 12) | _SHIFTL((int)((lry)), 0, 12); \ + }) + +#define gsDPSetScissor(mode, ulx, uly, lrx, lry) \ + { \ + _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((float)(ulx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(uly)*4.0F), 0, 12), \ + _SHIFTL(mode, 24, 2) | _SHIFTL((int)((float)(lrx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(lry)*4.0F), 0, 12) \ + } + +#define gsDPSetScissorFrac(mode, ulx, uly, lrx, lry) \ + { \ + _SHIFTL(G_SETSCISSOR, 24, 8) | _SHIFTL((int)((ulx)), 12, 12) | _SHIFTL((int)((uly)), 0, 12), \ + _SHIFTL(mode, 24, 2) | _SHIFTL((int)(lrx), 12, 12) | _SHIFTL((int)(lry), 0, 12) \ + } + +#define gDPFillWideRectangle(pkt, ulx, uly, lrx, lry) \ + { \ + Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt); \ + _g0->words.w0 = _SHIFTL(G_FILLWIDERECT, 24, 8) | _SHIFTL((lrx), 2, 22); \ + _g0->words.w1 = _SHIFTL((lry), 2, 22); \ + _g1->words.w0 = _SHIFTL((ulx), 2, 22); \ + _g1->words.w1 = _SHIFTL((uly), 2, 22); \ + } + +/* Fraction never used in fill */ +#define gDPFillRectangle(pkt, ulx, uly, lrx, lry) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL((lrx), 14, 10) | _SHIFTL((lry), 2, 10)); \ + _g->words.w1 = (_SHIFTL((ulx), 14, 10) | _SHIFTL((uly), 2, 10)); \ + }) + +#define gsDPFillRectangle(ulx, uly, lrx, lry) \ + { \ + (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL((lrx), 14, 10) | _SHIFTL((lry), 2, 10)), \ + (_SHIFTL((ulx), 14, 10) | _SHIFTL((uly), 2, 10)) \ + } + +/* like gDPFillRectangle but accepts negative arguments */ +#define gDPScisFillRectangle(pkt, ulx, uly, lrx, lry) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL(MAX((lrx), 0), 14, 10) | _SHIFTL(MAX((lry), 0), 2, 10)); \ + _g->words.w1 = (_SHIFTL(MAX((ulx), 0), 14, 10) | _SHIFTL(MAX((uly), 0), 2, 10)); \ + }) + +#define gDPSetConvert(pkt, k0, k1, k2, k3, k4, k5) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SETCONVERT, 24, 8) | _SHIFTL(k0, 13, 9) | _SHIFTL(k1, 4, 9) | _SHIFTR(k2, 5, 4)); \ + _g->words.w1 = (_SHIFTL(k2, 27, 5) | _SHIFTL(k3, 18, 9) | _SHIFTL(k4, 9, 9) | _SHIFTL(k5, 0, 9)); \ + }) + +#define gsDPSetConvert(k0, k1, k2, k3, k4, k5) \ + { \ + (_SHIFTL(G_SETCONVERT, 24, 8) | _SHIFTL(k0, 13, 9) | _SHIFTL(k1, 4, 9) | _SHIFTL(k2, 5, 4)), \ + (_SHIFTL(k2, 27, 5) | _SHIFTL(k3, 18, 9) | _SHIFTL(k4, 9, 9) | _SHIFTL(k5, 0, 9)) \ + } + +#define gDPSetKeyR(pkt, cR, sR, wR) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETKEYR, 24, 8); \ + _g->words.w1 = (_SHIFTL(wR, 16, 12) | _SHIFTL(cR, 8, 8) | _SHIFTL(sR, 0, 8)); \ + }) + +#define gsDPSetKeyR(cR, sR, wR) \ + { _SHIFTL(G_SETKEYR, 24, 8), _SHIFTL(wR, 16, 12) | _SHIFTL(cR, 8, 8) | _SHIFTL(sR, 0, 8) } + +#define gDPSetKeyGB(pkt, cG, sG, wG, cB, sB, wB) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SETKEYGB, 24, 8) | _SHIFTL(wG, 12, 12) | _SHIFTL(wB, 0, 12)); \ + _g->words.w1 = (_SHIFTL(cG, 24, 8) | _SHIFTL(sG, 16, 8) | _SHIFTL(cB, 8, 8) | _SHIFTL(sB, 0, 8)); \ + }) + +#define gsDPSetKeyGB(cG, sG, wG, cB, sB, wB) \ + { \ + (_SHIFTL(G_SETKEYGB, 24, 8) | _SHIFTL(wG, 12, 12) | _SHIFTL(wB, 0, 12)), \ + (_SHIFTL(cG, 24, 8) | _SHIFTL(sG, 16, 8) | _SHIFTL(cB, 8, 8) | _SHIFTL(sB, 0, 8)) \ + } + +#define gDPNoParam(pkt, cmd) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(cmd, 24, 8); \ + _g->words.w1 = 0; \ + }) + +#define gsDPNoParam(cmd) \ + { _SHIFTL(cmd, 24, 8), 0 } + +#define gDPParam(pkt, cmd, param) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(cmd, 24, 8); \ + _g->words.w1 = (param); \ + }) + +#define gsDPParam(cmd, param) \ + { _SHIFTL(cmd, 24, 8), (param) } + +/* Notice that textured rectangles are 128-bit commands, therefore + * gsDPTextureRectangle() should not be used in display lists + * under normal circumstances (use gsSPTextureRectangle()). + * That is also why there is no gDPTextureRectangle() macros. + */ +#define gsDPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + { \ + (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)), \ + }, \ + { \ + _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \ + } + +#define gDPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + if (pkt) \ + ; \ + _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)); \ + _g++; \ + _g->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ + }) + +#define gsDPTextureRectangleFlip(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + { \ + (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)), \ + }, \ + { \ + _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \ + } + +#define gDPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + if (pkt) \ + ; \ + _g->words.w0 = (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)); \ + _g++; \ + _g->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ + }) + +#define gsSPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)), \ + gsImmp1(G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))), \ + gsImmp1(G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))) + +#define gSPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)); \ + gImmp1(pkt, G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))); \ + }) + +#define gSPWideTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + { \ + Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt), *_g2 = (Gfx*)(pkt); \ + \ + _g0->words.w0 = _SHIFTL(G_TEXRECT_WIDE, 24, 8) | _SHIFTL((xh), 0, 24); \ + _g0->words.w1 = _SHIFTL((yh), 0, 24); \ + _g1->words.w0 = (_SHIFTL(tile, 24, 3) | _SHIFTL((xl), 0, 24)); \ + _g1->words.w1 = _SHIFTL((yl), 0, 24); \ + _g2->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g2->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ + } + +#define gsSPWideTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + { { \ + (_SHIFTL(G_TEXRECT_WIDE, 24, 8) | _SHIFTL((xh), 0, 24)), \ + _SHIFTL((yh), 0, 24), \ + } }, \ + { { \ + (_SHIFTL((tile), 24, 3) | _SHIFTL((xl), 0, 24)), \ + _SHIFTL((yl), 0, 24), \ + } }, \ + { \ + { _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) } \ + } + +/* like gSPTextureRectangle but accepts negative position arguments */ +#define gSPScisTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = \ + (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(MAX((s16)(xh), 0), 12, 12) | _SHIFTL(MAX((s16)(yh), 0), 0, 12)); \ + _g->words.w1 = \ + (_SHIFTL((tile), 24, 3) | _SHIFTL(MAX((s16)(xl), 0), 12, 12) | _SHIFTL(MAX((s16)(yl), 0), 0, 12)); \ + gImmp1(pkt, G_RDPHALF_1, \ + (_SHIFTL(((s) - (((s16)(xl) < 0) ? (((s16)(dsdx) < 0) ? (MAX((((s16)(xl) * (s16)(dsdx)) >> 7), 0)) \ + : (MIN((((s16)(xl) * (s16)(dsdx)) >> 7), 0))) \ + : 0)), \ + 16, 16) | \ + _SHIFTL(((t) - (((yl) < 0) ? (((s16)(dtdy) < 0) ? (MAX((((s16)(yl) * (s16)(dtdy)) >> 7), 0)) \ + : (MIN((((s16)(yl) * (s16)(dtdy)) >> 7), 0))) \ + : 0)), \ + 0, 16))); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL((dsdx), 16, 16) | _SHIFTL((dtdy), 0, 16))); \ + }) + +#define gsSPTextureRectangleFlip(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)), \ + gsImmp1(G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))), \ + gsImmp1(G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))) + +#define gSPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)); \ + gImmp1(pkt, G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))); \ + }) + +#define gsDPWord(wordhi, wordlo) \ + gsImmp1(G_RDPHALF_1, (unsigned int)(wordhi)), gsImmp1(G_RDPHALF_2, (unsigned int)(wordlo)) + +#define gDPWord(pkt, wordhi, wordlo) \ + _DW({ \ + Gfx* _g = (Gfx*)(pkt); \ + \ + gImmp1(pkt, G_RDPHALF_1, (unsigned int)(wordhi)); \ + gImmp1(pkt, G_RDPHALF_2, (unsigned int)(wordlo)); \ + }) + +#define gDPFullSync(pkt) gDPNoParam(pkt, G_RDPFULLSYNC) +#define gsDPFullSync() gsDPNoParam(G_RDPFULLSYNC) +#define gDPTileSync(pkt) gDPNoParam(pkt, G_RDPTILESYNC) +#define gsDPTileSync() gsDPNoParam(G_RDPTILESYNC) +#define gDPPipeSync(pkt) gDPNoParam(pkt, G_RDPPIPESYNC) +#define gsDPPipeSync() gsDPNoParam(G_RDPPIPESYNC) +#define gDPLoadSync(pkt) gDPNoParam(pkt, G_RDPLOADSYNC) +#define gsDPLoadSync() gsDPNoParam(G_RDPLOADSYNC) +#define gDPNoOp(pkt) gDPNoParam(pkt, G_NOOP) +#define gsDPNoOp() gsDPNoParam(G_NOOP) +#define gDPNoOpTag(pkt, tag) gDPParam(pkt, G_NOOP, tag) +#define gsDPNoOpTag(tag) gsDPParam(G_NOOP, tag) + +#define gDPNoOpHere(pkt, file, line) gDma1p(pkt, G_NOOP, file, line, 1) +#define gDPNoOpString(pkt, data, n) gDma1p(pkt, G_NOOP, data, n, 2) +#define gDPNoOpWord(pkt, data, n) gDma1p(pkt, G_NOOP, data, n, 3) +#define gDPNoOpFloat(pkt, data, n) gDma1p(pkt, G_NOOP, data, n, 4) +#define gDPNoOpQuiet(pkt) gDma1p(pkt, G_NOOP, 0, 0, 5) +#define gDPNoOpVerbose(pkt, n) gDma1p(pkt, G_NOOP, 0, n, 5) +#define gDPNoOpCallBack(pkt, callback, arg) gDma1p(pkt, G_NOOP, callback, arg, 6) +#define gDPNoOpOpenDisp(pkt, file, line) gDma1p(pkt, G_NOOP, file, line, 7) +#define gDPNoOpCloseDisp(pkt, file, line) gDma1p(pkt, G_NOOP, file, line, 8) +#define gDPNoOpTag3(pkt, type, data, n) gDma1p(pkt, G_NOOP, data, n, type) + +#endif diff --git a/libultraship/include/libultraship/libultra/gs2dex.h b/libultraship/include/libultraship/libultra/gs2dex.h new file mode 100644 index 000000000..662d43ae9 --- /dev/null +++ b/libultraship/include/libultraship/libultra/gs2dex.h @@ -0,0 +1,384 @@ +#ifndef GS2DEX_H +#define GS2DEX_H + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +/*===========================================================================* + * Macro + *===========================================================================*/ +#define GS_CALC_DXT(line) (((1 << G_TX_DXT_FRAC) - 1) / (line) + 1) +#define GS_PIX2TMEM(pix, siz) ((pix) >> (4 - (siz))) +#define GS_PIX2DXT(pix, siz) GS_CALC_DXT(GS_PIX2TMEM((pix), (siz))) + +/*===========================================================================* + * Data structures for S2DEX microcode + *===========================================================================*/ + +/*---------------------------------------------------------------------------* + * Background + *---------------------------------------------------------------------------*/ +#define G_BGLT_LOADBLOCK 0x0033 +#define G_BGLT_LOADTILE 0xfff4 + +#define G_BG_FLAG_FLIPS 0x01 +#define G_BG_FLAG_FLIPT 0x10 + +/* Non scalable background plane */ +typedef struct { + u16 imageX; /* x-coordinate of upper-left position of texture (u10.5) */ + u16 imageW; /* width of the texture (u10.2) */ + s16 frameX; /* upper-left position of transferred frame (s10.2) */ + u16 frameW; /* width of transferred frame (u10.2) */ + + u16 imageY; /* y-coordinate of upper-left position of texture (u10.5) */ + u16 imageH; /* height of the texture (u10.2) */ + s16 frameY; /* upper-left position of transferred frame (s10.2) */ + u16 frameH; /* height of transferred frame (u10.2) */ + + u64* imagePtr; /* texture source address on DRAM */ + u16 imageLoad; /* which to use, LoadBlock or LoadTile */ + u8 imageFmt; /* format of texel - G_IM_FMT_* */ + u8 imageSiz; /* size of texel - G_IM_SIZ_* */ + u16 imagePal; /* pallet number */ + u16 imageFlip; /* right & left image inversion (Inverted by G_BG_FLAG_FLIPS) */ + + /* The following is set in the initialization routine guS2DInitBg(). There is no need for the user to set it. */ + u16 tmemW; /* TMEM width and Word size of frame 1 line. + At LoadBlock, GS_PIX2TMEM(imageW/4,imageSiz) + At LoadTile GS_PIX2TMEM(frameW/4,imageSiz)+1 */ + u16 tmemH; /* height of TMEM loadable at a time (s13.2) 4 times value + When the normal texture, 512/tmemW*4 + When the CI texture, 256/tmemW*4 */ + u16 tmemLoadSH; /* SH value + At LoadBlock, tmemSize/2-1 + At LoadTile, tmemW*16-1 */ + u16 tmemLoadTH; /* TH value or Stride value + At LoadBlock, GS_CALC_DXT(tmemW) + At LoadTile, tmemH-1 */ + u16 tmemSizeW; /* skip value of imagePtr for image 1-line + At LoadBlock, tmemW*2 + At LoadTile, GS_PIX2TMEM(imageW/4,imageSiz)*2 */ + u16 tmemSize; /* skip value of imagePtr for 1-loading + = tmemSizeW*tmemH */ +} uObjBg_t; /* 40 bytes */ + +/* Scalable background plane */ +typedef struct { + u16 imageX; /* x-coordinate of upper-left position of texture (u10.5) */ + u16 imageW; /* width of texture (u10.2) */ + s16 frameX; /* upper-left position of transferred frame (s10.2) */ + u16 frameW; /* width of transferred frame (u10.2) */ + + u16 imageY; /* y-coordinate of upper-left position of texture (u10.5) */ + u16 imageH; /* height of texture (u10.2) */ + s16 frameY; /* upper-left position of transferred frame (s10.2) */ + u16 frameH; /* height of transferred frame (u10.2) */ + + u64* imagePtr; /* texture source address on DRAM */ + u16 imageLoad; /* Which to use, LoadBlock or LoadTile? */ + u8 imageFmt; /* format of texel - G_IM_FMT_* */ + u8 imageSiz; /* size of texel - G_IM_SIZ_* */ + u16 imagePal; /* pallet number */ + u16 imageFlip; /* right & left image inversion (Inverted by G_BG_FLAG_FLIPS) */ + + u16 scaleW; /* scale value of X-direction (u5.10) */ + u16 scaleH; /* scale value of Y-direction (u5.10) */ + s32 imageYorig; /* start point of drawing on image (s20.5) */ + + u8 padding[4]; + +} uObjScaleBg_t; /* 40 bytes */ + +typedef union { + uObjBg_t b; + uObjScaleBg_t s; + long long int force_structure_alignment; +} uObjBg; + +/*---------------------------------------------------------------------------* + * 2D Objects + *---------------------------------------------------------------------------*/ +#define G_OBJ_FLAG_FLIPS 1 << 0 /* inversion to S-direction */ +#define G_OBJ_FLAG_FLIPT 1 << 4 /* nversion to T-direction */ + +typedef struct { + s16 objX; /* s10.2 OBJ x-coordinate of upper-left end */ + u16 scaleW; /* u5.10 Scaling of u5.10 width direction */ + u16 imageW; /* u10.5 width of u10.5 texture (length of S-direction) */ + u16 paddingX; /* Unused - Always 0 */ + s16 objY; /* s10.2 OBJ y-coordinate of s10.2 OBJ upper-left end */ + u16 scaleH; /* u5.10 Scaling of u5.10 height direction */ + u16 imageH; /* u10.5 height of u10.5 texture (length of T-direction) */ + u16 paddingY; /* Unused - Always 0 */ + u16 imageStride; /* folding width of texel (In units of 64bit word) */ + u16 imageAdrs; /* texture header position in TMEM (In units of 64bit word) */ + u8 imageFmt; /* format of texel - G_IM_FMT_* */ + u8 imageSiz; /* size of texel - G_IM_SIZ_* */ + u8 imagePal; /* pallet number (0-7) */ + u8 imageFlags; /* The display flag - G_OBJ_FLAG_FLIP* */ +} uObjSprite_t; /* 24 bytes */ + +typedef union { + uObjSprite_t s; + long long int force_structure_alignment; +} uObjSprite; + +/*---------------------------------------------------------------------------* + * 2D Matrix + *---------------------------------------------------------------------------*/ +typedef struct { + s32 A, B, C, D; /* s15.16 */ + s16 X, Y; /* s10.2 */ + u16 BaseScaleX; /* u5.10 */ + u16 BaseScaleY; /* u5.10 */ +} uObjMtx_t; /* 24 bytes */ + +typedef union { + uObjMtx_t m; + long long int force_structure_alignment; +} uObjMtx; + +typedef struct { + s16 X, Y; /* s10.2 */ + u16 BaseScaleX; /* u5.10 */ + u16 BaseScaleY; /* u5.10 */ +} uObjSubMtx_t; /* 8 bytes */ + +typedef union { + uObjSubMtx_t m; + long long int force_structure_alignment; +} uObjSubMtx; + +/*---------------------------------------------------------------------------* + * Loading into TMEM + *---------------------------------------------------------------------------*/ +#define G_OBJLT_TXTRBLOCK 0x00001033 +#define G_OBJLT_TXTRTILE 0x00fc1034 +#define G_OBJLT_TLUT 0x00000030 + +#define GS_TB_TSIZE(pix, siz) (GS_PIX2TMEM((pix), (siz)) - 1) +#define GS_TB_TLINE(pix, siz) (GS_CALC_DXT(GS_PIX2TMEM((pix), (siz)))) + +typedef struct { + u32 type; /* G_OBJLT_TXTRBLOCK divided into types */ + u64* image; /* texture source address on DRAM */ + u16 tmem; /* loaded TMEM word address (8byteWORD) */ + u16 tsize; /* Texture size, Specified by macro GS_TB_TSIZE() */ + u16 tline; /* width of Texture 1-line, Specified by macro GS_TB_TLINE() */ + u16 sid; /* STATE ID Multipled by 4 (Either one of 0, 4, 8 and 12) */ + u32 flag; /* STATE flag */ + u32 mask; /* STATE mask */ +} uObjTxtrBlock_t; /* 24 bytes */ + +#define GS_TT_TWIDTH(pix, siz) ((GS_PIX2TMEM((pix), (siz)) << 2) - 1) +#define GS_TT_THEIGHT(pix, siz) (((pix) << 2) - 1) + +typedef struct { + u32 type; /* G_OBJLT_TXTRTILE divided into types */ + u64* image; /* texture source address on DRAM */ + u16 tmem; /* loaded TMEM word address (8byteWORD)*/ + u16 twidth; /* width of Texture (Specified by macro GS_TT_TWIDTH()) */ + u16 theight; /* height of Texture (Specified by macro GS_TT_THEIGHT()) */ + u16 sid; /* STATE ID Multipled by 4 (Either one of 0, 4, 8 and 12) */ + u32 flag; /* STATE flag */ + u32 mask; /* STATE mask */ +} uObjTxtrTile_t; /* 24 bytes */ + +#define GS_PAL_HEAD(head) ((head) + 256) +#define GS_PAL_NUM(num) ((num)-1) + +typedef struct { + u32 type; /* G_OBJLT_TLUT divided into types */ + u64* image; /* texture source address on DRAM */ + u16 phead; /* pallet number of load header (Between 256 and 511) */ + u16 pnum; /* loading pallet number -1 */ + u16 zero; /* Assign 0 all the time */ + u16 sid; /* STATE ID Multipled by 4 (Either one of 0, 4, 8 and 12)*/ + u32 flag; /* STATE flag */ + u32 mask; /* STATE mask */ +} uObjTxtrTLUT_t; /* 24 bytes */ + +typedef union { + uObjTxtrBlock_t block; + uObjTxtrTile_t tile; + uObjTxtrTLUT_t tlut; + long long int force_structure_alignment; +} uObjTxtr; + +/*---------------------------------------------------------------------------* + * Loading into TMEM & 2D Objects + *---------------------------------------------------------------------------*/ +typedef struct { + uObjTxtr txtr; + uObjSprite sprite; +} uObjTxSprite; /* 48 bytes */ + +/*===========================================================================* + * GBI Commands for S2DEX microcode + *===========================================================================*/ +/* GBI Header */ +#ifdef F3DEX_GBI_2 +#define G_OBJ_RECTANGLE_R 0xda +#define G_OBJ_MOVEMEM 0xdc +#define G_RDPHALF_0 0xe4 +#define G_OBJ_RECTANGLE 0x01 +#define G_OBJ_SPRITE 0x02 +#define G_SELECT_DL 0x04 +#define G_OBJ_LOADTXTR 0x05 +#define G_OBJ_LDTX_SPRITE 0x06 +#define G_OBJ_LDTX_RECT 0x07 +#define G_OBJ_LDTX_RECT_R 0x08 +#define G_BG_1CYC 0x09 +#define G_BG_COPY 0x0a +#define G_OBJ_RENDERMODE 0x0b +#else +#define G_BG_1CYC 0x01 +#define G_BG_COPY 0x02 +#define G_OBJ_RECTANGLE 0x03 +#define G_OBJ_SPRITE 0x04 +#define G_OBJ_MOVEMEM 0x05 +#define G_SELECT_DL 0xb0 +#define G_OBJ_RENDERMODE 0xb1 +#define G_OBJ_RECTANGLE_R 0xb2 +#define G_OBJ_LOADTXTR 0xc1 +#define G_OBJ_LDTX_SPRITE 0xc2 +#define G_OBJ_LDTX_RECT 0xc3 +#define G_OBJ_LDTX_RECT_R 0xc4 +#define G_RDPHALF_0 0xe4 +#endif + +/*---------------------------------------------------------------------------* + * Background wrapped screen + *---------------------------------------------------------------------------*/ +#define gSPBgRectangle(pkt, m, mptr) gDma0p((pkt), (m), (mptr), 0) +#define gsSPBgRectangle(m, mptr) gsDma0p((m), (mptr), 0) +#define gSPBgRectCopy(pkt, mptr) gSPBgRectangle((pkt), G_BG_COPY, (mptr)) +#define gsSPBgRectCopy(mptr) gsSPBgRectangle(G_BG_COPY, (mptr)) +#define gSPBgRect1Cyc(pkt, mptr) gSPBgRectangle((pkt), G_BG_1CYC, (mptr)) +#define gsSPBgRect1Cyc(mptr) gsSPBgRectangle(G_BG_1CYC, (mptr)) + +/*---------------------------------------------------------------------------* + * 2D Objects + *---------------------------------------------------------------------------*/ +#define gSPObjSprite(pkt, mptr) gDma0p((pkt), G_OBJ_SPRITE, (mptr), 0) +#define gsSPObjSprite(mptr) gsDma0p(G_OBJ_SPRITE, (mptr), 0) +#define gSPObjRectangle(pkt, mptr) gDma0p((pkt), G_OBJ_RECTANGLE, (mptr), 0) +#define gsSPObjRectangle(mptr) gsDma0p(G_OBJ_RECTANGLE, (mptr), 0) +#define gSPObjRectangleR(pkt, mptr) gDma0p((pkt), G_OBJ_RECTANGLE_R, (mptr), 0) +#define gsSPObjRectangleR(mptr) gsDma0p(G_OBJ_RECTANGLE_R, (mptr), 0) + +/*---------------------------------------------------------------------------* + * 2D Matrix + *---------------------------------------------------------------------------*/ +#define gSPObjMatrix(pkt, mptr) gDma1p((pkt), G_OBJ_MOVEMEM, (mptr), 0, 23) +#define gsSPObjMatrix(mptr) gsDma1p(G_OBJ_MOVEMEM, (mptr), 0, 23) +#define gSPObjSubMatrix(pkt, mptr) gDma1p((pkt), G_OBJ_MOVEMEM, (mptr), 2, 7) +#define gsSPObjSubMatrix(mptr) gsDma1p(G_OBJ_MOVEMEM, (mptr), 2, 7) + +/*---------------------------------------------------------------------------* + * Loading into TMEM + *---------------------------------------------------------------------------*/ +#define gSPObjLoadTxtr(pkt, tptr) gDma0p((pkt), G_OBJ_LOADTXTR, (tptr), 23) +#define gsSPObjLoadTxtr(tptr) gsDma0p(G_OBJ_LOADTXTR, (tptr), 23) +#define gSPObjLoadTxSprite(pkt, tptr) gDma0p((pkt), G_OBJ_LDTX_SPRITE, (tptr), 47) +#define gsSPObjLoadTxSprite(tptr) gsDma0p(G_OBJ_LDTX_SPRITE, (tptr), 47) +#define gSPObjLoadTxRect(pkt, tptr) gDma0p((pkt), G_OBJ_LDTX_RECT, (tptr), 47) +#define gsSPObjLoadTxRect(tptr) gsDma0p(G_OBJ_LDTX_RECT, (tptr), 47) +#define gSPObjLoadTxRectR(pkt, tptr) gDma0p((pkt), G_OBJ_LDTX_RECT_R, (tptr), 47) +#define gsSPObjLoadTxRectR(tptr) gsDma0p(G_OBJ_LDTX_RECT_R, (tptr), 47) + +/*---------------------------------------------------------------------------* + * Select Display List + *---------------------------------------------------------------------------*/ +#define gSPSelectDL(pkt, mptr, sid, flag, mask) \ + { \ + gDma1p((pkt), G_RDPHALF_0, (flag), (u32)(mptr)&0xffff, (sid)); \ + gDma1p((pkt), G_SELECT_DL, (mask), (u32)(mptr) >> 16, G_DL_PUSH); \ + } +#define gsSPSelectDL(mptr, sid, flag, mask) \ + { \ + gsDma1p(G_RDPHALF_0, (flag), (u32)(mptr)&0xffff, (sid)); \ + gsDma1p(G_SELECT_DL, (mask), (u32)(mptr) >> 16, G_DL_PUSH); \ + } +#define gSPSelectBranchDL(pkt, mptr, sid, flag, mask) \ + { \ + gDma1p((pkt), G_RDPHALF_0, (flag), (u32)(mptr)&0xffff, (sid)); \ + gDma1p((pkt), G_SELECT_DL, (mask), (u32)(mptr) >> 16, G_DL_NOPUSH); \ + } +#define gsSPSelectBranchDL(mptr, sid, flag, mask) \ + { \ + gsDma1p(G_RDPHALF_0, (flag), (u32)(mptr)&0xffff, (sid)); \ + gsDma1p(G_SELECT_DL, (mask), (u32)(mptr) >> 16, G_DL_NOPUSH); \ + } + +/*---------------------------------------------------------------------------* + * Set general status + *---------------------------------------------------------------------------*/ +#define G_MW_GENSTAT 0x08 /* Note that it is the same value of G_MW_FOG */ + +#define gSPSetStatus(pkt, sid, val) gMoveWd((pkt), G_MW_GENSTAT, (sid), (val)) +#define gsSPSetStatus(sid, val) gsMoveWd(G_MW_GENSTAT, (sid), (val)) + +/*---------------------------------------------------------------------------* + * Set Object Render Mode + *---------------------------------------------------------------------------*/ +#define G_OBJRM_NOTXCLAMP 0x01 +#define G_OBJRM_XLU 0x02 /* Ignored */ +#define G_OBJRM_ANTIALIAS 0x04 /* Ignored */ +#define G_OBJRM_BILERP 0x08 +#define G_OBJRM_SHRINKSIZE_1 0x10 +#define G_OBJRM_SHRINKSIZE_2 0x20 +#define G_OBJRM_WIDEN 0x40 + +#define gSPObjRenderMode(pkt, mode) gImmp1((pkt), G_OBJ_RENDERMODE, (mode)) +#define gsSPObjRenderMode(mode) gsImmp1(G_OBJ_RENDERMODE, (mode)) + +/*===========================================================================* + * Render Mode Macro + *===========================================================================*/ +#define RM_RA_SPRITE(clk) \ + AA_EN | CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define G_RM_SPRITE G_RM_OPA_SURF +#define G_RM_SPRITE2 G_RM_OPA_SURF2 +#define G_RM_RA_SPRITE RM_RA_SPRITE(1) +#define G_RM_RA_SPRITE2 RM_RA_SPRITE(2) +#define G_RM_AA_SPRITE G_RM_AA_TEX_TERR +#define G_RM_AA_SPRITE2 G_RM_AA_TEX_TERR2 +#define G_RM_XLU_SPRITE G_RM_XLU_SURF +#define G_RM_XLU_SPRITE2 G_RM_XLU_SURF2 +#define G_RM_AA_XLU_SPRITE G_RM_AA_XLU_SURF +#define G_RM_AA_XLU_SPRITE2 G_RM_AA_XLU_SURF2 + +/*===========================================================================* + * External functions + *===========================================================================*/ +extern u64 gspS2DEX_fifoTextStart[], gspS2DEX_fifoTextEnd[]; +extern u64 gspS2DEX_fifoDataStart[], gspS2DEX_fifoDataEnd[]; +extern u64 gspS2DEX_fifo_dTextStart[], gspS2DEX_fifo_dTextEnd[]; +extern u64 gspS2DEX_fifo_dDataStart[], gspS2DEX_fifo_dDataEnd[]; +extern u64 gspS2DEX2_fifoTextStart[], gspS2DEX2_fifoTextEnd[]; +extern u64 gspS2DEX2_fifoDataStart[], gspS2DEX2_fifoDataEnd[]; +extern u64 gspS2DEX2_xbusTextStart[], gspS2DEX2_xbusTextEnd[]; +extern u64 gspS2DEX2_xbusDataStart[], gspS2DEX2_xbusDataEnd[]; +extern void guS2DInitBg(uObjBg*); + +#ifdef F3DEX_GBI_2 +#define guS2DEmuBgRect1Cyc guS2D2EmuBgRect1Cyc /*Wrapper*/ +#define guS2DEmuSetScissor guS2D2EmuSetScissor /*Wrapper*/ +extern void guS2D2EmuSetScissor(u32, u32, u32, u32, u8); +extern void guS2D2EmuBgRect1Cyc(Gfx**, uObjBg*); +#else +extern void guS2DEmuSetScissor(u32, u32, u32, u32, u8); +extern void guS2DEmuBgRect1Cyc(Gfx**, uObjBg*); +#endif + +#ifdef _LANGUAGE_C_PLUS_PLUS +} +#endif +#endif /* GS2DEX_H */ + +/*======== End of gs2dex.h ========*/ diff --git a/libultraship/include/libultraship/libultra/gu.h b/libultraship/include/libultraship/libultra/gu.h new file mode 100644 index 000000000..535140a92 --- /dev/null +++ b/libultraship/include/libultraship/libultra/gu.h @@ -0,0 +1,201 @@ +#pragma once + +#include "types.h" +#include "gbi.h" +#include "sptask.h" + +#define GU_PI 3.1415926 + +#define ROUND(x) (s32)(((x) >= 0.0) ? ((x) + 0.5) : ((x)-0.5)) + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define D_PI 3.14159265358979323846 +#define D_DTOR (3.14159265358979323846 / 180.0) + +#define FTOFIX32(x) (long)((x) * (float)0x00010000) +#define FIX32TOF(x) ((float)(x) * (1.0f / (float)0x00010000)) +#define FTOFRAC8(x) ((int)MIN(((x) * (128.0f)), 127.0f) & 0xff) + +#define FILTER_WRAP 0 +#define FILTER_CLAMP 1 + +#define RAND(x) (guRandom() % x) /* random number between 0 to x */ + +/* + * Data Structures + */ +typedef struct { + unsigned char* base; + int fmt, siz; + int xsize, ysize; + int lsize; + /* current tile info */ + int addr; + int w, h; + int s, t; +} Image; + +typedef struct { + float col[3]; + float pos[3]; + float a1, a2; /* actual color = col/(a1*dist + a2) */ +} PositionalLight; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Function Prototypes + */ + +extern int guLoadTextureBlockMipMap(Gfx** glist, unsigned char* tbuf, Image* im, unsigned char startTile, + unsigned char pal, unsigned char cms, unsigned char cmt, unsigned char masks, + unsigned char maskt, unsigned char shifts, unsigned char shiftt, unsigned char cfs, + unsigned char cft); + +extern int guGetDPLoadTextureTileSz(int ult, int lrt); +extern void guDPLoadTextureTile(Gfx* glistp, void* timg, int texl_fmt, int texl_size, int img_width, int img_height, + int uls, int ult, int lrs, int lrt, int palette, int cms, int cmt, int masks, int maskt, + int shifts, int shiftt); + +/* + * matrix operations: + * + * The 'F' version is floating point, in case the application wants + * to do matrix manipulations and convert to fixed-point at the last + * minute. + */ +extern void guMtxIdent(Mtx* m); +extern void guMtxIdentF(float mf[4][4]); +extern void guOrtho(Mtx* m, float l, float r, float b, float t, float n, float f, float scale); +extern void guOrthoF(float mf[4][4], float l, float r, float b, float t, float n, float f, float scale); +extern void guFrustum(Mtx* m, float l, float r, float b, float t, float n, float f, float scale); +extern void guFrustumF(float mf[4][4], float l, float r, float b, float t, float n, float f, float scale); +extern void guPerspective(Mtx* m, u16* perspNorm, float fovy, float aspect, float near, float far, float scale); +extern void guPerspectiveF(float mf[4][4], u16* perspNorm, float fovy, float aspect, float near, float far, + float scale); +extern void guLookAt(Mtx* m, float xEye, float yEye, float zEye, float xAt, float yAt, float zAt, float xUp, float yUp, + float zUp); +extern void guLookAtF(float mf[4][4], float xEye, float yEye, float zEye, float xAt, float yAt, float zAt, float xUp, + float yUp, float zUp); +extern void guLookAtReflect(Mtx* m, LookAt* l, float xEye, float yEye, float zEye, float xAt, float yAt, float zAt, + float xUp, float yUp, float zUp); +extern void guLookAtReflectF(float mf[4][4], LookAt* l, float xEye, float yEye, float zEye, float xAt, float yAt, + float zAt, float xUp, float yUp, float zUp); +extern void guLookAtHilite(Mtx* m, LookAt* l, Hilite* h, float xEye, float yEye, float zEye, float xAt, float yAt, + float zAt, float xUp, float yUp, float zUp, float xl1, float yl1, float zl1, float xl2, + float yl2, float zl2, int twidth, int theight); +extern void guLookAtHiliteF(float mf[4][4], LookAt* l, Hilite* h, float xEye, float yEye, float zEye, float xAt, + float yAt, float zAt, float xUp, float yUp, float zUp, float xl1, float yl1, float zl1, + float xl2, float yl2, float zl2, int twidth, int theight); +extern void guLookAtStereo(Mtx* m, float xEye, float yEye, float zEye, float xAt, float yAt, float zAt, float xUp, + float yUp, float zUp, float eyedist); +extern void guLookAtStereoF(float mf[4][4], float xEye, float yEye, float zEye, float xAt, float yAt, float zAt, + float xUp, float yUp, float zUp, float eyedist); +extern void guRotate(Mtx* m, float a, float x, float y, float z); +extern void guRotateF(float mf[4][4], float a, float x, float y, float z); +extern void guRotateRPY(Mtx* m, float r, float p, float y); +extern void guRotateRPYF(float mf[4][4], float r, float p, float h); +extern void guAlign(Mtx* m, float a, float x, float y, float z); +extern void guAlignF(float mf[4][4], float a, float x, float y, float z); +extern void guScale(Mtx* m, float x, float y, float z); +extern void guScaleF(float mf[4][4], float x, float y, float z); +extern void guTranslate(Mtx* m, float x, float y, float z); +extern void guTranslateF(float mf[4][4], float x, float y, float z); +extern void guPosition(Mtx* m, float r, float p, float h, float s, float x, float y, float z); +extern void guPositionF(float mf[4][4], float r, float p, float h, float s, float x, float y, float z); +extern void guMtxF2L(float mf[4][4], Mtx* m); +extern void guMtxL2F(float mf[4][4], Mtx* m); +extern void guMtxCatF(float m[4][4], float n[4][4], float r[4][4]); +extern void guMtxCatL(Mtx* m, Mtx* n, Mtx* res); +extern void guMtxXFMF(float mf[4][4], float x, float y, float z, float* ox, float* oy, float* oz); +extern void guMtxXFML(Mtx* m, float x, float y, float z, float* ox, float* oy, float* oz); + +/* vector utility: */ +extern void guNormalize(float* x, float* y, float* z); + +/* light utilities: */ +void guPosLight(PositionalLight* pl, Light* l, float xOb, float yOb, float zOb); +void guPosLightHilite(PositionalLight* pl1, PositionalLight* pl2, Light* l1, Light* l2, LookAt* l, Hilite* h, + float xEye, float yEye, float zEye, float xOb, float yOb, float zOb, float xUp, float yUp, + float zUp, int twidth, int theight); +extern int guRandom(void); + +/* + * Math functions + */ +// extern float sinf(float angle); +// extern float cosf(float angle); +// extern signed short sins (unsigned short angle); +// extern signed short coss (unsigned short angle); +extern float guSqrtf(float value); + +/* + * Dump routines for low-level display lists + */ +/* flag values for guParseRdpDL() */ +#define GU_PARSERDP_VERBOSE 1 +#define GU_PARSERDP_PRAREA 2 +#define GU_PARSERDP_PRHISTO 4 +#define GU_PARSERDP_DUMPONLY 32 /* doesn't need to be same as */ + /* GU_PARSEGBI_DUMPOLNY, but this */ + /* allows app to use interchangeably */ + +extern void guParseRdpDL(u64* rdp_dl, u64 nbytes, u8 flags); +extern void guParseString(char* StringPointer, u64 nbytes); + +/* + * NO LONGER SUPPORTED, + * use guParseRdpDL with GU_PARSERDP_DUMPONLY flags + */ +/* extern void guDumpRawRdpDL(u64 *rdp_dl, u64 nbytes); */ + +/* flag values for guBlinkRdpDL() */ +#define GU_BLINKRDP_HILITE 1 +#define GU_BLINKRDP_EXTRACT 2 + +extern void guBlinkRdpDL(u64* rdp_dl_in, u64 nbytes_in, u64* rdp_dl_out, u64* nbytes_out, u32 x, u32 y, u32 radius, + u8 red, u8 green, u8 blue, u8 flags); + +/* flag values for guParseGbiDL() */ +#define GU_PARSEGBI_ROWMAJOR 1 +#define GU_PARSEGBI_NONEST 2 +#define GU_PARSEGBI_FLTMTX 4 +#define GU_PARSEGBI_SHOWDMA 8 +#define GU_PARSEGBI_ALLMTX 16 +#define GU_PARSEGBI_DUMPONLY 32 +/* +#define GU_PARSEGBI_HANGAFTER 64 +#define GU_PARSEGBI_NOTEXTURES 128 +*/ +extern void guParseGbiDL(u64* gbi_dl, u32 nbytes, u8 flags); +extern void guDumpGbiDL(OSTask* tp, u8 flags); + +#define GU_PARSE_GBI_TYPE 1 +#define GU_PARSE_RDP_TYPE 2 +#define GU_PARSE_READY 3 +#define GU_PARSE_MEM_BLOCK 4 +#define GU_PARSE_ABI_TYPE 5 +#define GU_PARSE_STRING_TYPE 6 + +typedef struct { + int dataSize; + int dlType; + int flags; + u32 paddr; +} guDLPrintCB; + +void guSprite2DInit(uSprite* SpritePointer, void* SourceImagePointer, void* TlutPointer, int Stride, int SubImageWidth, + int SubImageHeight, int SourceImageType, int SourceImageBitSize, int SourceImageOffsetS, + int SourceImageOffsetT); + +#ifdef __cplusplus +} +#endif diff --git a/libultraship/include/libultraship/libultra/internal.h b/libultraship/include/libultraship/libultra/internal.h new file mode 100644 index 000000000..1433033a8 --- /dev/null +++ b/libultraship/include/libultraship/libultra/internal.h @@ -0,0 +1,27 @@ +#pragma once + +#include "pi.h" + +typedef struct { + /* 0x00 */ u32 initialized; + /* 0x04 */ OSThread* mgrThread; + /* 0x08 */ OSMesgQueue* cmdQueue; + /* 0x0C */ OSMesgQueue* eventQueue; + /* 0x10 */ OSMesgQueue* accessQueue; + /* 0x14 */ s32 (*piDmaCallback)(s32, u32, void*, size_t); + /* 0x18 */ s32 (*epiDmaCallback)(OSPiHandle*, s32, u32, void*, size_t); +} OSMgrArgs; // size = 0x1C + +typedef struct { + /* 0x00 */ OSMesgQueue* queue; + /* 0x04 */ OSMesg msg; +} __OSEventState; // size = 0x08 + +#ifdef __cplusplus +extern "C" { +#endif +extern OSMgrArgs __osPiDevMgr; +extern __OSEventState __osEventStateTab[]; +#ifdef __cplusplus +} +#endif diff --git a/libultraship/include/libultraship/libultra/interrupt.h b/libultraship/include/libultraship/libultra/interrupt.h new file mode 100644 index 000000000..626ef4192 --- /dev/null +++ b/libultraship/include/libultraship/libultra/interrupt.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +typedef uint32_t OSIntMask; diff --git a/libultraship/include/libultraship/libultra/mbi.h b/libultraship/include/libultraship/libultra/mbi.h new file mode 100644 index 000000000..3019a6ce4 --- /dev/null +++ b/libultraship/include/libultraship/libultra/mbi.h @@ -0,0 +1,42 @@ +#ifndef ULTRA64_MBI_H +#define ULTRA64_MBI_H + +/* + * Header file for the Media Binary Interface + * + * NOTE: This file is included by the RSP microcode, so any C-specific + * constructs must be bracketed by #ifdef _LANGUAGE_C + * + */ + +/* + * the SHIFT macros are used to build display list commands, inserting + * bit-fields into a 32-bit word. They take a value, a shift amount, + * and a width. + * + * For the left shift, the lower bits of the value are masked, + * then shifted left. + * + * For the right shift, the value is shifted right, then the lower bits + * are masked. + * + * (NOTE: _SHIFTL(v, 0, 32) won't work, just use an assignment) + * + */ +#define _SHIFTL(v, s, w) ((u32)(((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +#define G_ON (1) +#define G_OFF (0) + +#include +#include "types.h" +#include "gbi.h" +#include "abi.h" + +#define NUM_SEGMENTS (16) +#define SEGMENT_OFFSET(a) ((u32)(a)&0x00FFFFFF) +#define SEGMENT_NUMBER(a) (((u32)(a) << 4) >> 28) +#define SEGMENT_ADDR(num, off) (((num) << 24) + (off)) + +#endif diff --git a/libultraship/include/libultraship/libultra/message.h b/libultraship/include/libultraship/libultra/message.h new file mode 100644 index 000000000..dca268f79 --- /dev/null +++ b/libultraship/include/libultraship/libultra/message.h @@ -0,0 +1,66 @@ +#ifndef ULTRA64_MESSAGE_H +#define ULTRA64_MESSAGE_H + +#include "thread.h" + +#define OS_MESG_NOBLOCK 0 +#define OS_MESG_BLOCK 1 + +typedef union { + u8 data8; + u16 data16; + u32 data32; + void* ptr; +} OSMesg; + +#define OS_MESG_8(x) ((OSMesg){ .data8 = (x) }) +#define OS_MESG_16(x) ((OSMesg){ .data16 = (x) }) +#define OS_MESG_32(x) ((OSMesg){ .data32 = (x) }) +#define OS_MESG_PTR(x) ((OSMesg){ .ptr = (x) }) + +#define osSendMesg8(queue, msg, flag) osSendMesg(queue, OS_MESG_8(msg), flag) +#define osSendMesg16(queue, msg, flag) osSendMesg(queue, OS_MESG_16(msg), flag) +#define osSendMesg32(queue, msg, flag) osSendMesg(queue, OS_MESG_32(msg), flag) +#define osSendMesgPtr(queue, msg, flag) osSendMesg(queue, OS_MESG_PTR(msg), flag) + +typedef u32 OSEvent; + +#define OS_NUM_EVENTS 15 + +#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */ +#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */ +#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */ +#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */ +#define OS_EVENT_SP 4 /* SP task done interrupt */ +#define OS_EVENT_SI 5 /* SI (controller) interrupt */ +#define OS_EVENT_AI 6 /* AI interrupt */ +#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */ +#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */ +#define OS_EVENT_DP 9 /* DP full sync interrupt */ +#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */ +#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */ +#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */ +#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */ +#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */ + +typedef struct OSMesgQueue { + /* 0x00 */ OSThread* mtqueue; + /* 0x04 */ OSThread* fullqueue; + /* 0x08 */ s32 validCount; + /* 0x0C */ s32 first; + /* 0x10 */ s32 msgCount; + /* 0x14 */ OSMesg* msg; +} OSMesgQueue; // size = 0x18 + +#ifdef __cplusplus +extern "C" { +#endif +void osCreateMesgQueue(OSMesgQueue* mq, OSMesg* msgBuf, int32_t count); +int32_t osSendMesg(OSMesgQueue* mq, OSMesg msg, int32_t flag); +int32_t osJamMesg(OSMesgQueue* mq, OSMesg msg, int32_t flag); +int32_t osRecvMesg(OSMesgQueue* mq, OSMesg* msg, int32_t flag); +void osSetEventMesg(OSEvent event, OSMesgQueue* mq, OSMesg msg); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libultraship/include/libultraship/libultra/motor.h b/libultraship/include/libultraship/libultra/motor.h new file mode 100644 index 000000000..c6274fad9 --- /dev/null +++ b/libultraship/include/libultraship/libultra/motor.h @@ -0,0 +1,22 @@ +#ifndef ULTRA64_MOTOR_H +#define ULTRA64_MOTOR_H + +#include "pfs.h" + +#define MOTOR_START 1 +#define MOTOR_STOP 0 + +#define osMotorStart(x) __osMotorAccess((x), MOTOR_START) +#define osMotorStop(x) __osMotorAccess((x), MOTOR_STOP) + +#ifdef __cplusplus +extern "C" { +#endif + +s32 __osMotorAccess(OSPfs* pfs, u32 vibrate); +s32 osMotorInit(OSMesgQueue* ctrlrqueue, OSPfs* pfs, s32 channel); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libultraship/include/libultraship/libultra/os.h b/libultraship/include/libultraship/libultra/os.h new file mode 100644 index 000000000..bd11553a4 --- /dev/null +++ b/libultraship/include/libultraship/libultra/os.h @@ -0,0 +1,152 @@ +#pragma once + +#ifndef OS_H +#define OS_H + +#include +#include "controller.h" +#include "message.h" +#include "time.h" +#include "pi.h" +#include "vi.h" + +#ifndef _HW_VERSION_1 +#define MAXCONTROLLERS 4 +#else +#define MAXCONTROLLERS 6 +#endif + +/* controller errors */ +#define CONT_NO_RESPONSE_ERROR 0x8 +#define CONT_OVERRUN_ERROR 0x4 +#define CONT_RANGE_ERROR -1 +#ifdef _HW_VERSION_1 +#define CONT_FRAME_ERROR 0x2 +#define CONT_COLLISION_ERROR 0x1 +#endif + +/* Controller type */ + +#define CONT_ABSOLUTE 0x0001 +#define CONT_RELATIVE 0x0002 +#define CONT_JOYPORT 0x0004 +#define CONT_EEPROM 0x8000 +#define CONT_EEP16K 0x4000 +#define CONT_TYPE_MASK 0x1F07 +#define CONT_TYPE_NORMAL 0x0005 +#define CONT_TYPE_MOUSE 0x0002 +#define CONT_TYPE_VOICE 0x0100 + +/* Controller status */ + +#define CONT_CARD_ON 0x01 +#define CONT_CARD_PULL 0x02 +#define CONT_ADDR_CRC_ER 0x04 +#define CONT_EEPROM_BUSY 0x80 + +/* Buttons */ + +#define CONT_A 0x8000 +#define CONT_B 0x4000 +#define CONT_G 0x2000 +#define CONT_START 0x1000 +#define CONT_UP 0x0800 +#define CONT_DOWN 0x0400 +#define CONT_LEFT 0x0200 +#define CONT_RIGHT 0x0100 +#define CONT_L 0x0020 +#define CONT_R 0x0010 +#define CONT_E 0x0008 +#define CONT_D 0x0004 +#define CONT_C 0x0002 +#define CONT_F 0x0001 + +/* Nintendo's official button names */ + +#define A_BUTTON CONT_A +#define B_BUTTON CONT_B +#define L_TRIG CONT_L +#define R_TRIG CONT_R +#define Z_TRIG CONT_G +#define START_BUTTON CONT_START +#define U_JPAD CONT_UP +#define L_JPAD CONT_LEFT +#define R_JPAD CONT_RIGHT +#define D_JPAD CONT_DOWN +#define U_CBUTTONS CONT_E +#define L_CBUTTONS CONT_C +#define R_CBUTTONS CONT_F +#define D_CBUTTONS CONT_D + +/* Controller error number */ + +#define CONT_ERR_NO_CONTROLLER PFS_ERR_NOPACK /* 1 */ +#define CONT_ERR_CONTRFAIL CONT_OVERRUN_ERROR /* 4 */ +#define CONT_ERR_INVALID PFS_ERR_INVALID /* 5 */ +#define CONT_ERR_DEVICE PFS_ERR_DEVICE /* 11 */ +#define CONT_ERR_NOT_READY 12 +#define CONT_ERR_VOICE_MEMORY 13 +#define CONT_ERR_VOICE_WORD 14 +#define CONT_ERR_VOICE_NO_RESPONSE 15 + +// EEPROM + +#define EEPROM_TYPE_4K 0x01 +#define EEPROM_TYPE_16K 0x02 + +#define EEPROM_MAXBLOCKS 64 +#define EEP16K_MAXBLOCKS 256 +#define EEPROM_BLOCK_SIZE 8 + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t osContInit(OSMesgQueue* mq, uint8_t* controllerBits, OSContStatus* status); +int32_t osContStartReadData(OSMesgQueue* mesg); +void osContGetReadData(OSContPad* pad); +uint8_t osContGetStatus(uint8_t controller); + +void osWritebackDCacheAll(); +void osInvalDCache(void* p, int32_t l); +void osInvalICache(void* p, int32_t x); +void osWritebackDCache(void* p, int32_t x); + +s32 osPiStartDma(OSIoMesg* mb, s32 priority, s32 direction, uintptr_t devAddr, void* vAddr, size_t nbytes, + OSMesgQueue* mq); +void osViSwapBuffer(void*); +void osViBlack(uint8_t active); +void osViFade(u8, u16); +void osViRepeatLine(u8); +void osViSetXScale(f32); +void osViSetYScale(f32); +void osViSetSpecialFeatures(u32); +void osViSetMode(OSViMode*); +void osViSetEvent(OSMesgQueue*, OSMesg, u32); +void osCreateViManager(OSPri); +void osCreatePiManager(OSPri pri, OSMesgQueue* cmdQ, OSMesg* cmdBuf, s32 cmdMsgCnt); + +void osSetTime(OSTime time); +uint64_t osGetTime(void); +uint32_t osGetCount(void); +s32 osEepromProbe(OSMesgQueue*); +s32 osEepromRead(OSMesgQueue*, u8, u8*); +s32 osEepromWrite(OSMesgQueue*, u8, u8*); +s32 osEepromLongRead(OSMesgQueue*, u8, u8*, int); +s32 osEepromLongWrite(OSMesgQueue*, u8, u8*, int); + +int osSetTimer(OSTimer* t, OSTime countdown, OSTime interval, OSMesgQueue* mq, OSMesg msg); + +s32 osAiSetFrequency(u32 freq); +OSPiHandle* osCartRomInit(void); +s32 osEPiStartDma(OSPiHandle* pihandle, OSIoMesg* mb, s32 direction); + +s32 osAiSetFrequency(u32); +s32 osAiSetNextBuffer(void*, size_t); +u32 osAiGetLength(void); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/libultraship/include/libultraship/libultra/pfs.h b/libultraship/include/libultraship/libultra/pfs.h new file mode 100644 index 000000000..faea235b5 --- /dev/null +++ b/libultraship/include/libultraship/libultra/pfs.h @@ -0,0 +1,134 @@ +#pragma once + +#include "message.h" + +/* File System size */ +#define OS_PFS_VERSION 0x0200 +#define OS_PFS_VERSION_HI (OS_PFS_VERSION >> 8) +#define OS_PFS_VERSION_LO (OS_PFS_VERSION & 255) + +#define PFS_INODE_SIZE_PER_PAGE 128 +#define PFS_FILE_NAME_LEN 16 +#define PFS_FILE_EXT_LEN 4 +#define BLOCKSIZE 32 +#define PFS_ONE_PAGE 8 +#define PFS_MAX_BANKS 62 + +/* File System flag */ + +#define PFS_READ 0 +#define PFS_WRITE 1 +#define PFS_CREATE 2 + +/* File System status */ +#define PFS_INITIALIZED 0x1 +#define PFS_CORRUPTED 0x2 +#define PFS_ID_BROKEN 0x4 +#define PFS_MOTOR_INITIALIZED 0x8 +#define PFS_GBPAK_INITIALIZED 0x10 + +/* Definition for page usage */ +#define PFS_EOF 1 +#define PFS_PAGE_NOT_EXIST 2 +#define PFS_PAGE_NOT_USED 3 + +/* File System error number */ + +#define PFS_ERR_NOPACK 1 /* no memory card is plugged or */ +#define PFS_ERR_NEW_PACK 2 /* ram pack has been changed to a different one */ +#define PFS_ERR_INCONSISTENT 3 /* need to run Pfschecker*/ +#define PFS_ERR_CONTRFAIL CONT_OVERRUN_ERROR +#define PFS_ERR_INVALID 5 /* invalid parameter or file not exist*/ +#define PFS_ERR_BAD_DATA 6 /* the data read from pack are bad*/ +#define PFS_DATA_FULL 7 /* no free pages on ram pack*/ +#define PFS_DIR_FULL 8 /* no free directories on ram pack*/ +#define PFS_ERR_EXIST 9 /* file exists*/ +#define PFS_ERR_ID_FATAL 10 /* dead ram pack */ +#define PFS_ERR_DEVICE 11 /* wrong device type*/ +#define PFS_ERR_NO_GBCART 12 /* no gb cartridge (64GB-PAK) */ +#define PFS_ERR_NEW_GBCART 13 /* gb cartridge may be changed */ + +/* Definition for bank */ +#define PFS_ID_BANK_256K 0 +#define PFS_ID_BANK_1M 4 +#define PFS_BANKS_256K 1 + +#define PFS_WRITTEN 2 +#define DEF_DIR_PAGES 2 + +#define PFS_ID_0AREA 1 +#define PFS_ID_1AREA 3 +#define PFS_ID_2AREA 4 +#define PFS_ID_3AREA 6 +#define PFS_LABEL_AREA 7 +#define PFS_ID_PAGE PFS_ONE_PAGE * 0 + +#define PFS_BANK_LAPPED_BY 8 /* => u8 */ +#define PFS_SECTOR_PER_BANK 32 +#define PFS_INODE_DIST_MAP (PFS_BANK_LAPPED_BY * PFS_SECTOR_PER_BANK) +#define PFS_SECTOR_SIZE (PFS_INODE_SIZE_PER_PAGE / PFS_SECTOR_PER_BANK) + +typedef struct { + /* 0x00 */ s32 status; + /* 0x04 */ OSMesgQueue* queue; + /* 0x08 */ s32 channel; + /* 0x0C */ u8 id[32]; + /* 0x2C */ u8 label[32]; + /* 0x4C */ s32 version; + /* 0x50 */ s32 dir_size; + /* 0x54 */ s32 inode_table; /* block location */ + /* 0x58 */ s32 minode_table; /* mirrioring inode_table */ + /* 0x5C */ s32 dir_table; /* block location */ + /* 0x60 */ s32 inodeStartPage; /* page # */ + /* 0x64 */ u8 banks; + /* 0x65 */ u8 activebank; +} OSPfs; // size = 0x68 + +typedef struct { + /* 0x00 */ u32 file_size; /* bytes */ + /* 0x04 */ u32 game_code; + /* 0x08 */ u16 company_code; + /* 0x0C */ char ext_name[4]; + /* 0x10 */ char game_name[16]; +} OSPfsState; // size = 0x20 + +typedef union { + struct { + /* 0x00 */ u8 bank; + /* 0x01 */ u8 page; + } inode_t; + /* 0x00 */ u16 ipage; +} __OSInodeUnit; // size = 0x02 + +typedef struct { + /* 0x00 */ __OSInodeUnit inodePage[128]; +} __OSInode; // size = 0x100 + +typedef struct { + /* 0x00 */ u32 game_code; + /* 0x04 */ u16 company_code; + /* 0x06 */ __OSInodeUnit start_page; + /* 0x08 */ u8 status; + /* 0x09 */ s8 reserved; + /* 0x0A */ u16 data_sum; + /* 0x0C */ u8 ext_name[PFS_FILE_EXT_LEN]; + /* 0x10 */ u8 game_name[PFS_FILE_NAME_LEN]; +} __OSDir; // size = 0x20 + +typedef struct { + /* 0x00 */ u32 repaired; + /* 0x04 */ u32 random; + /* 0x08 */ u64 serialMid; + /* 0x10 */ u64 serialLow; + /* 0x18 */ u16 deviceid; + /* 0x1A */ u8 banks; + /* 0x1B */ u8 version; + /* 0x1C */ u16 checksum; + /* 0x1E */ u16 invertedChecksum; +} __OSPackId; // size = 0x20 + +typedef struct { + /* 0x000 */ __OSInode inode; + /* 0x100 */ u8 bank; + /* 0x101 */ u8 map[PFS_INODE_DIST_MAP]; +} __OSInodeCache; // size = 0x202 diff --git a/libultraship/include/libultraship/libultra/pi.h b/libultraship/include/libultraship/libultra/pi.h new file mode 100644 index 000000000..1624900fe --- /dev/null +++ b/libultraship/include/libultraship/libultra/pi.h @@ -0,0 +1,79 @@ +#pragma once +#include "message.h" + +typedef struct { + /* 0x00 */ u32 errStatus; + /* 0x04 */ void* dramAddr; + /* 0x08 */ void* C2Addr; + /* 0x0C */ u32 sectorSize; + /* 0x10 */ u32 C1ErrNum; + /* 0x14 */ u32 C1ErrSector[4]; +} __OSBlockInfo; // size = 0x24 + +typedef struct { + /* 0x00 */ u32 cmdType; + /* 0x04 */ u16 transferMode; + /* 0x06 */ u16 blockNum; + /* 0x08 */ s32 sectorNum; + /* 0x0C */ u32 devAddr; + /* 0x10 */ u32 bmCtlShadow; + /* 0x14 */ u32 seqCtlShadow; + /* 0x18 */ __OSBlockInfo block[2]; +} __OSTranxInfo; // size = 0x60 + +typedef struct OSPiHandle { + /* 0x00 */ struct OSPiHandle* next; + /* 0x04 */ u8 type; + /* 0x05 */ u8 latency; + /* 0x06 */ u8 pageSize; + /* 0x07 */ u8 relDuration; + /* 0x08 */ u8 pulse; + /* 0x09 */ u8 domain; + /* 0x0C */ u32 baseAddress; + /* 0x10 */ u32 speed; + /* 0x14 */ __OSTranxInfo transferInfo; +} OSPiHandle; // size = 0x74 + +typedef struct { + /* 0x00 */ u8 type; + /* 0x04 */ u32 address; +} OSPiInfo; // size = 0x08 + +typedef struct { + /* 0x00 */ u16 type; + /* 0x02 */ u8 pri; + /* 0x03 */ u8 status; + /* 0x04 */ OSMesgQueue* retQueue; +} OSIoMesgHdr; // size = 0x08 + +typedef struct { + /* 0x00 */ OSIoMesgHdr hdr; + /* 0x08 */ void* dramAddr; + /* 0x0C */ u32 devAddr; + /* 0x10 */ size_t size; + /* 0x14 */ OSPiHandle* piHandle; +} OSIoMesg; // size = 0x18 + +#define OS_READ 0 // device -> RDRAM +#define OS_WRITE 1 // device <- RDRAM +#define OS_OTHERS 2 // for disk drive transfers + +#define PI_DOMAIN1 0 +#define PI_DOMAIN2 1 + +#define OS_MESG_TYPE_LOOPBACK 10 +#define OS_MESG_TYPE_DMAREAD 11 +#define OS_MESG_TYPE_DMAWRITE 12 +#define OS_MESG_TYPE_VRETRACE 13 +#define OS_MESG_TYPE_COUNTER 14 +#define OS_MESG_TYPE_EDMAREAD 15 +#define OS_MESG_TYPE_EDMAWRITE 16 + +#define OS_MESG_PRI_NORMAL 0 +#define OS_MESG_PRI_HIGH 1 + +#define DEVICE_TYPE_CART 0 /* ROM cartridge */ +#define DEVICE_TYPE_BULK 1 /* ROM bulk */ +#define DEVICE_TYPE_64DD 2 /* 64 Disk Drive */ +#define DEVICE_TYPE_SRAM 3 /* SRAM */ +#define DEVICE_TYPE_INIT 7 /* initial value */ diff --git a/libultraship/include/libultraship/libultra/printf.h b/libultraship/include/libultraship/libultra/printf.h new file mode 100644 index 000000000..f5ece686a --- /dev/null +++ b/libultraship/include/libultraship/libultra/printf.h @@ -0,0 +1,30 @@ +#pragma once + +#include "types.h" + +typedef struct { + /* 0x0 */ union { + /* 0x0 */ s64 ll; + /* 0x0 */ f64 ld; + } v; + /* 0x8 */ char* s; + /* 0xC */ s32 n0; + /* 0x10 */ s32 nz0; + /* 0x14 */ s32 n1; + /* 0x18 */ s32 nz1; + /* 0x1C */ s32 n2; + /* 0x20 */ s32 nz2; + /* 0x24 */ s32 prec; + /* 0x28 */ s32 width; + /* 0x2C */ u32 nchar; + /* 0x30 */ u32 flags; + /* 0x34 */ u8 qual; +} _Pft; // size = 0x38 + +typedef void* (*PrintCallback)(void*, const char*, u32); + +#define FLAGS_SPACE 1 +#define FLAGS_PLUS 2 +#define FLAGS_MINUS 4 +#define FLAGS_HASH 8 +#define FLAGS_ZERO 16 diff --git a/libultraship/include/libultraship/libultra/r4300.h b/libultraship/include/libultraship/libultra/r4300.h new file mode 100644 index 000000000..ac7d7c298 --- /dev/null +++ b/libultraship/include/libultraship/libultra/r4300.h @@ -0,0 +1,364 @@ +#pragma once + +#ifdef _LANGUAGE_C +#include "types.h" +#define U32(x) ((u32)x) +#define C_REG(x) (x) +#else +#define U32(x) (x) +#define C_REG(x) $x +#endif + +// Segment base addresses and sizes +#define KUBASE 0 +#define KUSIZE 0x80000000 +#define K0BASE 0x80000000 +#define K0SIZE 0x20000000 +#define K1BASE 0xA0000000 +#define K1SIZE 0x20000000 +#define K2BASE 0xC0000000 +#define K2SIZE 0x20000000 + +// Exception vectors +#define SIZE_EXCVEC 0x80 // Size of an exc. vec +#define UT_VEC K0BASE // utlbmiss vector +#define R_VEC (K1BASE + 0x1FC00000) // reset vector +#define XUT_VEC (K0BASE + 0x80) // extended address tlbmiss +#define ECC_VEC (K0BASE + 0x100) // Ecc exception vector +#define E_VEC (K0BASE + 0x180) // Gen. exception vector + +// Address conversion macros +#define K0_TO_K1(x) (U32(x) | 0xA0000000) // kseg0 to kseg1 +#define K1_TO_K0(x) (U32(x) & 0x9FFFFFFF) // kseg1 to kseg0 +#define K0_TO_PHYS(x) (U32(x) & 0x1FFFFFFF) // kseg0 to physical +#define K1_TO_PHYS(x) (U32(x) & 0x1FFFFFFF) // kseg1 to physical +#define KDM_TO_PHYS(x) (U32(x) & 0x1FFFFFFF) // direct mapped to physical +#define PHYS_TO_K0(x) (U32(x) | 0x80000000) // physical to kseg0 +#define PHYS_TO_K1(x) (U32(x) | 0xA0000000) // physical to kseg1 + +// Address predicates +#define IS_KSEG0(x) (U32(x) >= K0BASE && U32(x) < K1BASE) +#define IS_KSEG1(x) (U32(x) >= K1BASE && U32(x) < K2BASE) +#define IS_KSEGDM(x) (U32(x) >= K0BASE && U32(x) < K2BASE) +#define IS_KSEG2(x) (U32(x) >= K2BASE && U32(x) < KPTE_SHDUBASE) +#define IS_KPTESEG(x) (U32(x) >= KPTE_SHDUBASE) +#define IS_KUSEG(x) (U32(x) < K0BASE) + +// TLB size constants +#define NTLBENTRIES 31 /* entry 31 is reserved by rdb */ + +#define TLBHI_VPN2MASK 0xFFFFE000 +#define TLBHI_VPN2SHIFT 13 +#define TLBHI_PIDMASK 0xFF +#define TLBHI_PIDSHIFT 0 +#define TLBHI_NPID 255 // 255 to fit in 8 bits + +#define TLBLO_PFNMASK 0x3FFFFFC0 +#define TLBLO_PFNSHIFT 6 +#define TLBLO_CACHMASK 0x38 // cache coherency algorithm +#define TLBLO_CACHSHIFT 3 +#define TLBLO_UNCACHED 0x10 // not cached +#define TLBLO_NONCOHRNT 0x18 // Cacheable non-coherent +#define TLBLO_EXLWR 0x28 // Exclusive write +#define TLBLO_D 0x4 // writeable +#define TLBLO_V 0x2 // valid bit +#define TLBLO_G 0x1 // global access bit + +#define TLBINX_PROBE 0x80000000 +#define TLBINX_INXMASK 0x3F +#define TLBINX_INXSHIFT 0 + +#define TLBRAND_RANDMASK 0x3F +#define TLBRAND_RANDSHIFT 0 + +#define TLBWIRED_WIREDMASK 0x3F + +#define TLBCTXT_BASEMASK 0xFF800000 +#define TLBCTXT_BASESHIFT 23 +#define TLBCTXT_BASEBITS 9 + +#define TLBCTXT_VPNMASK 0x7FFFF0 +#define TLBCTXT_VPNSHIFT 4 + +#define TLBPGMASK_4K 0x0 +#define TLBPGMASK_16K 0x6000 +#define TLBPGMASK_64K 0x1E000 + +/* + * Status register + */ +#define SR_CUMASK 0xF0000000 // coproc usable bits + +#define SR_CU3 0x80000000 // Coprocessor 3 usable +#define SR_CU2 0x40000000 // Coprocessor 2 usable +#define SR_CU1 0x20000000 // Coprocessor 1 usable +#define SR_CU0 0x10000000 // Coprocessor 0 usable +#define SR_RP 0x08000000 // Reduced power (quarter speed) +#define SR_FR 0x04000000 // MIPS III FP register mode +#define SR_RE 0x02000000 // Reverse endian +#define SR_ITS 0x01000000 // Instruction trace support +#define SR_BEV 0x00400000 // Use boot exception vectors +#define SR_TS 0x00200000 // TLB shutdown +#define SR_SR 0x00100000 // Soft reset occured +#define SR_CH 0x00040000 // Cache hit for last 'cache' op +#define SR_CE 0x00020000 // Create ECC +#define SR_DE 0x00010000 // ECC of parity does not cause error + +// Interrupt enable bits +// (NOTE: bits set to 1 enable the corresponding level interrupt) +#define SR_IMASK 0x0000FF00 // Interrupt mask +#define SR_IMASK8 0x00000000 // mask level 8 +#define SR_IMASK7 0x00008000 // mask level 7 +#define SR_IMASK6 0x0000C000 // mask level 6 +#define SR_IMASK5 0x0000E000 // mask level 5 +#define SR_IMASK4 0x0000F000 // mask level 4 +#define SR_IMASK3 0x0000F800 // mask level 3 +#define SR_IMASK2 0x0000FC00 // mask level 2 +#define SR_IMASK1 0x0000FE00 // mask level 1 +#define SR_IMASK0 0x0000FF00 // mask level 0 + +#define SR_IBIT8 0x00008000 // bit level 8 +#define SR_IBIT7 0x00004000 // bit level 7 +#define SR_IBIT6 0x00002000 // bit level 6 +#define SR_IBIT5 0x00001000 // bit level 5 +#define SR_IBIT4 0x00000800 // bit level 4 +#define SR_IBIT3 0x00000400 // bit level 3 +#define SR_IBIT2 0x00000200 // bit level 2 +#define SR_IBIT1 0x00000100 // bit level 1 + +#define SR_IMASKSHIFT 8 + +#define SR_KX 0x00000080 // extended-addr TLB vec in kernel +#define SR_SX 0x00000040 // xtended-addr TLB vec supervisor +#define SR_UX 0x00000020 // xtended-addr TLB vec in user mode +#define SR_KSU_MASK 0x00000018 // mode mask +#define SR_KSU_USR 0x00000010 // user mode +#define SR_KSU_SUP 0x00000008 // supervisor mode +#define SR_KSU_KER 0x00000000 // kernel mode +#define SR_ERL 0x00000004 // Error level, 1=>cache error +#define SR_EXL 0x00000002 // Exception level, 1=>exception +#define SR_IE 0x00000001 // interrupt enable, 1=>enable + +// Cause Register +#define CAUSE_BD 0x80000000 // Branch delay slot +#define CAUSE_CEMASK 0x30000000 // coprocessor error +#define CAUSE_CESHIFT 28 + +// Interrupt pending bits +#define CAUSE_IP8 0x00008000 // External level 8 pending - COMPARE +#define CAUSE_IP7 0x00004000 // External level 7 pending - INT4 +#define CAUSE_IP6 0x00002000 // External level 6 pending - INT3 +#define CAUSE_IP5 0x00001000 // External level 5 pending - INT2 +#define CAUSE_IP4 0x00000800 // External level 4 pending - INT1 +#define CAUSE_IP3 0x00000400 // External level 3 pending - INT0 +#define CAUSE_SW2 0x00000200 // Software level 2 pending +#define CAUSE_SW1 0x00000100 // Software level 1 pending + +#define CAUSE_IPMASK 0x0000FF00 // Pending interrupt mask +#define CAUSE_IPSHIFT 8 + +#define CAUSE_EXCMASK 0x0000007C // Cause code bits +#define CAUSE_EXCSHIFT 2 + +// Cause register exception codes + +#define EXC_CODE(x) ((x) << 2) + +// Hardware exception codes +#define EXC_INT EXC_CODE(0) // interrupt +#define EXC_MOD EXC_CODE(1) // TLB mod +#define EXC_RMISS EXC_CODE(2) // Read TLB Miss +#define EXC_WMISS EXC_CODE(3) // Write TLB Miss +#define EXC_RADE EXC_CODE(4) // Read Address Error +#define EXC_WADE EXC_CODE(5) // Write Address Error +#define EXC_IBE EXC_CODE(6) // Instruction Bus Error +#define EXC_DBE EXC_CODE(7) // Data Bus Error +#define EXC_SYSCALL EXC_CODE(8) // SYSCALL +#define EXC_BREAK EXC_CODE(9) // BREAKpoint +#define EXC_II EXC_CODE(10) // Illegal Instruction +#define EXC_CPU EXC_CODE(11) // CoProcessor Unusable +#define EXC_OV EXC_CODE(12) // OVerflow +#define EXC_TRAP EXC_CODE(13) // Trap exception +#define EXC_VCEI EXC_CODE(14) // Virt. Coherency on Inst. fetch +#define EXC_FPE EXC_CODE(15) // Floating Point Exception +#define EXC_WATCH EXC_CODE(23) // Watchpoint reference +#define EXC_VCED EXC_CODE(31) // Virt. Coherency on data read + +// C0_PRID Defines +#define C0_IMPMASK 0xFF00 +#define C0_IMPSHIFT 8 +#define C0_REVMASK 0xFF +#define C0_MAJREVMASK 0xF0 +#define C0_MAJREVSHIFT 4 +#define C0_MINREVMASK 0xF + +// Coprocessor 0 operations +#define C0_READI 0x1 // read ITLB entry addressed by C0_INDEX +#define C0_WRITEI 0x2 // write ITLB entry addressed by C0_INDEX +#define C0_WRITER 0x6 // write ITLB entry addressed by C0_RAND +#define C0_PROBE 0x8 // probe for ITLB entry addressed by TLBHI +#define C0_RFE 0x10 // restore for exception + +// 'cache' instruction definitions + +// Target cache +#define CACH_PI 0x0 // specifies primary inst. cache +#define CACH_PD 0x1 // primary data cache +#define CACH_SI 0x2 // secondary instruction cache +#define CACH_SD 0x3 // secondary data cache + +// Cache operations +#define C_IINV 0x0 // index invalidate (inst, 2nd inst) +#define C_IWBINV 0x0 // index writeback inval (d, sd) +#define C_ILT 0x4 // index load tag (all) +#define C_IST 0x8 // index store tag (all) +#define C_CDX 0xC // create dirty exclusive (d, sd) +#define C_HINV 0x10 // hit invalidate (all) +#define C_HWBINV 0x14 // hit writeback inv. (d, sd) +#define C_FILL 0x14 // fill (i) +#define C_HWB 0x18 // hit writeback (i, d, sd) +#define C_HSV 0x1C // hit set virt. (si, sd) + +// Cache size definitions +#define ICACHE_SIZE 0x4000 // 16K +#define ICACHE_LINESIZE 32 // 8 words +#define ICACHE_LINEMASK (ICACHE_LINESIZE - 1) + +#define DCACHE_SIZE 0x2000 // 8K +#define DCACHE_LINESIZE 16 // 4 words +#define DCACHE_LINEMASK (DCACHE_LINESIZE - 1) + +// C0_CONFIG register definitions +#define CONFIG_CM 0x80000000 // 1 == Master-Checker enabled +#define CONFIG_EC 0x70000000 // System Clock ratio +#define CONFIG_EC_1_1 0x6 // System Clock ratio 1 :1 +#define CONFIG_EC_3_2 0x7 // System Clock ratio 1.5 :1 +#define CONFIG_EC_2_1 0x0 // System Clock ratio 2 :1 +#define CONFIG_EC_3_1 0x1 // System Clock ratio 3 :1 +#define CONFIG_EP 0x0F000000 // Transmit Data Pattern +#define CONFIG_SB 0x00C00000 // Secondary cache block size + +#define CONFIG_SS 0x00200000 // Split scache: 0 == I&D combined +#define CONFIG_SW 0x00100000 // scache port: 0==128, 1==64 +#define CONFIG_EW 0x000C0000 // System Port width: 0==64, 1==32 +#define CONFIG_SC 0x00020000 // 0 -> 2nd cache present +#define CONFIG_SM 0x00010000 // 0 -> Dirty Shared Coherency enable +#define CONFIG_BE 0x00008000 // Endian-ness: 1 --> BE +#define CONFIG_EM 0x00004000 // 1 -> ECC mode, 0 -> parity +#define CONFIG_EB 0x00002000 // Block order:1->sequent,0->subblock + +#define CONFIG_IC 0x00000E00 // Primary Icache size +#define CONFIG_DC 0x000001C0 // Primary Dcache size +#define CONFIG_IB 0x00000020 // Icache block size +#define CONFIG_DB 0x00000010 // Dcache block size +#define CONFIG_CU 0x00000008 // Update on Store-conditional +#define CONFIG_K0 0x00000007 // K0SEG Coherency algorithm + +#define CONFIG_UNCACHED 0x00000002 // K0 is uncached +#define CONFIG_NONCOHRNT 0x00000003 +#define CONFIG_COHRNT_EXLWR 0x00000005 +#define CONFIG_SB_SHFT 22 // shift SB to bit position 0 +#define CONFIG_IC_SHFT 9 // shift IC to bit position 0 +#define CONFIG_DC_SHFT 6 // shift DC to bit position 0 +#define CONFIG_BE_SHFT 15 // shift BE to bit position 0 + +// C0_TAGLO definitions for setting/getting cache states and physaddr bits +#define SADDRMASK 0xFFFFE000 // 31..13 -> scache paddr bits 35..17 +#define SVINDEXMASK 0x00000380 // 9..7: prim virt index bits 14..12 +#define SSTATEMASK 0x00001C00 // bits 12..10 hold scache line state +#define SINVALID 0x00000000 // invalid --> 000 == state 0 +#define SCLEANEXCL 0x00001000 // clean exclusive --> 100 == state 4 +#define SDIRTYEXCL 0x00001400 // dirty exclusive --> 101 == state 5 +#define SECC_MASK 0x0000007F // low 7 bits are ecc for the tag +#define SADDR_SHIFT 4 // shift STagLo (31..13) to 35..17 + +#define PADDRMASK 0xFFFFFF00 // PTagLo31..8->prim paddr bits35..12 +#define PADDR_SHIFT 4 // roll bits 35..12 down to 31..8 +#define PSTATEMASK 0x00C0 // bits 7..6 hold primary line state +#define PINVALID 0x0000 // invalid --> 000 == state 0 +#define PCLEANEXCL 0x0080 // clean exclusive --> 10 == state 2 +#define PDIRTYEXCL 0x00C0 // dirty exclusive --> 11 == state 3 +#define PPARITY_MASK 0x0001 // low bit is parity bit (even). + +// C0_CACHE_ERR definitions. +#define CACHERR_ER 0x80000000 // 0: inst ref, 1: data ref +#define CACHERR_EC 0x40000000 // 0: primary, 1: secondary +#define CACHERR_ED 0x20000000 // 1: data error +#define CACHERR_ET 0x10000000 // 1: tag error +#define CACHERR_ES 0x08000000 // 1: external ref, e.g. snoo +#define CACHERR_EE 0x04000000 // error on SysAD bus +#define CACHERR_EB 0x02000000 // complicated, see spec. +#define CACHERR_EI 0x01000000 // complicated, see spec. +#define CACHERR_SIDX_MASK 0x003FFFF8 // secondary cache index +#define CACHERR_PIDX_MASK 0x00000007 // primary cache index +#define CACHERR_PIDX_SHIFT 12 // bits 2..0 are paddr14..12 + +/* + * R4000 family supports hardware watchpoints: + * C0_WATCHLO: + * bits 31..3 are bits 31..3 of physaddr to watch + * bit 2: reserved; must be written as 0. + * bit 1: when set causes a watchpoint trap on load accesses to paddr. + * bit 0: when set traps on stores to paddr; + * C0_WATCHHI + * bits 31..4 are reserved and must be written as zeros. + * bits 3..0 are bits 35..32 of the physaddr to watch + */ +#define WATCHLO_WTRAP 0x00000001 +#define WATCHLO_RTRAP 0x00000002 +#define WATCHLO_ADDRMASK 0xFFFFFFF8 +#define WATCHLO_VALIDMASK 0xFFFFFFFB +#define WATCHHI_VALIDMASK 0x0000000F + +// Coprocessor 0 registers +#define C0_INX C_REG(0) +#define C0_RAND C_REG(1) +#define C0_ENTRYLO0 C_REG(2) +#define C0_ENTRYLO1 C_REG(3) +#define C0_CONTEXT C_REG(4) +#define C0_PAGEMASK C_REG(5) // page mask +#define C0_WIRED C_REG(6) // # wired entries in tlb +#define C0_BADVADDR C_REG(8) +#define C0_COUNT C_REG(9) // free-running counter +#define C0_ENTRYHI C_REG(10) +#define C0_COMPARE C_REG(11) // counter comparison reg. +#define C0_SR C_REG(12) +#define C0_CAUSE C_REG(13) +#define C0_EPC C_REG(14) +#define C0_PRID C_REG(15) // revision identifier +#define C0_CONFIG C_REG(16) // hardware configuration +#define C0_LLADDR C_REG(17) // load linked address +#define C0_WATCHLO C_REG(18) // watchpoint +#define C0_WATCHHI C_REG(19) // watchpoint +#define C0_ECC C_REG(26) // S-cache ECC and primary parity +#define C0_CACHE_ERR C_REG(27) // cache error status +#define C0_TAGLO C_REG(28) // cache operations +#define C0_TAGHI C_REG(29) // cache operations +#define C0_ERROR_EPC C_REG(30) // ECC error prg. counter + +// floating-point status register +#define C1_FPCSR C_REG(31) + +#define FPCSR_FS 0x01000000 // flush denorm to zero +#define FPCSR_C 0x00800000 // condition bit +#define FPCSR_CE 0x00020000 // cause: unimplemented operation +#define FPCSR_CV 0x00010000 // cause: invalid operation +#define FPCSR_CZ 0x00008000 // cause: division by zero +#define FPCSR_CO 0x00004000 // cause: overflow +#define FPCSR_CU 0x00002000 // cause: underflow +#define FPCSR_CI 0x00001000 // cause: inexact operation +#define FPCSR_EV 0x00000800 // enable: invalid operation +#define FPCSR_EZ 0x00000400 // enable: division by zero +#define FPCSR_EO 0x00000200 // enable: overflow +#define FPCSR_EU 0x00000100 // enable: underflow +#define FPCSR_EI 0x00000080 // enable: inexact operation +#define FPCSR_FV 0x00000040 // flag: invalid operation +#define FPCSR_FZ 0x00000020 // flag: division by zero +#define FPCSR_FO 0x00000010 // flag: overflow +#define FPCSR_FU 0x00000008 // flag: underflow +#define FPCSR_FI 0x00000004 // flag: inexact operation +#define FPCSR_RM_MASK 0x00000003 // rounding mode mask +#define FPCSR_RM_RN 0x00000000 // round to nearest +#define FPCSR_RM_RZ 0x00000001 // round to zero +#define FPCSR_RM_RP 0x00000002 // round to positive infinity +#define FPCSR_RM_RM 0x00000003 // round to negative infinity diff --git a/libultraship/include/libultraship/libultra/rcp.h b/libultraship/include/libultraship/libultra/rcp.h new file mode 100644 index 000000000..8f3ac31fd --- /dev/null +++ b/libultraship/include/libultraship/libultra/rcp.h @@ -0,0 +1,269 @@ +#pragma once + +/** + * PIF Physical memory map (total size = 2 KB) + * + * Size Description Mode + * 1FC007FF +-------+-----------------+-----+ + * | 64 B | JoyChannel RAM | R/W | + * 1FC007C0 +-------+-----------------+-----+ + * |1984 B | Boot ROM | * | * = Reserved + * 1FC00000 +-------+-----------------+-----+ + */ +#define PIF_ROM_START 0x1FC00000 +#define PIF_ROM_END 0x1FC007BF +#define PIF_RAM_START 0x1FC007C0 +#define PIF_RAM_END 0x1FC007FF + +/* + * Patterns to interpret VI_CONTROL_REG + */ +#define VI_CTRL_TYPE_16 0x00002 /* [1:0] pixel size: 16 bit */ +#define VI_CTRL_TYPE_32 0x00003 /* [1:0] pixel size: 32 bit */ +#define VI_CTRL_GAMMA_DITHER_ON 0x00004 /* 2: default = on */ +#define VI_CTRL_GAMMA_ON 0x00008 /* 3: default = on */ +#define VI_CTRL_DIVOT_ON 0x00010 /* 4: default = on */ +#define VI_CTRL_SERRATE_ON 0x00040 /* 6: on if interlaced */ +#define VI_CTRL_ANTIALIAS_MASK 0x00300 /* [9:8] anti-alias mode */ +#define VI_CTRL_ANTIALIAS_MODE_1 0x00100 /* Bit [9:8] anti-alias mode */ +#define VI_CTRL_ANTIALIAS_MODE_2 0x00200 /* Bit [9:8] anti-alias mode */ +#define VI_CTRL_ANTIALIAS_MODE_3 0x00300 /* Bit [9:8] anti-alias mode */ +#define VI_CTRL_PIXEL_ADV_MASK 0x01000 /* [15:12] pixel advance mode? */ +#define VI_CTRL_PIXEL_ADV_1 0x01000 /* Bit [15:12] pixel advance mode? */ +#define VI_CTRL_PIXEL_ADV_2 0x02000 /* Bit [15:12] pixel advance mode? */ +#define VI_CTRL_PIXEL_ADV_3 0x03000 /* Bit [15:12] pixel advance mode? */ +#define VI_CTRL_DITHER_FILTER_ON 0x10000 /* 16: dither-filter mode */ + +#define VI_NTSC_CLOCK 48681812 /* Hz = 48.681812 MHz */ +#define VI_PAL_CLOCK 49656530 /* Hz = 49.656530 MHz */ +#define VI_MPAL_CLOCK 48628316 /* Hz = 48.628316 MHz */ + +/** + * Audio Interface (AI) Registers + */ +#define AI_BASE_REG 0x04500000 + +/* AI DRAM address (W): [23:0] starting RDRAM address (8B-aligned) */ +#define AI_DRAM_ADDR_REG (AI_BASE_REG + 0x00) /* R0: DRAM address */ + +/* AI length (R/W): [14:0] transfer length (v1.0) - Bottom 3 bits are ignored */ +/* [17:0] transfer length (v2.0) - Bottom 3 bits are ignored */ +#define AI_LEN_REG (AI_BASE_REG + 0x04) /* R1: Length */ + +/* AI control (W): [0] DMA enable - if LSB == 1, DMA is enabled */ +#define AI_CONTROL_REG (AI_BASE_REG + 0x08) /* R2: DMA Control */ + +/* Value for control register */ +#define AI_CONTROL_DMA_ON 1 /* LSB = 1: DMA enable*/ +#define AI_CONTROL_DMA_OFF 0 /* LSB = 1: DMA enable*/ + +/* + * AI status (R): [31]/[0] ai_full (addr & len buffer full), [30] ai_busy + * Note that a 1->0 transition in ai_full will set interrupt + * (W): clear audio interrupt + */ +#define AI_STATUS_REG (AI_BASE_REG + 0x0C) /* R3: Status */ + +/* Value for status register */ +#define AI_STATUS_FIFO_FULL (1 << 31) +#define AI_STATUS_DMA_BUSY (1 << 30) + +/* + * AI DAC sample period register (W): [13:0] dac rate + * - vid_clock/(dperiod + 1) is the DAC sample rate + * - (dperiod + 1) >= 66 * (aclockhp + 1) must be true + */ +#define AI_DACRATE_REG (AI_BASE_REG + 0x10) /* R4: DAC rate 14-lsb*/ + +/* DAC rate = video clock / audio frequency + * - DAC rate >= (66 * Bit rate) must be true + */ +#define AI_MAX_DAC_RATE 16384 /* 14-bit+1 */ +#define AI_MIN_DAC_RATE 132 + +/* + * AI bit rate (W): [3:0] bit rate (abus clock half period register - aclockhp) + * - vid_clock/(2 * (aclockhp + 1)) is the DAC clock rate + * - The abus clock stops if aclockhp is zero + */ +#define AI_BITRATE_REG (AI_BASE_REG + 0x14) /* R5: Bit rate 4-lsb */ + +/* Bit rate <= (DAC rate / 66) */ +#define AI_MAX_BIT_RATE 16 /* 4-bit+1 */ +#define AI_MIN_BIT_RATE 2 + +#define CHNL_ERR_NORESP 0x80 /* Bit 7 (Rx): No response error */ +#define CHNL_ERR_OVERRUN 0x40 /* Bit 6 (Rx): Overrun error */ +#define CHNL_ERR_FRAME 0x80 /* Bit 7 (Tx): Frame error */ +#define CHNL_ERR_COLLISION 0x40 /* Bit 6 (Tx): Collision error */ + +#define CHNL_ERR_MASK 0xC0 /* Bit 6-7: channel errors */ + +#define DEVICE_TYPE_CART 0 /* ROM cartridge */ +#define DEVICE_TYPE_BULK 1 /* ROM bulk */ +#define DEVICE_TYPE_64DD 2 /* 64 Disk Drive */ +#define DEVICE_TYPE_SRAM 3 /* SRAM */ +#define DEVICE_TYPE_INIT 7 /* initial value */ + +/** + * Signal Processor (SP) Memory + */ +#define SP_DMEM_START 0x04000000 +#define SP_DMEM_END 0x04000FFF +#define SP_IMEM_START 0x04001000 +#define SP_IMEM_END 0x04001FFF + +#define SP_MEM_ADDR_REG 0x04040000 +#define SP_DRAM_ADDR_REG 0x04040004 +#define SP_RD_LEN_REG 0x04040008 +#define SP_WR_LEN_REG 0x0404000C +#define SP_STATUS_REG 0x04040010 +#define SP_DMA_FULL_REG 0x04040014 +#define SP_DMA_BUSY_REG 0x04040018 +#define SP_PC_REG 0x04080000 + +/** + * SP_STATUS_REG: write bits + */ +#define SP_CLR_HALT (1 << 0) +#define SP_SET_HALT (1 << 1) +#define SP_CLR_BROKE (1 << 2) +#define SP_CLR_INTR (1 << 3) +#define SP_SET_INTR (1 << 4) +#define SP_CLR_SSTEP (1 << 5) +#define SP_SET_SSTEP (1 << 6) +#define SP_CLR_INTR_BREAK (1 << 7) +#define SP_SET_INTR_BREAK (1 << 8) +#define SP_CLR_SIG0 (1 << 9) +#define SP_SET_SIG0 (1 << 10) +#define SP_CLR_SIG1 (1 << 11) +#define SP_SET_SIG1 (1 << 12) +#define SP_CLR_SIG2 (1 << 13) +#define SP_SET_SIG2 (1 << 14) +#define SP_CLR_SIG3 (1 << 15) +#define SP_SET_SIG3 (1 << 16) +#define SP_CLR_SIG4 (1 << 17) +#define SP_SET_SIG4 (1 << 18) +#define SP_CLR_SIG5 (1 << 19) +#define SP_SET_SIG5 (1 << 20) +#define SP_CLR_SIG6 (1 << 21) +#define SP_SET_SIG6 (1 << 22) +#define SP_CLR_SIG7 (1 << 23) +#define SP_SET_SIG7 (1 << 24) + +/* + * SP_STATUS_REG: read bits + */ +#define SP_STATUS_HALT (1 << 0) +#define SP_STATUS_BROKE (1 << 1) +#define SP_STATUS_DMA_BUSY (1 << 2) +#define SP_STATUS_DMA_FULL (1 << 3) +#define SP_STATUS_IO_FULL (1 << 4) +#define SP_STATUS_SSTEP (1 << 5) +#define SP_STATUS_INTR_BREAK (1 << 6) +#define SP_STATUS_SIG0 (1 << 7) +#define SP_STATUS_SIG1 (1 << 8) +#define SP_STATUS_SIG2 (1 << 9) +#define SP_STATUS_SIG3 (1 << 10) +#define SP_STATUS_SIG4 (1 << 11) +#define SP_STATUS_SIG5 (1 << 12) +#define SP_STATUS_SIG6 (1 << 13) +#define SP_STATUS_SIG7 (1 << 14) + +/** + * SP_STATUS_REG: use of SIG bits + */ +#define SP_CLR_YIELD SP_CLR_SIG0 +#define SP_SET_YIELD SP_SET_SIG0 +#define SP_STATUS_YIELD SP_STATUS_SIG0 +#define SP_CLR_YIELDED SP_CLR_SIG1 +#define SP_SET_YIELDED SP_SET_SIG1 +#define SP_STATUS_YIELDED SP_STATUS_SIG1 +#define SP_CLR_TASKDONE SP_CLR_SIG2 +#define SP_SET_TASKDONE SP_SET_SIG2 +#define SP_STATUS_TASKDONE SP_STATUS_SIG2 +#define SP_CLR_RSPSIGNAL SP_CLR_SIG3 +#define SP_SET_RSPSIGNAL SP_SET_SIG3 +#define SP_STATUS_RSPSIGNAL SP_STATUS_SIG3 +#define SP_CLR_CPUSIGNAL SP_CLR_SIG4 +#define SP_SET_CPUSIGNAL SP_SET_SIG4 +#define SP_STATUS_CPUSIGNAL SP_STATUS_SIG4 + +#define VI_STATUS_REG 0x04400000 +#define VI_CONTROL_REG 0x04400000 +#define VI_ORIGIN_REG 0x04400004 +#define VI_DRAM_ADDR_REG 0x04400004 +#define VI_WIDTH_REG 0x04400008 +#define VI_H_WIDTH_REG 0x04400008 +#define VI_INTR_REG 0x0440000C +#define VI_V_INTER_REG 0x0440000C +#define VI_CURRENT_REG 0x04400010 +#define VI_V_CURRENT_LINE_REG 0x04400010 +#define VI_BURST_REG 0x04400014 +#define VI_TIMING_REG 0x04400014 +#define VI_V_SYNC_REG 0x04400018 // VI vertical sync +#define VI_H_SYNC_REG 0x0440001C // VI horizontal sync +#define VI_LEAP_REG 0x04400020 // VI horizontal sync leap +#define VI_H_SYNC_LEAP_REG 0x04400020 +#define VI_H_START_REG 0x04400024 // VI horizontal video +#define VI_H_VIDEO_REG 0x04400024 +#define VI_V_START_REG 0x04400028 // VI vertical video +#define VI_V_VIDEO_REG 0x04400028 +#define VI_V_BURST_REG 0x0440002C // VI vertical burst +#define VI_X_SCALE_REG 0x04400030 // VI x-scale +#define VI_Y_SCALE_REG 0x04400034 // VI y-scale + +#define PI_DRAM_ADDR_REG 0x04600000 // PI DRAM address +#define PI_CART_ADDR_REG 0x04600004 // PI pbus (cartridge) address +#define PI_RD_LEN_REG 0x04600008 // PI read length +#define PI_WR_LEN_REG 0x0460000C // PI write length +#define PI_STATUS_REG 0x04600010 // PI status +#define PI_BSD_DOM1_LAT_REG 0x04600014 // PI dom1 latency +#define PI_DOMAIN1_REG 0x04600014 +#define PI_BSD_DOM1_PWD_REG 0x04600018 // PI dom1 pulse width +#define PI_BSD_DOM1_PGS_REG 0x0460001C // PI dom1 page size +#define PI_BSD_DOM1_RLS_REG 0x04600020 // PI dom1 release +#define PI_BSD_DOM2_LAT_REG 0x04600024 // PI dom2 latency +#define PI_DOMAIN2_REG 0x04600024 +#define PI_BSD_DOM2_PWD_REG 0x04600028 // PI dom2 pulse width +#define PI_BSD_DOM2_PGS_REG 0x0460002C // PI dom2 page size +#define PI_BSD_DOM2_RLS_REG 0x04600030 // PI dom2 release + +#define PI_STATUS_DMA_BUSY (1 << 0) +#define PI_STATUS_IO_BUSY (1 << 1) +#define PI_STATUS_ERROR (1 << 2) + +/* + * PI_STATUS_REG: write bits + */ +#define PI_STATUS_RESET (1 << 0) +#define PI_SET_RESET PI_STATUS_RESET + +#define PI_STATUS_CLR_INTR (1 << 1) +#define PI_CLR_INTR PI_STATUS_CLR_INTR + +#define PI_DMA_BUFFER_SIZE 128 + +#define PI_DOM1_ADDR1 0x06000000 /* to 0x07FFFFFF */ +#define PI_DOM1_ADDR2 0x10000000 /* to 0x1FBFFFFF */ +#define PI_DOM1_ADDR3 0x1FD00000 /* to 0x7FFFFFFF */ +#define PI_DOM2_ADDR1 0x05000000 /* to 0x05FFFFFF */ +#define PI_DOM2_ADDR2 0x08000000 /* to 0x0FFFFFFF */ + +/** + * Serial Interface (SI) Registers + */ +#define SI_BASE_REG 0x04800000 + +#define SI_DRAM_ADDR_REG (SI_BASE_REG + 0x00) +#define SI_PIF_ADDR_RD64B_REG (SI_BASE_REG + 0x04) +#define SI_PIF_ADDR_WR64B_REG (SI_BASE_REG + 0x10) +#define SI_STATUS_REG (SI_BASE_REG + 0x18) + +#define SI_STATUS_DMA_BUSY (1 << 0) +#define SI_STATUS_IO_READ_BUSY (1 << 1) +#define SI_STATUS_DMA_ERROR (1 << 3) +#define SI_STATUS_INTERRUPT (1 << 12) + +#define IO_READ(addr) (*(vu32*)PHYS_TO_K1(addr)) +#define IO_WRITE(addr, data) (*(vu32*)PHYS_TO_K1(addr) = (u32)(data)) diff --git a/libultraship/include/libultraship/libultra/rdp.h b/libultraship/include/libultraship/libultra/rdp.h new file mode 100644 index 000000000..6921ce581 --- /dev/null +++ b/libultraship/include/libultraship/libultra/rdp.h @@ -0,0 +1,48 @@ +#pragma once + +/* DP Command Registers */ + +#define DPC_REG_BASE 0xA4100000 + +#define DPC_START_REG (*(vu32*)(DPC_REG_BASE + 0x00)) +#define DPC_END_REG (*(vu32*)(DPC_REG_BASE + 0x04)) +#define DPC_CURRENT_REG (*(vu32*)(DPC_REG_BASE + 0x08)) +#define DPC_STATUS_REG (*(vu32*)(DPC_REG_BASE + 0x0C)) +#define DPC_CLOCK_REG (*(vu32*)(DPC_REG_BASE + 0x10)) +#define DPC_BUFBUSY_REG (*(vu32*)(DPC_REG_BASE + 0x14)) +#define DPC_PIPEBUSY_REG (*(vu32*)(DPC_REG_BASE + 0x18)) +#define DPC_TMEM_REG (*(vu32*)(DPC_REG_BASE + 0x1C)) + +/* DP Span Registers */ + +#define DPS_REG_BASE 0xA4200000 + +#define DPS_TBIST_REG (*(vu32*)(DPS_REG_BASE + 0x00)) +#define DPS_TEST_MODE_REG (*(vu32*)(DPS_REG_BASE + 0x04)) +#define DPS_BUFTEST_ADDR_REG (*(vu32*)(DPS_REG_BASE + 0x08)) +#define DPS_BUFTEST_DATA_REG (*(vu32*)(DPS_REG_BASE + 0x0C)) + +/* DP Status Read Flags */ +#define DPC_STATUS_XBUS_DMEM_DMA 0x001 +#define DPC_STATUS_FREEZE 0x002 +#define DPC_STATUS_FLUSH 0x004 +#define DPC_STATUS_START_GCLK 0x008 +#define DPC_STATUS_TMEM_BUSY 0x010 +#define DPC_STATUS_PIPE_BUSY 0x020 +#define DPC_STATUS_CMD_BUSY 0x040 +#define DPC_STATUS_CBUF_READY 0x080 +#define DPC_STATUS_DMA_BUSY 0x100 +#define DPC_STATUS_END_VALID 0x200 +#define DPC_STATUS_START_VALID 0x400 + +/* DP Status Write Flags */ +#define DPC_CLR_XBUS_DMEM_DMA 0x0001 +#define DPC_SET_XBUS_DMEM_DMA 0x0002 +#define DPC_CLR_FREEZE 0x0004 +#define DPC_SET_FREEZE 0x0008 +#define DPC_CLR_FLUSH 0x0010 +#define DPC_SET_FLUSH 0x0020 +#define DPC_CLR_TMEM_CTR 0x0040 +#define DPC_CLR_PIPE_CTR 0x0080 +#define DPC_CLR_CMD_CTR 0x0100 +#define DPC_CLR_CLOCK_CTR 0x0200 diff --git a/libultraship/include/libultraship/libultra/sptask.h b/libultraship/include/libultraship/libultra/sptask.h new file mode 100644 index 000000000..fa74dc921 --- /dev/null +++ b/libultraship/include/libultraship/libultra/sptask.h @@ -0,0 +1,65 @@ +#pragma once + +#include "types.h" + +/* Task Types */ +#define M_NULTASK 0 +#define M_GFXTASK 1 +#define M_AUDTASK 2 +#define M_VIDTASK 3 +#define M_NJPEGTASK 4 +#define M_HVQTASK 6 +#define M_HVQMTASK 7 + +/* Task Flags */ +#define M_TASK_FLAG0 1 +#define M_TASK_FLAG1 2 + +/* Task Flag Fields */ +#define OS_TASK_YIELDED 0x0001 +#define OS_TASK_DP_WAIT 0x0002 +#define OS_TASK_LOADABLE 0x0004 +#define OS_TASK_SP_ONLY 0x0008 +#define OS_TASK_USR0 0x0010 +#define OS_TASK_USR1 0x0020 +#define OS_TASK_USR2 0x0040 +#define OS_TASK_USR3 0x0080 + +#define OS_YIELD_DATA_SIZE 0xC00 + +typedef struct { + /* 0x00 */ u32 type; + /* 0x04 */ u32 flags; + + /* 0x08 */ u64* ucode_boot; + /* 0x0C */ u32 ucode_boot_size; + + /* 0x10 */ u64* ucode; + /* 0x14 */ u32 ucode_size; + + /* 0x18 */ u64* ucode_data; + /* 0x1C */ u32 ucode_data_size; + + /* 0x20 */ u64* dram_stack; + /* 0x24 */ u32 dram_stack_size; + + /* 0x28 */ u64* output_buff; + /* 0x2C */ u64* output_buff_size; + + /* 0x30 */ u64* data_ptr; + /* 0x34 */ u32 data_size; + + /* 0x38 */ u64* yield_data_ptr; + /* 0x3C */ u32 yield_data_size; +} OSTask_t; // size = 0x40 + +typedef union { + OSTask_t t; + long long int force_structure_alignment; +} OSTask; + +typedef u32 OSYieldResult; + +#define osSpTaskStart(p) \ + osSpTaskLoad(p); \ + osSpTaskStartGo(p); diff --git a/libultraship/include/libultraship/libultra/thread.h b/libultraship/include/libultraship/libultra/thread.h new file mode 100644 index 000000000..75ced5a75 --- /dev/null +++ b/libultraship/include/libultraship/libultra/thread.h @@ -0,0 +1,61 @@ +#pragma once + +#include "types.h" + +#define OS_PRIORITY_MAX 255 +#define OS_PRIORITY_VIMGR 254 +#define OS_PRIORITY_RMON 250 +#define OS_PRIORITY_RMONSPIN 200 +#define OS_PRIORITY_PIMGR 150 +#define OS_PRIORITY_SIMGR 140 +#define OS_PRIORITY_APPMAX 127 +#define OS_PRIORITY_IDLE 0 + +#define OS_STATE_STOPPED 1 +#define OS_STATE_RUNNABLE 2 +#define OS_STATE_RUNNING 4 +#define OS_STATE_WAITING 8 + +#define OS_FLAG_CPU_BREAK 1 +#define OS_FLAG_FAULT 2 + +typedef s32 OSPri; +typedef s32 OSId; + +typedef union { + struct { + /* 0x00 */ f32 f_odd; + /* 0x04 */ f32 f_even; + } f; +} __OSfp; // size = 0x08 + +typedef struct { + /* 0x000 */ u64 at, v0, v1, a0, a1, a2, a3; + /* 0x038 */ u64 t0, t1, t2, t3, t4, t5, t6, t7; + /* 0x078 */ u64 s0, s1, s2, s3, s4, s5, s6, s7; + /* 0x0B8 */ u64 t8, t9, gp, sp, s8, ra; + /* 0x0E8 */ u64 lo, hi; + /* 0x0F8 */ u32 sr, pc, cause, badvaddr, rcp; + /* 0x10C */ u32 fpcsr; + /* 0x110 */ __OSfp fp0, fp2, fp4, fp6, fp8, fp10, fp12, fp14; + /* 0x150 */ __OSfp fp16, fp18, fp20, fp22, fp24, fp26, fp28, fp30; +} __OSThreadContext; // size = 0x190 + +typedef struct { + /* 0x00 */ u32 flag; + /* 0x04 */ u32 count; + /* 0x08 */ u64 time; +} __OSThreadprofile; // size = 0x10 + +typedef struct OSThread { + /* 0x00 */ struct OSThread* next; + /* 0x04 */ OSPri priority; + /* 0x08 */ struct OSThread** queue; + /* 0x0C */ struct OSThread* tlnext; + /* 0x10 */ u16 state; + /* 0x12 */ u16 flags; + /* 0x14 */ OSId id; + /* 0x18 */ s32 fp; + /* 0x1C */ __OSThreadprofile* thprof; + /* 0x20 */ __OSThreadContext context; +} OSThread; // size = 0x1B0 diff --git a/libultraship/include/libultraship/libultra/time.h b/libultraship/include/libultraship/libultra/time.h new file mode 100644 index 000000000..6241eb05a --- /dev/null +++ b/libultraship/include/libultraship/libultra/time.h @@ -0,0 +1,14 @@ +#pragma once + +#include "message.h" + +typedef u64 OSTime; + +typedef struct OSTimer { + /* 0x00 */ struct OSTimer* next; + /* 0x04 */ struct OSTimer* prev; + /* 0x08 */ OSTime interval; + /* 0x10 */ OSTime value; + /* 0x18 */ OSMesgQueue* mq; + /* 0x1C */ OSMesg msg; +} OSTimer; // size = 0x20 diff --git a/libultraship/include/libultraship/libultra/types.h b/libultraship/include/libultraship/libultra/types.h new file mode 100644 index 000000000..3bdb4d31c --- /dev/null +++ b/libultraship/include/libultraship/libultra/types.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +typedef signed char s8; +typedef unsigned char u8; +typedef signed short int s16; +typedef unsigned short int u16; +typedef signed int s32; +typedef unsigned int u32; +typedef signed long long int s64; +typedef unsigned long long int u64; + +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; +typedef volatile s8 vs8; +typedef volatile s16 vs16; +typedef volatile s32 vs32; +typedef volatile s64 vs64; + +typedef float f32; +typedef double f64; +#if 0 + +typedef s32 ptrdiff_t; +typedef s32 intptr_t; +typedef u32 uintptr_t; +#endif diff --git a/libultraship/include/libultraship/libultra/vi.h b/libultraship/include/libultraship/libultra/vi.h new file mode 100644 index 000000000..1b50f3d92 --- /dev/null +++ b/libultraship/include/libultraship/libultra/vi.h @@ -0,0 +1,134 @@ +#pragma once + +#include "message.h" + +/* Special Features */ +#define OS_VI_GAMMA_ON 0x0001 +#define OS_VI_GAMMA_OFF 0x0002 +#define OS_VI_GAMMA_DITHER_ON 0x0004 +#define OS_VI_GAMMA_DITHER_OFF 0x0008 +#define OS_VI_DIVOT_ON 0x0010 +#define OS_VI_DIVOT_OFF 0x0020 +#define OS_VI_DITHER_FILTER_ON 0x0040 +#define OS_VI_DITHER_FILTER_OFF 0x0080 + +#define OS_VI_GAMMA 0x08 +#define OS_VI_GAMMA_DITHER 0x04 +#define OS_VI_DIVOT 0x10 +#define OS_VI_DITHER_FILTER 0x10000 +#define OS_VI_UNK1 0x1 +#define OS_VI_UNK2 0x2 +#define OS_VI_UNK40 0x40 +#define OS_VI_UNK100 0x100 +#define OS_VI_UNK200 0x200 +#define OS_VI_UNK1000 0x1000 +#define OS_VI_UNK2000 0x2000 + +typedef struct { + /* 0x00 */ u32 ctrl; + /* 0x04 */ u32 width; + /* 0x08 */ u32 burst; + /* 0x0C */ u32 vSync; + /* 0x10 */ u32 hSync; + /* 0x14 */ u32 leap; + /* 0x18 */ u32 hStart; + /* 0x1C */ u32 xScale; + /* 0x20 */ u32 vCurrent; +} OSViCommonRegs; // size = 0x20 + +typedef struct { + /* 0x00 */ u32 origin; + /* 0x04 */ u32 yScale; + /* 0x08 */ u32 vStart; + /* 0x0C */ u32 vBurst; + /* 0x10 */ u32 vIntr; +} OSViFieldRegs; // size = 0x14 + +typedef struct { + /* 0x00 */ u8 type; + /* 0x04 */ OSViCommonRegs comRegs; + /* 0x24 */ OSViFieldRegs fldRegs[2]; +} OSViMode; // size = 0x4C + +typedef struct { + /* 0x0 */ f32 factor; + /* 0x4 */ u16 offset; + /* 0x8 */ u32 scale; +} __OSViScale; // size = 0x0C + +typedef struct { + /* 0x00 */ u16 state; + /* 0x02 */ u16 retraceCount; + /* 0x04 */ void* buffer; + /* 0x08 */ OSViMode* modep; + /* 0x0C */ u32 features; + /* 0x10 */ OSMesgQueue* mq; + /* 0x14 */ OSMesg* msg; + /* 0x18 */ __OSViScale x; + /* 0x24 */ __OSViScale y; +} OSViContext; // size = 0x30 + +#define OS_VI_NTSC_LPN1 0 /* NTSC */ +#define OS_VI_NTSC_LPF1 1 +#define OS_VI_NTSC_LAN1 2 +#define OS_VI_NTSC_LAF1 3 +#define OS_VI_NTSC_LPN2 4 +#define OS_VI_NTSC_LPF2 5 +#define OS_VI_NTSC_LAN2 6 +#define OS_VI_NTSC_LAF2 7 +#define OS_VI_NTSC_HPN1 8 +#define OS_VI_NTSC_HPF1 9 +#define OS_VI_NTSC_HAN1 10 +#define OS_VI_NTSC_HAF1 11 +#define OS_VI_NTSC_HPN2 12 +#define OS_VI_NTSC_HPF2 13 + +#define OS_VI_PAL_LPN1 14 /* PAL */ +#define OS_VI_PAL_LPF1 15 +#define OS_VI_PAL_LAN1 16 +#define OS_VI_PAL_LAF1 17 +#define OS_VI_PAL_LPN2 18 +#define OS_VI_PAL_LPF2 19 +#define OS_VI_PAL_LAN2 20 +#define OS_VI_PAL_LAF2 21 +#define OS_VI_PAL_HPN1 22 +#define OS_VI_PAL_HPF1 23 +#define OS_VI_PAL_HAN1 24 +#define OS_VI_PAL_HAF1 25 +#define OS_VI_PAL_HPN2 26 +#define OS_VI_PAL_HPF2 27 + +#define OS_VI_MPAL_LPN1 28 /* MPAL - mainly Brazil */ +#define OS_VI_MPAL_LPF1 29 +#define OS_VI_MPAL_LAN1 30 +#define OS_VI_MPAL_LAF1 31 +#define OS_VI_MPAL_LPN2 32 +#define OS_VI_MPAL_LPF2 33 +#define OS_VI_MPAL_LAN2 34 +#define OS_VI_MPAL_LAF2 35 +#define OS_VI_MPAL_HPN1 36 +#define OS_VI_MPAL_HPF1 37 +#define OS_VI_MPAL_HAN1 38 +#define OS_VI_MPAL_HAF1 39 +#define OS_VI_MPAL_HPN2 40 +#define OS_VI_MPAL_HPF2 41 + +#define OS_VI_FPAL_LPN1 42 /* FPAL - Full screen PAL */ +#define OS_VI_FPAL_LPF1 43 +#define OS_VI_FPAL_LAN1 44 +#define OS_VI_FPAL_LAF1 45 +#define OS_VI_FPAL_LPN2 46 +#define OS_VI_FPAL_LPF2 47 +#define OS_VI_FPAL_LAN2 48 +#define OS_VI_FPAL_LAF2 49 +#define OS_VI_FPAL_HPN1 50 +#define OS_VI_FPAL_HPF1 51 +#define OS_VI_FPAL_HAN1 52 +#define OS_VI_FPAL_HAF1 53 +#define OS_VI_FPAL_HPN2 54 +#define OS_VI_FPAL_HPF2 55 + +#define OS_TV_PAL 0 +#define OS_TV_NTSC 1 +#define OS_TV_MPAL 2 +#define OS_VI_UNK28 28 diff --git a/libultraship/include/libultraship/libultraship.h b/libultraship/include/libultraship/libultraship.h new file mode 100644 index 000000000..e01d10e50 --- /dev/null +++ b/libultraship/include/libultraship/libultraship.h @@ -0,0 +1,10 @@ +#pragma once + +#include "libultra.h" +#include "bridge.h" +#include "color.h" +#include "luslog.h" + +#ifdef __cplusplus +#include "classes.h" +#endif diff --git a/libultraship/include/libultraship/luslog.h b/libultraship/include/libultraship/luslog.h new file mode 100644 index 000000000..d5a15ef5d --- /dev/null +++ b/libultraship/include/libultraship/luslog.h @@ -0,0 +1,3 @@ +#pragma once + +#include "log/luslog.h" diff --git a/libultraship/include/libultraship/window/gui/GfxDebuggerWindow.h b/libultraship/include/libultraship/window/gui/GfxDebuggerWindow.h new file mode 100644 index 000000000..43666e2f0 --- /dev/null +++ b/libultraship/include/libultraship/window/gui/GfxDebuggerWindow.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ship/window/gui/GuiWindow.h" +#include +#include + +namespace Fast { +union F3DGfx; +class Interpreter; +} // namespace Fast + +namespace LUS { + +class GfxDebuggerWindow : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + virtual ~GfxDebuggerWindow(); + + protected: + void InitElement() override; + void UpdateElement() override; + void DrawElement() override; + + private: + void DrawDisasNode(const Fast::F3DGfx* cmd, std::vector& gfxPath, float parentPosY) const; + void DrawDisas(); + + private: + std::vector mLastBreakPoint = {}; + std::weak_ptr mInterpreter; +}; + +} // namespace LUS diff --git a/libultraship/include/ship/Context.h b/libultraship/include/ship/Context.h new file mode 100644 index 000000000..15c69b032 --- /dev/null +++ b/libultraship/include/ship/Context.h @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "ship/audio/Audio.h" + +namespace spdlog { +class logger; +} + +namespace Fast { +class GfxDebugger; +} + +namespace Ship { + +class Console; +class ConsoleVariable; +class ControlDeck; +class CrashHandler; +class Window; +class Config; +class ResourceManager; +class FileDropMgr; + +class Context { + public: + static std::shared_ptr GetInstance(); + static std::shared_ptr CreateInstance(const std::string name, const std::string shortName, + const std::string configFilePath, + const std::vector& archivePaths = {}, + const std::unordered_set& validHashes = {}, + uint32_t reservedThreadCount = 1, AudioSettings audioSettings = {}, + std::shared_ptr window = nullptr, + std::shared_ptr controlDeck = nullptr); + static std::shared_ptr CreateUninitializedInstance(const std::string name, const std::string shortName, + const std::string configFilePath); + static std::string GetAppBundlePath(); + static std::string GetAppDirectoryPath(std::string appName = ""); + static std::string GetPathRelativeToAppDirectory(const std::string path, std::string appName = ""); + static std::string GetPathRelativeToAppBundle(const std::string path); + static std::string LocateFileAcrossAppDirs(const std::string path, std::string appName = ""); + + Context(std::string name, std::string shortName, std::string configFilePath); + ~Context(); + + bool Init(const std::vector& archivePaths, const std::unordered_set& validHashes, + uint32_t reservedThreadCount, AudioSettings audioSettings, std::shared_ptr window = nullptr, + std::shared_ptr controlDeck = nullptr); + + std::shared_ptr GetLogger(); + std::shared_ptr GetConfig(); + std::shared_ptr GetConsoleVariables(); + std::shared_ptr GetResourceManager(); + std::shared_ptr GetControlDeck(); + std::shared_ptr GetCrashHandler(); + std::shared_ptr GetWindow(); + std::shared_ptr GetConsole(); + std::shared_ptr