From ea890d4064180701ef9f07e985f398504d4e4915 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 2 Mar 2026 12:50:45 +0100 Subject: [PATCH] Fix relocated files --- CMakeLists.txt | 8 +- files.cmake | 480 +++++++++++++------------- src/dusk/OSContext.cpp | 92 +++++ src/dusk/OSMutex.cpp | 243 ++++++++++++++ src/dusk/OSThread.cpp | 747 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1329 insertions(+), 241 deletions(-) create mode 100644 src/dusk/OSContext.cpp create mode 100644 src/dusk/OSMutex.cpp create mode 100644 src/dusk/OSThread.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5354ba1754..f9761fc513 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,13 @@ add_library(game_debug STATIC ${JSYSTEM_DEBUG_FILES} ${SSYSTEM_FILES}) target_compile_definitions(game_debug PRIVATE TARGET_PC VERSION=0 $<$:DEBUG=1>) # Make these properties PUBLIC so that the regular game target also sees them. -target_include_directories(game_debug PUBLIC include src assets/${DUSK_TP_VERSION} ${CMAKE_BINARY_DIR}/../${DUSK_TP_VERSION}/include build/${DUSK_TP_VERSION}/include) +target_include_directories(game_debug PUBLIC + include + src + assets/${DUSK_TP_VERSION} + libs/JSystem/include + ${CMAKE_BINARY_DIR}/../${DUSK_TP_VERSION}/include + build/${DUSK_TP_VERSION}/include) target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::si aurora::vi aurora::pad aurora::mtx) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) diff --git a/files.cmake b/files.cmake index c9c7b5fa4a..7534864a45 100644 --- a/files.cmake +++ b/files.cmake @@ -315,243 +315,243 @@ set(SSYSTEM_FILES ) set(JSYSTEM_DEBUG_FILES - src/JSystem/JParticle/JPAResourceManager.cpp - src/JSystem/JParticle/JPAResource.cpp - src/JSystem/JParticle/JPABaseShape.cpp - src/JSystem/JParticle/JPAExtraShape.cpp - src/JSystem/JParticle/JPAChildShape.cpp - src/JSystem/JParticle/JPAExTexShape.cpp - src/JSystem/JParticle/JPADynamicsBlock.cpp - src/JSystem/JParticle/JPAFieldBlock.cpp - src/JSystem/JParticle/JPAKeyBlock.cpp - src/JSystem/JParticle/JPATexture.cpp - src/JSystem/JParticle/JPAResourceLoader.cpp - src/JSystem/JParticle/JPAEmitterManager.cpp - src/JSystem/JParticle/JPAEmitter.cpp - src/JSystem/JParticle/JPAParticle.cpp - src/JSystem/JParticle/JPAMath.cpp - src/JSystem/JFramework/JFWSystem.cpp - src/JSystem/JFramework/JFWDisplay.cpp - src/JSystem/J3DU/J3DUClipper.cpp - src/JSystem/J3DU/J3DUDL.cpp - src/JSystem/JKernel/JKRHeap.cpp - src/JSystem/JKernel/JKRExpHeap.cpp - src/JSystem/JKernel/JKRSolidHeap.cpp - src/JSystem/JKernel/JKRAssertHeap.cpp - src/JSystem/JKernel/JKRDisposer.cpp - src/JSystem/JKernel/JKRThread.cpp - src/JSystem/JKernel/JKRAram.cpp - src/JSystem/JKernel/JKRAramHeap.cpp - src/JSystem/JKernel/JKRAramBlock.cpp - src/JSystem/JKernel/JKRAramPiece.cpp - src/JSystem/JKernel/JKRAramStream.cpp - src/JSystem/JKernel/JKRFileLoader.cpp - src/JSystem/JKernel/JKRFileFinder.cpp - src/JSystem/JKernel/JKRFileCache.cpp - src/JSystem/JKernel/JKRArchivePub.cpp - src/JSystem/JKernel/JKRArchivePri.cpp - src/JSystem/JKernel/JKRMemArchive.cpp - src/JSystem/JKernel/JKRAramArchive.cpp - src/JSystem/JKernel/JKRDvdArchive.cpp - src/JSystem/JKernel/JKRCompArchive.cpp - src/JSystem/JKernel/JKRFile.cpp - src/JSystem/JKernel/JKRDvdFile.cpp - src/JSystem/JKernel/JKRDvdRipper.cpp - src/JSystem/JKernel/JKRDvdAramRipper.cpp - src/JSystem/JKernel/JKRDecomp.cpp - src/JSystem/JMath/JMath.cpp - src/JSystem/JMath/random.cpp - src/JSystem/JMath/JMATrigonometric.cpp - src/JSystem/JSupport/JSUList.cpp - src/JSystem/JSupport/JSUInputStream.cpp - src/JSystem/JSupport/JSUOutputStream.cpp - src/JSystem/JSupport/JSUMemoryStream.cpp - src/JSystem/JSupport/JSUFileStream.cpp - src/JSystem/JUtility/JUTCacheFont.cpp - src/JSystem/JUtility/JUTResource.cpp - src/JSystem/JUtility/JUTTexture.cpp - src/JSystem/JUtility/JUTPalette.cpp - src/JSystem/JUtility/JUTNameTab.cpp - src/JSystem/JUtility/JUTGraphFifo.cpp - src/JSystem/JUtility/JUTFont.cpp - src/JSystem/JUtility/JUTResFont.cpp - src/JSystem/JUtility/JUTDbPrint.cpp - src/JSystem/JUtility/JUTGamePad.cpp - src/JSystem/JUtility/JUTException.cpp - src/JSystem/JUtility/JUTDirectPrint.cpp - src/JSystem/JUtility/JUTAssert.cpp - src/JSystem/JUtility/JUTVideo.cpp - src/JSystem/JUtility/JUTXfb.cpp - src/JSystem/JUtility/JUTFader.cpp - src/JSystem/JUtility/JUTProcBar.cpp - src/JSystem/JUtility/JUTConsole.cpp - src/JSystem/JUtility/JUTDirectFile.cpp - src/JSystem/JUtility/JUTFontData_Ascfont_fix12.cpp - src/JSystem/JStage/JSGActor.cpp - src/JSystem/JStage/JSGAmbientLight.cpp - src/JSystem/JStage/JSGCamera.cpp - src/JSystem/JStage/JSGFog.cpp - src/JSystem/JStage/JSGLight.cpp - src/JSystem/JStage/JSGObject.cpp - src/JSystem/JStage/JSGSystem.cpp - src/JSystem/J2DGraph/J2DGrafContext.cpp - src/JSystem/J2DGraph/J2DOrthoGraph.cpp - src/JSystem/J2DGraph/J2DTevs.cpp - src/JSystem/J2DGraph/J2DMaterial.cpp - src/JSystem/J2DGraph/J2DMatBlock.cpp - src/JSystem/J2DGraph/J2DMaterialFactory.cpp - src/JSystem/J2DGraph/J2DPrint.cpp - src/JSystem/J2DGraph/J2DPane.cpp - src/JSystem/J2DGraph/J2DScreen.cpp - src/JSystem/J2DGraph/J2DWindow.cpp - src/JSystem/J2DGraph/J2DPicture.cpp - src/JSystem/J2DGraph/J2DTextBox.cpp - src/JSystem/J2DGraph/J2DWindowEx.cpp - src/JSystem/J2DGraph/J2DPictureEx.cpp - src/JSystem/J2DGraph/J2DTextBoxEx.cpp - src/JSystem/J2DGraph/J2DAnmLoader.cpp - src/JSystem/J2DGraph/J2DAnimation.cpp - src/JSystem/J2DGraph/J2DManage.cpp - src/JSystem/J3DGraphBase/J3DGD.cpp - src/JSystem/J3DGraphBase/J3DSys.cpp - src/JSystem/J3DGraphBase/J3DVertex.cpp - src/JSystem/J3DGraphBase/J3DTransform.cpp - src/JSystem/J3DGraphBase/J3DTexture.cpp - src/JSystem/J3DGraphBase/J3DPacket.cpp - src/JSystem/J3DGraphBase/J3DShapeMtx.cpp - src/JSystem/J3DGraphBase/J3DShapeDraw.cpp - src/JSystem/J3DGraphBase/J3DShape.cpp - src/JSystem/J3DGraphBase/J3DMaterial.cpp - src/JSystem/J3DGraphBase/J3DMatBlock.cpp - src/JSystem/J3DGraphBase/J3DTevs.cpp - src/JSystem/J3DGraphBase/J3DDrawBuffer.cpp - src/JSystem/J3DGraphBase/J3DStruct.cpp - src/JSystem/J3DGraphAnimator/J3DShapeTable.cpp - src/JSystem/J3DGraphAnimator/J3DJointTree.cpp - src/JSystem/J3DGraphAnimator/J3DModelData.cpp - src/JSystem/J3DGraphAnimator/J3DMtxBuffer.cpp - src/JSystem/J3DGraphAnimator/J3DModel.cpp - src/JSystem/J3DGraphAnimator/J3DAnimation.cpp - src/JSystem/J3DGraphAnimator/J3DMaterialAnm.cpp - src/JSystem/J3DGraphAnimator/J3DSkinDeform.cpp - src/JSystem/J3DGraphAnimator/J3DCluster.cpp - src/JSystem/J3DGraphAnimator/J3DJoint.cpp - src/JSystem/J3DGraphAnimator/J3DMaterialAttach.cpp - src/JSystem/J3DGraphLoader/J3DMaterialFactory.cpp - src/JSystem/J3DGraphLoader/J3DMaterialFactory_v21.cpp - src/JSystem/J3DGraphLoader/J3DClusterLoader.cpp - src/JSystem/J3DGraphLoader/J3DModelLoader.cpp - src/JSystem/J3DGraphLoader/J3DModelLoaderCalcSize.cpp - src/JSystem/J3DGraphLoader/J3DJointFactory.cpp - src/JSystem/J3DGraphLoader/J3DShapeFactory.cpp - src/JSystem/J3DGraphLoader/J3DAnmLoader.cpp - src/JSystem/JStudio/JStudio/ctb.cpp - src/JSystem/JStudio/JStudio/ctb-data.cpp - src/JSystem/JStudio/JStudio/functionvalue.cpp - src/JSystem/JStudio/JStudio/fvb.cpp - src/JSystem/JStudio/JStudio/fvb-data.cpp - src/JSystem/JStudio/JStudio/fvb-data-parse.cpp - src/JSystem/JStudio/JStudio/jstudio-control.cpp - src/JSystem/JStudio/JStudio/jstudio-data.cpp - src/JSystem/JStudio/JStudio/jstudio-math.cpp - src/JSystem/JStudio/JStudio/jstudio-object.cpp - src/JSystem/JStudio/JStudio/object-id.cpp - src/JSystem/JStudio/JStudio/stb.cpp - src/JSystem/JStudio/JStudio/stb-data-parse.cpp - src/JSystem/JStudio/JStudio/stb-data.cpp - src/JSystem/JStudio/JStudio_JStage/control.cpp - src/JSystem/JStudio/JStudio_JStage/object.cpp - src/JSystem/JStudio/JStudio_JStage/object-actor.cpp - src/JSystem/JStudio/JStudio_JStage/object-ambientlight.cpp - src/JSystem/JStudio/JStudio_JStage/object-camera.cpp - src/JSystem/JStudio/JStudio_JStage/object-fog.cpp - src/JSystem/JStudio/JStudio_JStage/object-light.cpp - src/JSystem/JStudio/JStudio_JAudio2/control.cpp - src/JSystem/JStudio/JStudio_JAudio2/object-sound.cpp - src/JSystem/JStudio/JStudio_JParticle/control.cpp - src/JSystem/JStudio/JStudio_JParticle/object-particle.cpp - src/JSystem/JAudio2/JASCalc.cpp - src/JSystem/JAudio2/JASTaskThread.cpp - src/JSystem/JAudio2/JASDvdThread.cpp - src/JSystem/JAudio2/JASCallback.cpp - src/JSystem/JAudio2/JASHeapCtrl.cpp - src/JSystem/JAudio2/JASResArcLoader.cpp - src/JSystem/JAudio2/JASProbe.cpp - src/JSystem/JAudio2/JASReport.cpp - src/JSystem/JAudio2/JASCmdStack.cpp - src/JSystem/JAudio2/JASTrack.cpp - src/JSystem/JAudio2/JASTrackPort.cpp - src/JSystem/JAudio2/JASRegisterParam.cpp - src/JSystem/JAudio2/JASSeqCtrl.cpp - src/JSystem/JAudio2/JASSeqParser.cpp - src/JSystem/JAudio2/JASSeqReader.cpp - src/JSystem/JAudio2/JASAramStream.cpp - src/JSystem/JAudio2/JASBank.cpp - src/JSystem/JAudio2/JASBasicBank.cpp - src/JSystem/JAudio2/JASVoiceBank.cpp - src/JSystem/JAudio2/JASBasicInst.cpp - src/JSystem/JAudio2/JASDrumSet.cpp - src/JSystem/JAudio2/JASBasicWaveBank.cpp - src/JSystem/JAudio2/JASSimpleWaveBank.cpp - src/JSystem/JAudio2/JASWSParser.cpp - src/JSystem/JAudio2/JASBNKParser.cpp - src/JSystem/JAudio2/JASWaveArcLoader.cpp - src/JSystem/JAudio2/JASChannel.cpp - src/JSystem/JAudio2/JASLfo.cpp - src/JSystem/JAudio2/JASOscillator.cpp - src/JSystem/JAudio2/JASAiCtrl.cpp - src/JSystem/JAudio2/JASAudioThread.cpp - src/JSystem/JAudio2/JASAudioReseter.cpp - src/JSystem/JAudio2/JASDSPChannel.cpp - src/JSystem/JAudio2/JASDSPInterface.cpp - src/JSystem/JAudio2/JASDriverIF.cpp - src/JSystem/JAudio2/JASSoundParams.cpp - src/JSystem/JAudio2/dspproc.cpp - src/JSystem/JAudio2/dsptask.cpp - src/JSystem/JAudio2/osdsp.cpp - src/JSystem/JAudio2/osdsp_task.cpp - src/JSystem/JAudio2/JAIAudible.cpp - src/JSystem/JAudio2/JAIAudience.cpp - src/JSystem/JAudio2/JAISe.cpp - src/JSystem/JAudio2/JAISeMgr.cpp - src/JSystem/JAudio2/JAISeq.cpp - src/JSystem/JAudio2/JAISeqDataMgr.cpp - src/JSystem/JAudio2/JAISeqMgr.cpp - src/JSystem/JAudio2/JAISound.cpp - src/JSystem/JAudio2/JAISoundChild.cpp - src/JSystem/JAudio2/JAISoundHandles.cpp - src/JSystem/JAudio2/JAISoundInfo.cpp - src/JSystem/JAudio2/JAISoundParams.cpp - src/JSystem/JAudio2/JAISoundStarter.cpp - src/JSystem/JAudio2/JAIStream.cpp - src/JSystem/JAudio2/JAIStreamDataMgr.cpp - src/JSystem/JAudio2/JAIStreamMgr.cpp - src/JSystem/JAudio2/JAUAudioArcInterpreter.cpp - src/JSystem/JAudio2/JAUAudioArcLoader.cpp - src/JSystem/JAudio2/JAUAudioMgr.cpp - src/JSystem/JAudio2/JAUBankTable.cpp - src/JSystem/JAudio2/JAUClusterSound.cpp - src/JSystem/JAudio2/JAUInitializer.cpp - src/JSystem/JAudio2/JAUSectionHeap.cpp - src/JSystem/JAudio2/JAUSeqCollection.cpp - src/JSystem/JAudio2/JAUSeqDataBlockMgr.cpp - src/JSystem/JAudio2/JAUSoundAnimator.cpp - src/JSystem/JAudio2/JAUSoundTable.cpp - src/JSystem/JAudio2/JAUStreamFileTable.cpp - src/JSystem/JMessage/control.cpp - src/JSystem/JMessage/data.cpp - src/JSystem/JMessage/processor.cpp - src/JSystem/JMessage/resource.cpp - src/JSystem/JMessage/locale.cpp - src/JSystem/JGadget/binary.cpp - src/JSystem/JGadget/define.cpp - src/JSystem/JGadget/linklist.cpp - src/JSystem/JGadget/std-vector.cpp - src/JSystem/JHostIO/JORHostInfo.cpp - src/JSystem/JHostIO/JORServer.cpp - src/JSystem/JHostIO/JHIhioASync.cpp - src/JSystem/JHostIO/JHIRMcc.cpp - src/JSystem/JHostIO/JHIMccBuf.cpp + libs/JSystem/src/JParticle/JPAResourceManager.cpp + libs/JSystem/src/JParticle/JPAResource.cpp + libs/JSystem/src/JParticle/JPABaseShape.cpp + libs/JSystem/src/JParticle/JPAExtraShape.cpp + libs/JSystem/src/JParticle/JPAChildShape.cpp + libs/JSystem/src/JParticle/JPAExTexShape.cpp + libs/JSystem/src/JParticle/JPADynamicsBlock.cpp + libs/JSystem/src/JParticle/JPAFieldBlock.cpp + libs/JSystem/src/JParticle/JPAKeyBlock.cpp + libs/JSystem/src/JParticle/JPATexture.cpp + libs/JSystem/src/JParticle/JPAResourceLoader.cpp + libs/JSystem/src/JParticle/JPAEmitterManager.cpp + libs/JSystem/src/JParticle/JPAEmitter.cpp + libs/JSystem/src/JParticle/JPAParticle.cpp + libs/JSystem/src/JParticle/JPAMath.cpp + libs/JSystem/src/JFramework/JFWSystem.cpp + libs/JSystem/src/JFramework/JFWDisplay.cpp + libs/JSystem/src/J3DU/J3DUClipper.cpp + libs/JSystem/src/J3DU/J3DUDL.cpp + libs/JSystem/src/JKernel/JKRHeap.cpp + libs/JSystem/src/JKernel/JKRExpHeap.cpp + libs/JSystem/src/JKernel/JKRSolidHeap.cpp + libs/JSystem/src/JKernel/JKRAssertHeap.cpp + libs/JSystem/src/JKernel/JKRDisposer.cpp + libs/JSystem/src/JKernel/JKRThread.cpp + libs/JSystem/src/JKernel/JKRAram.cpp + libs/JSystem/src/JKernel/JKRAramHeap.cpp + libs/JSystem/src/JKernel/JKRAramBlock.cpp + libs/JSystem/src/JKernel/JKRAramPiece.cpp + libs/JSystem/src/JKernel/JKRAramStream.cpp + libs/JSystem/src/JKernel/JKRFileLoader.cpp + libs/JSystem/src/JKernel/JKRFileFinder.cpp + libs/JSystem/src/JKernel/JKRFileCache.cpp + libs/JSystem/src/JKernel/JKRArchivePub.cpp + libs/JSystem/src/JKernel/JKRArchivePri.cpp + libs/JSystem/src/JKernel/JKRMemArchive.cpp + libs/JSystem/src/JKernel/JKRAramArchive.cpp + libs/JSystem/src/JKernel/JKRDvdArchive.cpp + libs/JSystem/src/JKernel/JKRCompArchive.cpp + libs/JSystem/src/JKernel/JKRFile.cpp + libs/JSystem/src/JKernel/JKRDvdFile.cpp + libs/JSystem/src/JKernel/JKRDvdRipper.cpp + libs/JSystem/src/JKernel/JKRDvdAramRipper.cpp + libs/JSystem/src/JKernel/JKRDecomp.cpp + libs/JSystem/src/JMath/JMath.cpp + libs/JSystem/src/JMath/random.cpp + libs/JSystem/src/JMath/JMATrigonometric.cpp + libs/JSystem/src/JSupport/JSUList.cpp + libs/JSystem/src/JSupport/JSUInputStream.cpp + libs/JSystem/src/JSupport/JSUOutputStream.cpp + libs/JSystem/src/JSupport/JSUMemoryStream.cpp + libs/JSystem/src/JSupport/JSUFileStream.cpp + libs/JSystem/src/JUtility/JUTCacheFont.cpp + libs/JSystem/src/JUtility/JUTResource.cpp + libs/JSystem/src/JUtility/JUTTexture.cpp + libs/JSystem/src/JUtility/JUTPalette.cpp + libs/JSystem/src/JUtility/JUTNameTab.cpp + libs/JSystem/src/JUtility/JUTGraphFifo.cpp + libs/JSystem/src/JUtility/JUTFont.cpp + libs/JSystem/src/JUtility/JUTResFont.cpp + libs/JSystem/src/JUtility/JUTDbPrint.cpp + libs/JSystem/src/JUtility/JUTGamePad.cpp + libs/JSystem/src/JUtility/JUTException.cpp + libs/JSystem/src/JUtility/JUTDirectPrint.cpp + libs/JSystem/src/JUtility/JUTAssert.cpp + libs/JSystem/src/JUtility/JUTVideo.cpp + libs/JSystem/src/JUtility/JUTXfb.cpp + libs/JSystem/src/JUtility/JUTFader.cpp + libs/JSystem/src/JUtility/JUTProcBar.cpp + libs/JSystem/src/JUtility/JUTConsole.cpp + libs/JSystem/src/JUtility/JUTDirectFile.cpp + libs/JSystem/src/JUtility/JUTFontData_Ascfont_fix12.cpp + libs/JSystem/src/JStage/JSGActor.cpp + libs/JSystem/src/JStage/JSGAmbientLight.cpp + libs/JSystem/src/JStage/JSGCamera.cpp + libs/JSystem/src/JStage/JSGFog.cpp + libs/JSystem/src/JStage/JSGLight.cpp + libs/JSystem/src/JStage/JSGObject.cpp + libs/JSystem/src/JStage/JSGSystem.cpp + libs/JSystem/src/J2DGraph/J2DGrafContext.cpp + libs/JSystem/src/J2DGraph/J2DOrthoGraph.cpp + libs/JSystem/src/J2DGraph/J2DTevs.cpp + libs/JSystem/src/J2DGraph/J2DMaterial.cpp + libs/JSystem/src/J2DGraph/J2DMatBlock.cpp + libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp + libs/JSystem/src/J2DGraph/J2DPrint.cpp + libs/JSystem/src/J2DGraph/J2DPane.cpp + libs/JSystem/src/J2DGraph/J2DScreen.cpp + libs/JSystem/src/J2DGraph/J2DWindow.cpp + libs/JSystem/src/J2DGraph/J2DPicture.cpp + libs/JSystem/src/J2DGraph/J2DTextBox.cpp + libs/JSystem/src/J2DGraph/J2DWindowEx.cpp + libs/JSystem/src/J2DGraph/J2DPictureEx.cpp + libs/JSystem/src/J2DGraph/J2DTextBoxEx.cpp + libs/JSystem/src/J2DGraph/J2DAnmLoader.cpp + libs/JSystem/src/J2DGraph/J2DAnimation.cpp + libs/JSystem/src/J2DGraph/J2DManage.cpp + libs/JSystem/src/J3DGraphBase/J3DGD.cpp + libs/JSystem/src/J3DGraphBase/J3DSys.cpp + libs/JSystem/src/J3DGraphBase/J3DVertex.cpp + libs/JSystem/src/J3DGraphBase/J3DTransform.cpp + libs/JSystem/src/J3DGraphBase/J3DTexture.cpp + libs/JSystem/src/J3DGraphBase/J3DPacket.cpp + libs/JSystem/src/J3DGraphBase/J3DShapeMtx.cpp + libs/JSystem/src/J3DGraphBase/J3DShapeDraw.cpp + libs/JSystem/src/J3DGraphBase/J3DShape.cpp + libs/JSystem/src/J3DGraphBase/J3DMaterial.cpp + libs/JSystem/src/J3DGraphBase/J3DMatBlock.cpp + libs/JSystem/src/J3DGraphBase/J3DTevs.cpp + libs/JSystem/src/J3DGraphBase/J3DDrawBuffer.cpp + libs/JSystem/src/J3DGraphBase/J3DStruct.cpp + libs/JSystem/src/J3DGraphAnimator/J3DShapeTable.cpp + libs/JSystem/src/J3DGraphAnimator/J3DJointTree.cpp + libs/JSystem/src/J3DGraphAnimator/J3DModelData.cpp + libs/JSystem/src/J3DGraphAnimator/J3DMtxBuffer.cpp + libs/JSystem/src/J3DGraphAnimator/J3DModel.cpp + libs/JSystem/src/J3DGraphAnimator/J3DAnimation.cpp + libs/JSystem/src/J3DGraphAnimator/J3DMaterialAnm.cpp + libs/JSystem/src/J3DGraphAnimator/J3DSkinDeform.cpp + libs/JSystem/src/J3DGraphAnimator/J3DCluster.cpp + libs/JSystem/src/J3DGraphAnimator/J3DJoint.cpp + libs/JSystem/src/J3DGraphAnimator/J3DMaterialAttach.cpp + libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory.cpp + libs/JSystem/src/J3DGraphLoader/J3DMaterialFactory_v21.cpp + libs/JSystem/src/J3DGraphLoader/J3DClusterLoader.cpp + libs/JSystem/src/J3DGraphLoader/J3DModelLoader.cpp + libs/JSystem/src/J3DGraphLoader/J3DModelLoaderCalcSize.cpp + libs/JSystem/src/J3DGraphLoader/J3DJointFactory.cpp + libs/JSystem/src/J3DGraphLoader/J3DShapeFactory.cpp + libs/JSystem/src/J3DGraphLoader/J3DAnmLoader.cpp + libs/JSystem/src/JStudio/JStudio/ctb.cpp + libs/JSystem/src/JStudio/JStudio/ctb-data.cpp + libs/JSystem/src/JStudio/JStudio/functionvalue.cpp + libs/JSystem/src/JStudio/JStudio/fvb.cpp + libs/JSystem/src/JStudio/JStudio/fvb-data.cpp + libs/JSystem/src/JStudio/JStudio/fvb-data-parse.cpp + libs/JSystem/src/JStudio/JStudio/jstudio-control.cpp + libs/JSystem/src/JStudio/JStudio/jstudio-data.cpp + libs/JSystem/src/JStudio/JStudio/jstudio-math.cpp + libs/JSystem/src/JStudio/JStudio/jstudio-object.cpp + libs/JSystem/src/JStudio/JStudio/object-id.cpp + libs/JSystem/src/JStudio/JStudio/stb.cpp + libs/JSystem/src/JStudio/JStudio/stb-data-parse.cpp + libs/JSystem/src/JStudio/JStudio/stb-data.cpp + libs/JSystem/src/JStudio/JStudio_JStage/control.cpp + libs/JSystem/src/JStudio/JStudio_JStage/object.cpp + libs/JSystem/src/JStudio/JStudio_JStage/object-actor.cpp + libs/JSystem/src/JStudio/JStudio_JStage/object-ambientlight.cpp + libs/JSystem/src/JStudio/JStudio_JStage/object-camera.cpp + libs/JSystem/src/JStudio/JStudio_JStage/object-fog.cpp + libs/JSystem/src/JStudio/JStudio_JStage/object-light.cpp + libs/JSystem/src/JStudio/JStudio_JAudio2/control.cpp + libs/JSystem/src/JStudio/JStudio_JAudio2/object-sound.cpp + libs/JSystem/src/JStudio/JStudio_JParticle/control.cpp + libs/JSystem/src/JStudio/JStudio_JParticle/object-particle.cpp + libs/JSystem/src/JAudio2/JASCalc.cpp + libs/JSystem/src/JAudio2/JASTaskThread.cpp + libs/JSystem/src/JAudio2/JASDvdThread.cpp + libs/JSystem/src/JAudio2/JASCallback.cpp + libs/JSystem/src/JAudio2/JASHeapCtrl.cpp + libs/JSystem/src/JAudio2/JASResArcLoader.cpp + libs/JSystem/src/JAudio2/JASProbe.cpp + libs/JSystem/src/JAudio2/JASReport.cpp + libs/JSystem/src/JAudio2/JASCmdStack.cpp + libs/JSystem/src/JAudio2/JASTrack.cpp + libs/JSystem/src/JAudio2/JASTrackPort.cpp + libs/JSystem/src/JAudio2/JASRegisterParam.cpp + libs/JSystem/src/JAudio2/JASSeqCtrl.cpp + libs/JSystem/src/JAudio2/JASSeqParser.cpp + libs/JSystem/src/JAudio2/JASSeqReader.cpp + libs/JSystem/src/JAudio2/JASAramStream.cpp + libs/JSystem/src/JAudio2/JASBank.cpp + libs/JSystem/src/JAudio2/JASBasicBank.cpp + libs/JSystem/src/JAudio2/JASVoiceBank.cpp + libs/JSystem/src/JAudio2/JASBasicInst.cpp + libs/JSystem/src/JAudio2/JASDrumSet.cpp + libs/JSystem/src/JAudio2/JASBasicWaveBank.cpp + libs/JSystem/src/JAudio2/JASSimpleWaveBank.cpp + libs/JSystem/src/JAudio2/JASWSParser.cpp + libs/JSystem/src/JAudio2/JASBNKParser.cpp + libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp + libs/JSystem/src/JAudio2/JASChannel.cpp + libs/JSystem/src/JAudio2/JASLfo.cpp + libs/JSystem/src/JAudio2/JASOscillator.cpp + libs/JSystem/src/JAudio2/JASAiCtrl.cpp + libs/JSystem/src/JAudio2/JASAudioThread.cpp + libs/JSystem/src/JAudio2/JASAudioReseter.cpp + libs/JSystem/src/JAudio2/JASDSPChannel.cpp + libs/JSystem/src/JAudio2/JASDSPInterface.cpp + libs/JSystem/src/JAudio2/JASDriverIF.cpp + libs/JSystem/src/JAudio2/JASSoundParams.cpp + libs/JSystem/src/JAudio2/dspproc.cpp + libs/JSystem/src/JAudio2/dsptask.cpp + libs/JSystem/src/JAudio2/osdsp.cpp + libs/JSystem/src/JAudio2/osdsp_task.cpp + libs/JSystem/src/JAudio2/JAIAudible.cpp + libs/JSystem/src/JAudio2/JAIAudience.cpp + libs/JSystem/src/JAudio2/JAISe.cpp + libs/JSystem/src/JAudio2/JAISeMgr.cpp + libs/JSystem/src/JAudio2/JAISeq.cpp + libs/JSystem/src/JAudio2/JAISeqDataMgr.cpp + libs/JSystem/src/JAudio2/JAISeqMgr.cpp + libs/JSystem/src/JAudio2/JAISound.cpp + libs/JSystem/src/JAudio2/JAISoundChild.cpp + libs/JSystem/src/JAudio2/JAISoundHandles.cpp + libs/JSystem/src/JAudio2/JAISoundInfo.cpp + libs/JSystem/src/JAudio2/JAISoundParams.cpp + libs/JSystem/src/JAudio2/JAISoundStarter.cpp + libs/JSystem/src/JAudio2/JAIStream.cpp + libs/JSystem/src/JAudio2/JAIStreamDataMgr.cpp + libs/JSystem/src/JAudio2/JAIStreamMgr.cpp + libs/JSystem/src/JAudio2/JAUAudioArcInterpreter.cpp + libs/JSystem/src/JAudio2/JAUAudioArcLoader.cpp + libs/JSystem/src/JAudio2/JAUAudioMgr.cpp + libs/JSystem/src/JAudio2/JAUBankTable.cpp + libs/JSystem/src/JAudio2/JAUClusterSound.cpp + libs/JSystem/src/JAudio2/JAUInitializer.cpp + libs/JSystem/src/JAudio2/JAUSectionHeap.cpp + libs/JSystem/src/JAudio2/JAUSeqCollection.cpp + libs/JSystem/src/JAudio2/JAUSeqDataBlockMgr.cpp + libs/JSystem/src/JAudio2/JAUSoundAnimator.cpp + libs/JSystem/src/JAudio2/JAUSoundTable.cpp + libs/JSystem/src/JAudio2/JAUStreamFileTable.cpp + libs/JSystem/src/JMessage/control.cpp + libs/JSystem/src/JMessage/data.cpp + libs/JSystem/src/JMessage/processor.cpp + libs/JSystem/src/JMessage/resource.cpp + libs/JSystem/src/JMessage/locale.cpp + libs/JSystem/src/JGadget/binary.cpp + libs/JSystem/src/JGadget/define.cpp + libs/JSystem/src/JGadget/linklist.cpp + libs/JSystem/src/JGadget/std-vector.cpp + libs/JSystem/src/JHostIO/JORHostInfo.cpp + libs/JSystem/src/JHostIO/JORServer.cpp + libs/JSystem/src/JHostIO/JHIhioASync.cpp + libs/JSystem/src/JHostIO/JHIRMcc.cpp + libs/JSystem/src/JHostIO/JHIMccBuf.cpp ) set(JSYSTEM_FILES @@ -1333,7 +1333,7 @@ set(DUSK_FILES src/dusk/imgui/debug_overlay.cpp src/dusk/imgui/heaps.cpp src/dusk/offset_ptr.cpp - src/dolphin/os/OSContext.cpp - src/dolphin/os/OSThread.cpp - src/dolphin/os/OSMutex.cpp + src/dusk/OSContext.cpp + src/dusk/OSThread.cpp + src/dusk/OSMutex.cpp ) diff --git a/src/dusk/OSContext.cpp b/src/dusk/OSContext.cpp new file mode 100644 index 0000000000..4f54b8b963 --- /dev/null +++ b/src/dusk/OSContext.cpp @@ -0,0 +1,92 @@ +// OSContext.cpp - PC implementation of GameCube OSContext API +// Replaces PowerPC assembly context switching with minimal PC stubs. +// On PC there is no register-level context save/restore; the OS handles +// thread contexts natively via std::thread. + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// --- Current context pointer (per-process, not per-thread) --- +static OSContext* sCurrentContext = nullptr; + +OSContext* OSGetCurrentContext(void) { + return sCurrentContext; +} + +void OSSetCurrentContext(OSContext* context) { + sCurrentContext = context; +} + +void OSClearContext(OSContext* context) { + if (!context) return; + context->mode = 0; + context->state = 0; +} + +void OSInitContext(OSContext* context, u32 pc, u32 newsp) { + if (!context) return; + memset(context, 0, sizeof(OSContext)); + context->srr0 = pc; + context->gpr[1] = newsp; +} + +u32 OSSaveContext(OSContext* context) { + // On PC we don't save PowerPC registers. + // Return 0 = "context was just saved" (as opposed to 1 = "restored from save"). + return 0; +} + +void OSLoadContext(OSContext* context) { + // No-op on PC (no PowerPC register restore) +} + +void OSDumpContext(OSContext* context) { + if (!context) { + OSReport("[PC] OSDumpContext: NULL context\n"); + return; + } + OSReport("[PC] OSDumpContext: context at %p (no register info on PC)\n", context); +} + +void OSFillFPUContext(OSContext* context) { + // No-op on PC (no PowerPC FPU state) +} + +void OSLoadFPUContext(OSContext* fpucontext) { + // No-op on PC +} + +void OSSaveFPUContext(OSContext* fpucontext) { + // No-op on PC +} + +u32 OSGetStackPointer(void) { + // Return approximate stack pointer + volatile u32 dummy; + return (u32)(uintptr_t)&dummy; +} + +u32 OSSwitchStack(u32 newsp) { + // Not meaningful on PC - return current sp + return OSGetStackPointer(); +} + +int OSSwitchFiber(u32 pc, u32 newsp) { + // Not meaningful on PC + OSReport("[PC] OSSwitchFiber: not supported on PC\n"); + return 0; +} + +void __OSContextInit(void) { + // On GC this installs the FPU exception handler. + // On PC nothing to do. +} + +#ifdef __cplusplus +} +#endif diff --git a/src/dusk/OSMutex.cpp b/src/dusk/OSMutex.cpp new file mode 100644 index 0000000000..9ec3197cb1 --- /dev/null +++ b/src/dusk/OSMutex.cpp @@ -0,0 +1,243 @@ +// OSMutex.cpp - PC implementation of GameCube OSMutex/OSCond API +// Uses std::recursive_mutex and std::condition_variable_any behind the +// unchanged GameCube C API. The OSMutex struct layout is preserved so +// game code can read its fields. + +#include +#include + +#include +#include +#include +#include +#include + +// ============================================================================ +// Malloc-based allocator to bypass JKRHeap operator new/delete +// Without this, side-table allocations call operator new -> JKRHeap::alloc +// -> OSLockMutex -> GetMutexData -> operator new ... infinite recursion. +// ============================================================================ + +template +struct MallocAllocator { + using value_type = T; + MallocAllocator() = default; + template MallocAllocator(const MallocAllocator&) noexcept {} + T* allocate(std::size_t n) { + void* p = std::malloc(n * sizeof(T)); + if (!p) throw std::bad_alloc(); + return static_cast(p); + } + void deallocate(T* p, std::size_t) noexcept { std::free(p); } + template bool operator==(const MallocAllocator&) const noexcept { return true; } + template bool operator!=(const MallocAllocator&) const noexcept { return false; } +}; + +template +struct MallocDeleter { + void operator()(T* p) const { + p->~T(); + std::free(p); + } +}; + +template +std::unique_ptr> make_malloc_unique(Args&&... args) { + void* mem = std::malloc(sizeof(T)); + if (!mem) throw std::bad_alloc(); + T* obj = new (mem) T(std::forward(args)...); + return std::unique_ptr>(obj); +} + +// ============================================================================ +// Side-table: native mutex per OSMutex +// ============================================================================ + +struct PCMutexData { + std::recursive_mutex nativeMutex; +}; + +template +using MallocMap = std::unordered_map, std::equal_to, + MallocAllocator>>; + +// Lazy-initialized to avoid DLL static init crashes +static std::mutex& GetMutexMapMutex() { + static std::mutex mtx; + return mtx; +} +static MallocMap>>& GetMutexMap() { + static MallocMap>> map; + return map; +} + +static PCMutexData& GetMutexData(OSMutex* mutex) { + std::lock_guard lock(GetMutexMapMutex()); + auto& map = GetMutexMap(); + auto it = map.find(mutex); + if (it == map.end()) { + auto result = map.emplace(mutex, make_malloc_unique()); + return *result.first->second; + } + return *it->second; +} + +// ============================================================================ +// Side-table: native condition variable per OSCond +// ============================================================================ + +struct PCCondData { + std::condition_variable_any cv; +}; + +// Lazy-initialized to avoid DLL static init crashes +static std::mutex& GetCondMapMutex() { + static std::mutex mtx; + return mtx; +} +static MallocMap>>& GetCondMap() { + static MallocMap>> map; + return map; +} + +static PCCondData& GetCondData(OSCond* cond) { + std::lock_guard lock(GetCondMapMutex()); + auto& map = GetCondMap(); + auto it = map.find(cond); + if (it == map.end()) { + auto result = map.emplace(cond, make_malloc_unique()); + return *result.first->second; + } + return *it->second; +} + +// ============================================================================ +// C API functions +// ============================================================================ + +extern "C" { + +void OSInitMutex(OSMutex* mutex) { + if (!mutex) return; + OSInitThreadQueue(&mutex->queue); + mutex->thread = nullptr; + mutex->count = 0; + + // Create/reset side-table entry + GetMutexData(mutex); +} + +void OSLockMutex(OSMutex* mutex) { + if (!mutex) return; + + PCMutexData& data = GetMutexData(mutex); + data.nativeMutex.lock(); + + // Update GC-visible fields + OSThread* currentThread = OSGetCurrentThread(); + mutex->thread = currentThread; + mutex->count++; +} + +void OSUnlockMutex(OSMutex* mutex) { + if (!mutex) return; + + OSThread* currentThread = OSGetCurrentThread(); + if (mutex->thread != currentThread) return; + + mutex->count--; + if (mutex->count == 0) { + mutex->thread = nullptr; + } + + PCMutexData& data = GetMutexData(mutex); + data.nativeMutex.unlock(); +} + +BOOL OSTryLockMutex(OSMutex* mutex) { + if (!mutex) return FALSE; + + PCMutexData& data = GetMutexData(mutex); + if (data.nativeMutex.try_lock()) { + OSThread* currentThread = OSGetCurrentThread(); + mutex->thread = currentThread; + mutex->count++; + return TRUE; + } + return FALSE; +} + +// ============================================================================ +// Internal: unlock all mutexes held by a thread (called on thread exit) +// ============================================================================ + +void __OSUnlockAllMutex(OSThread* thread) { + // On GC this walks the thread's mutex queue. + // On PC the native mutexes are cleaned up when threads exit. + // Clear the GC-visible queue. + if (!thread) return; + thread->queueMutex.head = nullptr; + thread->queueMutex.tail = nullptr; +} + +int __OSCheckDeadLock(OSThread* thread) { + // Simplified: native OS handles deadlock detection. + return 0; +} + +int __OSCheckMutexes(OSThread* thread) { + return 1; +} + +// ============================================================================ +// Condition Variable API +// ============================================================================ + +void OSInitCond(OSCond* cond) { + if (!cond) return; + OSInitThreadQueue(&cond->queue); + GetCondData(cond); +} + +void OSWaitCond(OSCond* cond, OSMutex* mutex) { + if (!cond || !mutex) return; + + PCCondData& condData = GetCondData(cond); + PCMutexData& mutexData = GetMutexData(mutex); + + // Save and clear the GC mutex state + OSThread* currentThread = OSGetCurrentThread(); + s32 savedCount = mutex->count; + mutex->count = 0; + mutex->thread = nullptr; + + // Unlock the recursive mutex the same number of times it was locked + for (s32 i = 0; i < savedCount; i++) { + mutexData.nativeMutex.unlock(); + } + + // Wait on the condition variable + { + std::unique_lock lock(mutexData.nativeMutex); + condData.cv.wait(lock); + } + + // Re-lock the recursive mutex the same number of times + for (s32 i = 0; i < savedCount; i++) { + mutexData.nativeMutex.lock(); + } + + // Restore GC mutex state + mutex->thread = currentThread; + mutex->count = savedCount; +} + +void OSSignalCond(OSCond* cond) { + if (!cond) return; + PCCondData& condData = GetCondData(cond); + condData.cv.notify_all(); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/dusk/OSThread.cpp b/src/dusk/OSThread.cpp new file mode 100644 index 0000000000..96ef2bcfff --- /dev/null +++ b/src/dusk/OSThread.cpp @@ -0,0 +1,747 @@ +// OSThread.cpp - PC implementation of GameCube OSThread API +// Maps GameCube cooperative threading to native OS threads via std::thread. +// The OSThread struct layout is preserved so game code can read its fields. +// A side-table stores the native std::thread and synchronization primitives. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ============================================================================ +// Malloc-based allocator to bypass JKRHeap operator new/delete +// ============================================================================ + +template +struct MallocAllocator { + using value_type = T; + MallocAllocator() = default; + template MallocAllocator(const MallocAllocator&) noexcept {} + T* allocate(std::size_t n) { + void* p = std::malloc(n * sizeof(T)); + if (!p) throw std::bad_alloc(); + return static_cast(p); + } + void deallocate(T* p, std::size_t) noexcept { std::free(p); } + template bool operator==(const MallocAllocator&) const noexcept { return true; } + template bool operator!=(const MallocAllocator&) const noexcept { return false; } +}; + +template +struct MallocDeleter { + void operator()(T* p) const { + p->~T(); + std::free(p); + } +}; + +template +std::unique_ptr> make_malloc_unique(Args&&... args) { + void* mem = std::malloc(sizeof(T)); + if (!mem) throw std::bad_alloc(); + T* obj = new (mem) T(std::forward(args)...); + return std::unique_ptr>(obj); +} + +template +using MallocMap = std::unordered_map, std::equal_to, + MallocAllocator>>; + +// ============================================================================ +// Side-table: native thread data per OSThread +// ============================================================================ + +struct PCThreadData { + std::thread nativeThread; + std::mutex mtx; + std::condition_variable cv; + void* (*func)(void*); + void* param; + bool started = false; + bool suspended = false; +}; + +// Lazy-initialized to avoid DLL static init crashes (used before DllMain completes) +static std::mutex& GetThreadDataMutex() { + static std::mutex mtx; + return mtx; +} +static MallocMap>>& GetThreadDataMap() { + static MallocMap>> map; + return map; +} + +// Side-table for OSThreadQueue -> condition_variable (for OSSleepThread/OSWakeupThread) +static std::mutex& GetQueueCvMutex() { + static std::mutex mtx; + return mtx; +} +static MallocMap>>& GetQueueCvMap() { + static MallocMap>> map; + return map; +} + +static std::condition_variable& GetQueueCV(OSThreadQueue* queue) { + std::lock_guard lock(GetQueueCvMutex()); + auto& map = GetQueueCvMap(); + auto it = map.find(queue); + if (it == map.end()) { + auto result = map.emplace(queue, make_malloc_unique()); + return *result.first->second; + } + return *it->second; +} + +// ============================================================================ +// Thread-local current thread pointer +// ============================================================================ + +static thread_local OSThread* tls_currentThread = nullptr; + +// ============================================================================ +// Global state +// ============================================================================ + +static OSThread sDefaultThread; +static u8 sDefaultStack[64 * 1024]; +static u32 sDefaultStackEnd = OS_THREAD_STACK_MAGIC; + +OSThreadQueue __OSActiveThreadQueue; + +// Global interrupt mutex (coarse-grained lock replacing interrupt disable) +// Lazy-initialized to avoid DLL static init crashes +static std::recursive_mutex& GetInterruptMutex() { + static std::recursive_mutex mtx; + return mtx; +} +static thread_local int sInterruptLockCount = 0; + +// Scheduler suspend count +static std::atomic sSchedulerSuspendCount{0}; + +// Active thread count +static std::atomic sActiveThreadCount{0}; + +// Switch thread callback +static OSSwitchThreadCallback sSwitchThreadCallback = nullptr; + +// ============================================================================ +// Internal helpers +// ============================================================================ + +// Linked list macros for the active thread queue +static void EnqueueActive(OSThread* thread) { + OSThread* prev = __OSActiveThreadQueue.tail; + if (prev == nullptr) { + __OSActiveThreadQueue.head = thread; + } else { + prev->linkActive.next = thread; + } + thread->linkActive.prev = prev; + thread->linkActive.next = nullptr; + __OSActiveThreadQueue.tail = thread; +} + +static void DequeueActive(OSThread* thread) { + OSThread* next = thread->linkActive.next; + OSThread* prev = thread->linkActive.prev; + if (next == nullptr) { + __OSActiveThreadQueue.tail = prev; + } else { + next->linkActive.prev = prev; + } + if (prev == nullptr) { + __OSActiveThreadQueue.head = next; + } else { + prev->linkActive.next = next; + } + thread->linkActive.next = nullptr; + thread->linkActive.prev = nullptr; +} + +// Thread entry wrapper - runs on the new std::thread +static void ThreadEntryWrapper(OSThread* thread, PCThreadData* data) { + // Set thread-local pointer + tls_currentThread = thread; + + // Set context pointers for this thread + OSClearContext(&thread->context); + OSSetCurrentContext(&thread->context); + + thread->state = OS_THREAD_STATE_RUNNING; + + // Call the actual thread function + void* result = data->func(data->param); + + // Thread returned - equivalent to OSExitThread + thread->val = result; + thread->state = OS_THREAD_STATE_MORIBUND; +} + +// ============================================================================ +// C API functions +// ============================================================================ + +extern "C" { + +void __OSThreadInit(void) { + memset(&sDefaultThread, 0, sizeof(OSThread)); + + sDefaultThread.state = OS_THREAD_STATE_RUNNING; + sDefaultThread.attr = OS_THREAD_ATTR_DETACH; + sDefaultThread.priority = 16; + sDefaultThread.base = 16; + sDefaultThread.suspend = 0; + sDefaultThread.val = (void*)(intptr_t)-1; + sDefaultThread.mutex = nullptr; + sDefaultThread.queue = nullptr; + + OSInitThreadQueue(&sDefaultThread.queueJoin); + sDefaultThread.queueMutex.head = sDefaultThread.queueMutex.tail = nullptr; + sDefaultThread.link.next = sDefaultThread.link.prev = nullptr; + sDefaultThread.linkActive.next = sDefaultThread.linkActive.prev = nullptr; + + // Stack pointers (JKRThread reads these) + sDefaultThread.stackBase = sDefaultStack + sizeof(sDefaultStack); + sDefaultThread.stackEnd = &sDefaultStackEnd; + sDefaultStackEnd = OS_THREAD_STACK_MAGIC; + + OSClearContext(&sDefaultThread.context); + + sDefaultThread.error = 0; + sDefaultThread.specific[0] = nullptr; + sDefaultThread.specific[1] = nullptr; + + // Set as current thread for main thread + tls_currentThread = &sDefaultThread; + + // Active queue + OSInitThreadQueue(&__OSActiveThreadQueue); + EnqueueActive(&sDefaultThread); + sActiveThreadCount = 1; + + OSReport("[PC-OSThread] Thread system initialized (multi-threaded mode)\n"); +} + +// ============================================================================ +// Thread Queue +// ============================================================================ + +void OSInitThreadQueue(OSThreadQueue* queue) { + if (queue) { + queue->head = queue->tail = nullptr; + } +} + +// ============================================================================ +// Current Thread +// ============================================================================ + +OSThread* OSGetCurrentThread(void) { + // Lazy-init for main thread if __OSThreadInit hasn't been called yet + if (tls_currentThread == nullptr) { + __OSThreadInit(); + } + return tls_currentThread; +} + +// ============================================================================ +// Thread Creation +// ============================================================================ + +int OSCreateThread(OSThread* thread, void* (*func)(void*), void* param, + void* stack, u32 stackSize, OSPriority priority, u16 attr) { + if (!thread) return 0; + if (priority < OS_PRIORITY_MIN || priority > OS_PRIORITY_MAX) return 0; + + // Ensure thread system is initialized + OSGetCurrentThread(); + + memset(thread, 0, sizeof(OSThread)); + + thread->state = OS_THREAD_STATE_READY; + thread->attr = attr & 1u; + thread->base = priority; + thread->priority = priority; + thread->suspend = 1; // Created suspended (GC behavior) + thread->val = (void*)(intptr_t)-1; + thread->mutex = nullptr; + + OSInitThreadQueue(&thread->queueJoin); + thread->queueMutex.head = thread->queueMutex.tail = nullptr; + thread->link.next = thread->link.prev = nullptr; + thread->linkActive.next = thread->linkActive.prev = nullptr; + + // Stack (stack points to TOP on GameCube) + thread->stackBase = (u8*)stack; + thread->stackEnd = (u32*)((uintptr_t)stack - stackSize); + *thread->stackEnd = OS_THREAD_STACK_MAGIC; + + OSClearContext(&thread->context); + + thread->error = 0; + thread->specific[0] = nullptr; + thread->specific[1] = nullptr; + + // Create side-table entry (but don't start the thread yet) + { + auto data = make_malloc_unique(); + data->func = func; + data->param = param; + + std::lock_guard lock(GetThreadDataMutex()); + GetThreadDataMap()[thread] = std::move(data); + } + + // Add to active queue + EnqueueActive(thread); + sActiveThreadCount++; + + OSReport("[PC-OSThread] Created thread %p (priority=%d, stackSize=%u)\n", + thread, priority, stackSize); + return 1; +} + +// ============================================================================ +// Resume / Suspend +// ============================================================================ +/* +s32 OSResumeThread(OSThread* thread) { + if (!thread) return 0; + + s32 prevSuspend = thread->suspend; + if (thread->suspend > 0) { + thread->suspend--; + } + + if (thread->suspend == 0) { + std::lock_guard lock(GetThreadDataMutex()); + auto it = GetThreadDataMap().find(thread); + if (it != GetThreadDataMap().end()) { + PCThreadData* data = it->second.get(); + if (!data->started) { + // First resume: launch the native thread + data->started = true; + data->suspended = false; + data->nativeThread = std::thread(ThreadEntryWrapper, thread, data); + data->nativeThread.detach(); + OSReport("[PC-OSThread] Started thread %p\n", thread); + } else if (data->suspended) { + // Resume from suspension: signal the CV + data->suspended = false; + data->cv.notify_one(); + } + } + } + + return prevSuspend; +} + +s32 OSSuspendThread(OSThread* thread) { + if (!thread) return 0; + + s32 prevSuspend = thread->suspend; + thread->suspend++; + + if (prevSuspend == 0) { + std::lock_guard lock(GetThreadDataMutex()); + auto it = GetThreadDataMap().find(thread); + if (it != GetThreadDataMap().end()) { + PCThreadData* data = it->second.get(); + if (data->started) { + data->suspended = true; + // The thread must check its suspended flag and wait + } + } + } + + return prevSuspend; +} +*/ + +// ============================================================================ +// Resume / Suspend +// ============================================================================ + +s32 OSResumeThread(OSThread* thread) { + if (!thread) + return 0; + + s32 prevSuspend = thread->suspend; + if (thread->suspend > 0) { + thread->suspend--; + } + + // Only wake up if suspend count drops to 0 + if (thread->suspend == 0) { + PCThreadData* data = nullptr; + + // Lock the global map to safely retrieve our thread data pointer + { + std::lock_guard mapLock(GetThreadDataMutex()); + auto it = GetThreadDataMap().find(thread); + if (it != GetThreadDataMap().end()) { + data = it->second.get(); + } + } + + if (data) { + // Lock the specific thread mutex to safely modify state and notify + std::unique_lock threadLock(data->mtx); + + if (!data->started) { + // First resume: launch the native thread + data->started = true; + data->suspended = false; + + // Unlock before launching to avoid potential deadlocks in thread initialization + threadLock.unlock(); + + data->nativeThread = std::thread(ThreadEntryWrapper, thread, data); + data->nativeThread.detach(); + OSReport("[PC-OSThread] Started thread %p\n", thread); + } else { + // Resume from suspension: signal the condition variable + // IMPORTANT: Set suspended to false BEFORE notifying to pass the wait predicate + data->suspended = false; + data->cv.notify_all(); + } + } + } + + return prevSuspend; +} + +s32 OSSuspendThread(OSThread* thread) { + if (!thread) + return 0; + + s32 prevSuspend = thread->suspend; + thread->suspend++; + + // If transitioning from running (0) to suspended (1) + if (prevSuspend == 0) { + PCThreadData* data = nullptr; + + // Lock the global map to find our thread data + { + std::lock_guard mapLock(GetThreadDataMutex()); + auto it = GetThreadDataMap().find(thread); + if (it != GetThreadDataMap().end()) { + data = it->second.get(); + } + } + + if (data && data->started) { + std::unique_lock threadLock(data->mtx); + data->suspended = true; + + // FIX: If the thread is suspending ITSELF, we must block execution here. + // This replicates the GameCube behavior where OSSuspendThread yields the CPU + // immediately. + if (thread == OSGetCurrentThread()) { + // Block until 'suspended' becomes false (set by OSResumeThread) + // The predicate protects against spurious wakeups. + data->cv.wait(threadLock, [data] { return !data->suspended; }); + } else { + // NOTE: Suspending *other* threads is difficult in C++ std::thread + // without cooperative checkpoints or platform-specific hacks. + // For now, we only set the flag. The target thread would need to check 'suspended' + // periodically. + } + } + } + + return prevSuspend; +} + +// ============================================================================ +// Sleep / Wakeup (thread queue based) +// ============================================================================ + +void OSSleepThread(OSThreadQueue* queue) { + if (!queue) return; + + OSThread* currentThread = OSGetCurrentThread(); + if (!currentThread) return; + + currentThread->state = OS_THREAD_STATE_WAITING; + currentThread->queue = queue; + + // Enqueue into the thread queue + OSThread* prev = queue->tail; + if (prev == nullptr) { + queue->head = currentThread; + } else { + prev->link.next = currentThread; + } + currentThread->link.prev = prev; + currentThread->link.next = nullptr; + queue->tail = currentThread; + + // Wait on the condition variable for this queue + std::condition_variable& cv = GetQueueCV(queue); + std::unique_lock lock(GetQueueCvMutex()); + cv.wait(lock, [currentThread]() { + return currentThread->state != OS_THREAD_STATE_WAITING; + }); +} + +void OSWakeupThread(OSThreadQueue* queue) { + if (!queue) return; + + // Wake all threads in the queue + OSThread* thread = queue->head; + while (thread) { + OSThread* next = thread->link.next; + thread->state = OS_THREAD_STATE_READY; + thread->link.next = nullptr; + thread->link.prev = nullptr; + thread->queue = nullptr; + thread = next; + } + queue->head = queue->tail = nullptr; + + // Notify all waiters + std::condition_variable& cv = GetQueueCV(queue); + cv.notify_all(); +} + +// ============================================================================ +// Exit / Cancel / Detach / Join +// ============================================================================ + +void OSExitThread(void* val) { + OSThread* currentThread = OSGetCurrentThread(); + if (!currentThread) return; + + currentThread->val = val; + + if (currentThread->attr & OS_THREAD_ATTR_DETACH) { + DequeueActive(currentThread); + currentThread->state = 0; + } else { + currentThread->state = OS_THREAD_STATE_MORIBUND; + } + + // Wake anyone waiting to join + OSWakeupThread(¤tThread->queueJoin); + sActiveThreadCount--; +} + +void OSCancelThread(OSThread* thread) { + if (!thread) return; + + if (thread->attr & OS_THREAD_ATTR_DETACH) { + DequeueActive(thread); + thread->state = 0; + } else { + thread->state = OS_THREAD_STATE_MORIBUND; + } + + OSWakeupThread(&thread->queueJoin); + sActiveThreadCount--; +} + +void OSDetachThread(OSThread* thread) { + if (!thread) return; + thread->attr |= OS_THREAD_ATTR_DETACH; + + if (thread->state == OS_THREAD_STATE_MORIBUND) { + DequeueActive(thread); + thread->state = 0; + } + OSWakeupThread(&thread->queueJoin); +} + +int OSJoinThread(OSThread* thread, void* val) { + if (!thread) return 0; + + if (!(thread->attr & OS_THREAD_ATTR_DETACH) && + thread->state != OS_THREAD_STATE_MORIBUND && + thread->queueJoin.head == nullptr) { + OSSleepThread(&thread->queueJoin); + } + + if (thread->state == OS_THREAD_STATE_MORIBUND) { + if (val) { + *(s32*)val = (s32)(intptr_t)thread->val; + } + DequeueActive(thread); + thread->state = 0; + return 1; + } + return 0; +} + +// ============================================================================ +// Yield / Terminated / Active +// ============================================================================ + +void OSYieldThread(void) { + std::this_thread::yield(); +} + +BOOL OSIsThreadSuspended(OSThread* thread) { + return (thread && thread->suspend > 0) ? TRUE : FALSE; +} + +BOOL OSIsThreadTerminated(OSThread* thread) { + if (!thread) return TRUE; + return (thread->state == OS_THREAD_STATE_MORIBUND || thread->state == 0) ? TRUE : FALSE; +} + +s32 OSCheckActiveThreads(void) { + return sActiveThreadCount.load(); +} + +// ============================================================================ +// Priority +// ============================================================================ + +int OSSetThreadPriority(OSThread* thread, OSPriority priority) { + if (!thread) return 0; + if (priority < OS_PRIORITY_MIN || priority > OS_PRIORITY_MAX) return 0; + thread->base = priority; + thread->priority = priority; + return 1; +} + +s32 OSGetThreadPriority(OSThread* thread) { + if (!thread) return 16; + return thread->base; +} + +// ============================================================================ +// Switch Thread Callback +// ============================================================================ + +OSSwitchThreadCallback OSSetSwitchThreadCallback(OSSwitchThreadCallback callback) { + OSSwitchThreadCallback prev = sSwitchThreadCallback; + sSwitchThreadCallback = callback; + return prev; +} + +// ============================================================================ +// Scheduler (atomic counter, no real effect with native OS threads) +// ============================================================================ + +s32 OSDisableScheduler(void) { + return sSchedulerSuspendCount.fetch_add(1); +} + +s32 OSEnableScheduler(void) { + return sSchedulerSuspendCount.fetch_sub(1); +} + +// ============================================================================ +// Interrupts (global recursive mutex for mutual exclusion) +// ============================================================================ + +BOOL OSDisableInterrupts(void) { + GetInterruptMutex().lock(); + sInterruptLockCount++; + return (BOOL)(sInterruptLockCount > 1); // TRUE if was already locked +} + +BOOL OSRestoreInterrupts(BOOL level) { + if (sInterruptLockCount > 0) { + sInterruptLockCount--; + GetInterruptMutex().unlock(); + } + return level; +} + +BOOL OSEnableInterrupts(void) { + if (sInterruptLockCount > 0) { + sInterruptLockCount--; + GetInterruptMutex().unlock(); + } + return FALSE; +} + +// ============================================================================ +// Idle function (stub on PC) +// ============================================================================ + +OSThread* OSSetIdleFunction(OSIdleFunction idleFunction, void* param, void* stack, u32 stackSize) { + return nullptr; +} + +OSThread* OSGetIdleFunction(void) { + return nullptr; +} + +// ============================================================================ +// Thread-specific storage +// ============================================================================ + +void OSSetThreadSpecific(s32 index, void* ptr) { + OSThread* thread = OSGetCurrentThread(); + if (thread && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) { + thread->specific[index] = ptr; + } +} + +void* OSGetThreadSpecific(s32 index) { + OSThread* thread = OSGetCurrentThread(); + if (thread && index >= 0 && index < OS_THREAD_SPECIFIC_MAX) { + return thread->specific[index]; + } + return nullptr; +} + +// ============================================================================ +// Clear stack (minimal implementation) +// ============================================================================ + +void OSClearStack(u8 val) { + // On PC we don't clear the stack - it's managed by the OS +} + +// ============================================================================ +// Internal functions used by OSMutex +// ============================================================================ + +s32 __OSGetEffectivePriority(OSThread* thread) { + // On PC with native threads, priority inversion handling is simplified. + // Just return the base priority. + return thread ? thread->base : 16; +} + +void __OSPromoteThread(OSThread* thread, s32 priority) { + // Simplified: no real priority inheritance on PC + if (thread && priority < thread->priority) { + thread->priority = priority; + } +} + +void __OSReschedule(void) { + // With native OS threads, rescheduling is handled by the OS. + // Nothing to do here. +} + +// ============================================================================ +// Interrupt handler registration (stub) +// ============================================================================ + +__OSInterruptHandler __OSSetInterruptHandler(__OSInterrupt interrupt, + __OSInterruptHandler handler) { + return nullptr; +} + +OSInterruptMask __OSUnmaskInterrupts(OSInterruptMask mask) { + return 0; +} + +#ifdef __cplusplus +} +#endif