├── Module.manifest ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── kotlin │ │ ├── allegrex │ │ │ ├── agent │ │ │ │ └── ppsspp │ │ │ │ │ ├── client │ │ │ │ │ ├── model │ │ │ │ │ │ ├── PpssppModelKey.kt │ │ │ │ │ │ ├── PpssppException.kt │ │ │ │ │ │ ├── PpssppState.kt │ │ │ │ │ │ ├── PpssppCpuBreakpointMeta.kt │ │ │ │ │ │ ├── PpssppHleThreadMeta.kt │ │ │ │ │ │ ├── event │ │ │ │ │ │ │ ├── PpssppEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuResumeEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuSteppingEvent.kt │ │ │ │ │ │ │ ├── PpssppMemoryWriteEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointAddEvent.kt │ │ │ │ │ │ │ ├── PpssppMemoryReadEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointRemoveEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointUpdateEvent.kt │ │ │ │ │ │ │ ├── PpssppMemoryBreakpointAddEvent.kt │ │ │ │ │ │ │ ├── PpssppErrorEvent.kt │ │ │ │ │ │ │ ├── PpssppMemoryBreakpointRemoveEvent.kt │ │ │ │ │ │ │ ├── PpssppMemoryBreakpointUpdateEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuEvaluateEvent.kt │ │ │ │ │ │ │ ├── PpssppGamePauseEvent.kt │ │ │ │ │ │ │ ├── PpssppGameQuitEvent.kt │ │ │ │ │ │ │ ├── PpssppGameStartEvent.kt │ │ │ │ │ │ │ ├── PpssppGameResumeEvent.kt │ │ │ │ │ │ │ ├── PpssppSetRegisterEvent.kt │ │ │ │ │ │ │ ├── PpssppHleBacktraceEvent.kt │ │ │ │ │ │ │ ├── PpssppHleModuleListEvent.kt │ │ │ │ │ │ │ ├── PpssppHleThreadsListEvent.kt │ │ │ │ │ │ │ ├── PpssppMemoryMappingEvent.kt │ │ │ │ │ │ │ ├── PpssppHleFunctionListEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointListEvent.kt │ │ │ │ │ │ │ ├── PpssppMemoryBreakpointListEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuStatusEvent.kt │ │ │ │ │ │ │ ├── PpssppGameStatusEvent.kt │ │ │ │ │ │ │ ├── PpssppLogEvent.kt │ │ │ │ │ │ │ ├── PpssppCpuRegistersEvent.kt │ │ │ │ │ │ │ └── PpssppEventMap.kt │ │ │ │ │ │ ├── PpssppGameStatus.kt │ │ │ │ │ │ ├── PpssppGame.kt │ │ │ │ │ │ ├── request │ │ │ │ │ │ │ ├── PpssppRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuResumeRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuStatusRequest.kt │ │ │ │ │ │ │ ├── PpssppGameStatusRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuSteppingRequest.kt │ │ │ │ │ │ │ ├── PpssppHleModuleListRequest.kt │ │ │ │ │ │ │ ├── PpssppMemoryMappingRequest.kt │ │ │ │ │ │ │ ├── PpssppHleThreadsListRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuStepOutRequest.kt │ │ │ │ │ │ │ ├── PpssppHleFunctionListRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointListRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuStepIntoRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuStepOverRequest.kt │ │ │ │ │ │ │ ├── PpssppHleBacktraceRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuGetRegistersRequest.kt │ │ │ │ │ │ │ ├── PpssppMemoryBreakpointListRequest.kt │ │ │ │ │ │ │ ├── PpssppMemoryWriteRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointRemoveRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuEvaluateRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointUpdateRequest.kt │ │ │ │ │ │ │ ├── PpssppMemoryReadRequest.kt │ │ │ │ │ │ │ ├── PpssppMemoryBreakpointRemoveRequest.kt │ │ │ │ │ │ │ ├── PpssppSetRegisterRequest.kt │ │ │ │ │ │ │ ├── PpssppMemoryBreakpointUpdateRequest.kt │ │ │ │ │ │ │ ├── PpssppCpuBreakpointAddRequest.kt │ │ │ │ │ │ │ └── PpssppMemoryBreakpointAddRequest.kt │ │ │ │ │ │ ├── PpssppMemoryBreakpointMeta.kt │ │ │ │ │ │ ├── PpssppHleFunction.kt │ │ │ │ │ │ ├── PpssppCpuStatus.kt │ │ │ │ │ │ ├── PpssppHleModuleMeta.kt │ │ │ │ │ │ ├── PpssppCpuEvaluatedExpression.kt │ │ │ │ │ │ ├── PpssppStackFrame.kt │ │ │ │ │ │ ├── PpssppInstance.kt │ │ │ │ │ │ ├── PpssppMemoryRange.kt │ │ │ │ │ │ ├── PpssppCpuRegisterMeta.kt │ │ │ │ │ │ ├── PpssppCpuBreakpoint.kt │ │ │ │ │ │ ├── PpssppHleModule.kt │ │ │ │ │ │ ├── PpssppCpuRegister.kt │ │ │ │ │ │ ├── PpssppMemoryBreakpoint.kt │ │ │ │ │ │ ├── PpssppHleThread.kt │ │ │ │ │ │ ├── PpssppCpuRegistersCategory.kt │ │ │ │ │ │ └── PpssppLogMessage.kt │ │ │ │ │ ├── PpssppEventListener.kt │ │ │ │ │ ├── PpssppClient.kt │ │ │ │ │ └── websocket │ │ │ │ │ │ ├── PpssppWsEventDispatcher.kt │ │ │ │ │ │ └── PpssppWsClient.kt │ │ │ │ │ ├── model │ │ │ │ │ ├── UpdateReason.kt │ │ │ │ │ ├── PpssppTargetObject.kt │ │ │ │ │ ├── PpssppModelTargetEnvironment.kt │ │ │ │ │ ├── PpssppModelTargetStackFrame.kt │ │ │ │ │ ├── PpssppModelTargetSymbol.kt │ │ │ │ │ ├── PpssppModelTargetStack.kt │ │ │ │ │ ├── PpssppModelTargetModule.kt │ │ │ │ │ ├── PpssppModelTargetSymbolContainer.kt │ │ │ │ │ ├── PpssppModelTargetModuleContainer.kt │ │ │ │ │ ├── PpssppModelTargetRegister.kt │ │ │ │ │ ├── PpssppModelTargetMemoryRegion.kt │ │ │ │ │ ├── PpssppModelTargetThreadContainer.kt │ │ │ │ │ ├── PpssppModelTargetProcessMemory.kt │ │ │ │ │ ├── PpssppModelTargetThread.kt │ │ │ │ │ ├── PpssppModelTargetCpuBreakpoint.kt │ │ │ │ │ ├── PpssppModelTargetMemoryBreakpoint.kt │ │ │ │ │ ├── PpssppModelTargetBreakpointContainer.kt │ │ │ │ │ ├── PpssppModelTargetSession.kt │ │ │ │ │ ├── PpssppModelTargetRegisterContainerAndBank.kt │ │ │ │ │ ├── PpssppDebuggerObjectModel.kt │ │ │ │ │ └── PpssppModelTargetProcess.kt │ │ │ │ │ ├── util │ │ │ │ │ └── CoroutineUtil.kt │ │ │ │ │ ├── PpssppWsDebuggerModelFactory.kt │ │ │ │ │ └── platform │ │ │ │ │ └── PpssppDebuggerMappingOpinion.kt │ │ │ ├── pcode │ │ │ │ ├── TempVarnodeProvider.kt │ │ │ │ ├── VectorMapper.kt │ │ │ │ ├── MatrixMapper.kt │ │ │ │ ├── InjectVfpuWriteVector.kt │ │ │ │ ├── InjectVfpuLoadQ.kt │ │ │ │ ├── InjectVfpuLoadQPart.kt │ │ │ │ ├── InjectVfpuSaveQPart.kt │ │ │ │ ├── InjectVfpuWriteMatrixRow.kt │ │ │ │ ├── InjectVfpuReadVector.kt │ │ │ │ ├── InjectVfpuReadMatrix.kt │ │ │ │ ├── PcodeOpEmitter.kt │ │ │ │ ├── AllegrexPcodeInjectLibrary.kt │ │ │ │ └── VfpuPcode.kt │ │ │ ├── format │ │ │ │ └── elf │ │ │ │ │ └── relocation │ │ │ │ │ ├── AllegrexRelocationFixupHandler.kt │ │ │ │ │ ├── AllegrexRelocation.kt │ │ │ │ │ └── AllegrexRelocationApplicator.kt │ │ │ └── MipsInstructionStasher.kt │ │ └── ghidra │ │ │ ├── program │ │ │ └── emulation │ │ │ │ └── AllegrexEmulateInstructionStateModifier.kt │ │ │ └── app │ │ │ └── util │ │ │ ├── bin │ │ │ └── format │ │ │ │ └── elf │ │ │ │ ├── PspElfHeader.kt │ │ │ │ ├── relocation │ │ │ │ ├── AllegrexElfRelocationConstants.kt │ │ │ │ └── AllegrexElfRelocationExtension.kt │ │ │ │ └── PspElfConstants.kt │ │ │ └── opinion │ │ │ └── PspElfLoader.kt │ └── resources │ │ └── log4j2-test.xml └── test │ └── kotlin │ └── allegrex │ └── agent │ └── ppsspp │ └── client │ └── websocket │ └── PpssppWsClientManualTest.kt ├── extension.properties ├── data ├── patterns │ ├── patternconstraints.xml │ └── Allegrex_patterns.xml └── languages │ ├── allegrex.slaspec │ ├── Allegrex.opinion │ ├── allegrex.ldefs │ ├── allegrex.pspec │ ├── allegrex.cspec │ └── allegrexFloat.sinc ├── .editorconfig ├── .gitignore ├── ghidra_scripts ├── PpssppImportSymFile.py └── PpssppExportSymFile.py ├── .github └── workflows │ ├── build.yml │ └── publish.yml ├── gradlew.bat ├── CHANGES.md ├── README.md └── gradlew /Module.manifest: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rndtrash/ghidra-allegrex/master/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppModelKey.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | interface PpssppModelKey 4 | -------------------------------------------------------------------------------- /extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=Ghidra processor module adding support for the Allegrex CPU (PSP) 3 | author=kotcrab 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppException.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | class PpssppException(message: String) : Exception(message) 4 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppState.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | enum class PpssppState { 4 | RUNNING, STEPPING, NO_GAME, EXITED 5 | } 6 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppCpuBreakpointMeta.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppCpuBreakpointMeta( 4 | val address: Long 5 | ) 6 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppHleThreadMeta.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppHleThreadMeta( 4 | val id: Long 5 | ) : PpssppModelKey 6 | -------------------------------------------------------------------------------- /data/patterns/patternconstraints.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Allegrex_patterns.xml 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | interface PpssppEvent { 4 | val ticket: String? 5 | val event: String 6 | } 7 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppGameStatus.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppGameStatus( 4 | val game: PpssppGame?, 5 | val paused: Boolean 6 | ) 7 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppGame.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppGame( 4 | val id: String, 5 | val version: String, 6 | val title: String, 7 | ) 8 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | interface PpssppRequest { 4 | val ticket: String? 5 | val event: String 6 | } 7 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppMemoryBreakpointMeta.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppMemoryBreakpointMeta( 4 | val address: Long, 5 | val size: Long 6 | ) 7 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppHleFunction.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppHleFunction( 4 | val name: String, 5 | val address: Long, 6 | val size: Long, 7 | ) 8 | -------------------------------------------------------------------------------- /data/languages/allegrex.slaspec: -------------------------------------------------------------------------------- 1 | @include "allegrex.sinc" 2 | @include "allegrexInstructions.sinc" 3 | @include "allegrexFloat.sinc" 4 | @include "allegrexVfpuBase.sinc" 5 | @include "allegrexVfpuGenerated.sinc" 6 | @include "allegrexVfpu.sinc" 7 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppCpuStatus.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppCpuStatus( 4 | val stepping: Boolean, 5 | val paused: Boolean, 6 | val pc: Long, 7 | val ticks: Long, 8 | ) 9 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppHleModuleMeta.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppHleModuleMeta( 4 | val name: String, 5 | val address: Long, 6 | val size: Long 7 | ) : PpssppModelKey 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppCpuEvaluatedExpression.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppCpuEvaluatedExpression( 4 | val expression: String, 5 | val uintValue: Long, 6 | val floatValue: String, 7 | ) 8 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppStackFrame.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppStackFrame( 4 | val entry: Long, 5 | val pc: Long, 6 | val sp: Long, 7 | val stackSize: Long, 8 | val code: String 9 | ) 10 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppInstance.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class PpssppInstance( 6 | val ip: String, 7 | @SerializedName("p") 8 | val port: Int 9 | ) 10 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppMemoryRange.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppMemoryRange( 4 | val type: String, 5 | val subtype: String, 6 | val name: String, 7 | val address: Long, 8 | val size: Long 9 | ) : PpssppModelKey 10 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppCpuRegisterMeta.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppCpuRegisterMeta( 4 | val threadId: Long, 5 | val categoryId: Int, 6 | val id: Int, 7 | val name: String, 8 | val bitLength: Int = 32 9 | ) : PpssppModelKey 10 | -------------------------------------------------------------------------------- /data/languages/Allegrex.opinion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuResumeEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppCpuResumeEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "cpu.resume" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuSteppingEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppCpuSteppingEvent( 4 | override val ticket: String?, 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "cpu.stepping" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppMemoryWriteEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppMemoryWriteEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "memory.write" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | max_line_length = 150 10 | end_of_line = lf 11 | 12 | [{*.sinc, *.cspec, *.ldefs, *.opinion, *.pspec}] 13 | indent_size = 4 14 | 15 | [*.md] 16 | max_line_length = off 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuBreakpointAddEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppCpuBreakpointAddEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "cpu.breakpoint.add" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppMemoryReadEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppMemoryReadEvent( 4 | val base64: String, 5 | override val ticket: String? 6 | ) : PpssppEvent { 7 | companion object { 8 | const val EVENT_NAME = "memory.read" 9 | } 10 | 11 | override val event: String = EVENT_NAME 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuBreakpointRemoveEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppCpuBreakpointRemoveEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "cpu.breakpoint.remove" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuBreakpointUpdateEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppCpuBreakpointUpdateEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "cpu.breakpoint.update" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppMemoryBreakpointAddEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppMemoryBreakpointAddEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "memory.breakpoint.add" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppErrorEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppErrorEvent( 4 | val message: String, 5 | val level: Int, 6 | override val ticket: String?, 7 | ) : PpssppEvent { 8 | companion object { 9 | const val EVENT_NAME = "error" 10 | } 11 | 12 | override val event: String = EVENT_NAME 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppMemoryBreakpointRemoveEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppMemoryBreakpointRemoveEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "memory.breakpoint.remove" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppMemoryBreakpointUpdateEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppMemoryBreakpointUpdateEvent( 4 | override val ticket: String? 5 | ) : PpssppEvent { 6 | companion object { 7 | const val EVENT_NAME = "memory.breakpoint.update" 8 | } 9 | 10 | override val event: String = EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppCpuBreakpoint.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppCpuBreakpoint( 4 | val address: Long, 5 | val enabled: Boolean, 6 | val log: Boolean, 7 | val condition: String?, 8 | val logFormat: String?, 9 | val symbol: String?, 10 | val code: String 11 | ) { 12 | fun meta() = PpssppCpuBreakpointMeta(address) 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppHleModule.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class PpssppHleModule( 6 | val name: String, 7 | val address: Long, 8 | val size: Long, 9 | @SerializedName("isActive") 10 | val active: Boolean, 11 | ) { 12 | fun meta() = PpssppHleModuleMeta(name, address, size) 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/PpssppEventListener.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppLogMessage 4 | import allegrex.agent.ppsspp.client.model.PpssppState 5 | 6 | interface PpssppEventListener { 7 | fun onStateChange(state: PpssppState, paused: Boolean) 8 | 9 | fun onStepCompleted() 10 | 11 | fun onLog(message: PpssppLogMessage) 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuEvaluateEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppCpuEvaluateEvent( 4 | val uintValue: Long, 5 | val floatValue: String, 6 | override val ticket: String? 7 | ) : PpssppEvent { 8 | companion object { 9 | const val EVENT_NAME = "cpu.evaluate" 10 | } 11 | 12 | override val event: String = EVENT_NAME 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppCpuRegister.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppCpuRegister( 4 | val threadId: Long, 5 | val categoryId: Int, 6 | val id: Int, 7 | val name: String, 8 | val uintValue: Long, 9 | val floatValue: String, 10 | val bitLength: Int = 32, 11 | ) { 12 | fun meta() = PpssppCpuRegisterMeta(threadId, categoryId, id, name, bitLength) 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/ghidra/program/emulation/AllegrexEmulateInstructionStateModifier.kt: -------------------------------------------------------------------------------- 1 | package ghidra.program.emulation 2 | 3 | import ghidra.pcode.emulate.Emulate 4 | import ghidra.pcode.emulate.EmulateInstructionStateModifier 5 | 6 | class AllegrexEmulateInstructionStateModifier(emu: Emulate) : EmulateInstructionStateModifier(emu) { 7 | init { 8 | // Refer to allegrex.sinc for additional PcodeOp that can be implemented 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuResumeRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuResumeEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuResumeRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppCpuResumeEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuStatusRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuStatusEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuStatusRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppCpuStatusEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppGameStatusRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppGameStatusEvent 4 | import java.util.UUID 5 | 6 | data class PpssppGameStatusRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppGameStatusEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppGamePauseEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppGame 4 | 5 | data class PpssppGamePauseEvent( 6 | val game: PpssppGame?, 7 | override val ticket: String?, 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "game.pause" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppGameQuitEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppGame 4 | 5 | data class PpssppGameQuitEvent( 6 | val game: PpssppGame?, 7 | override val ticket: String?, 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "game.quit" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppGameStartEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppGame 4 | 5 | data class PpssppGameStartEvent( 6 | val game: PpssppGame?, 7 | override val ticket: String?, 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "game.start" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuSteppingRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuSteppingEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuSteppingRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppCpuSteppingEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppGameResumeEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppGame 4 | 5 | data class PpssppGameResumeEvent( 6 | val game: PpssppGame?, 7 | override val ticket: String?, 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "game.resume" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppHleModuleListRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppHleModuleListEvent 4 | import java.util.UUID 5 | 6 | data class PpssppHleModuleListRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppHleModuleListEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppMemoryMappingRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppMemoryMappingEvent 4 | import java.util.UUID 5 | 6 | data class PpssppMemoryMappingRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppMemoryMappingEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppHleThreadsListRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppHleThreadsListEvent 4 | import java.util.UUID 5 | 6 | data class PpssppHleThreadsListRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppHleThreadsListEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuStepOutRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import java.util.UUID 4 | 5 | data class PpssppCpuStepOutRequest( 6 | val thread: Long, 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | companion object { 10 | private const val EVENT_NAME = "cpu.stepOut" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppHleFunctionListRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppHleFunctionListEvent 4 | import java.util.UUID 5 | 6 | data class PpssppHleFunctionListRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppHleFunctionListEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppMemoryBreakpoint.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppMemoryBreakpoint( 4 | val address: Long, 5 | val size: Long, 6 | val enabled: Boolean, 7 | val log: Boolean, 8 | val read: Boolean, 9 | val write: Boolean, 10 | val change: Boolean, 11 | val logFormat: String?, 12 | val symbol: String? 13 | ) { 14 | fun meta() = PpssppMemoryBreakpointMeta(address, size) 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuBreakpointListRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuBreakpointListEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuBreakpointListRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppCpuBreakpointListEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuStepIntoRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import java.util.UUID 4 | 5 | data class PpssppCpuStepIntoRequest( 6 | val thread: Long, 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | companion object { 10 | private const val EVENT_NAME = "cpu.stepInto" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuStepOverRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import java.util.UUID 4 | 5 | data class PpssppCpuStepOverRequest( 6 | val thread: Long, 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | companion object { 10 | private const val EVENT_NAME = "cpu.stepOver" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppSetRegisterEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | data class PpssppSetRegisterEvent( 4 | val category: Int, 5 | val register: Int, 6 | val uintValue: Long, 7 | val floatValue: String, 8 | override val ticket: String?, 9 | ) : PpssppEvent { 10 | companion object { 11 | const val EVENT_NAME = "cpu.setReg" 12 | } 13 | 14 | override val event: String = EVENT_NAME 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppHleBacktraceRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppHleBacktraceEvent 4 | import java.util.UUID 5 | 6 | data class PpssppHleBacktraceRequest( 7 | val thread: Long, 8 | override val ticket: String = UUID.randomUUID().toString(), 9 | ) : PpssppRequest { 10 | override val event: String = PpssppHleBacktraceEvent.EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppHleBacktraceEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppStackFrame 4 | 5 | data class PpssppHleBacktraceEvent( 6 | val frames: List, 7 | override val ticket: String? 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "hle.backtrace" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppHleModuleListEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleModule 4 | 5 | data class PpssppHleModuleListEvent( 6 | val modules: List, 7 | override val ticket: String? 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "hle.module.list" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppHleThreadsListEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleThread 4 | 5 | data class PpssppHleThreadsListEvent( 6 | val threads: List, 7 | override val ticket: String? 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "hle.thread.list" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppMemoryMappingEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppMemoryRange 4 | 5 | data class PpssppMemoryMappingEvent( 6 | val ranges: List, 7 | override val ticket: String? 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "memory.mapping" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuGetRegistersRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuRegistersEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuGetRegistersRequest( 7 | val thread: Long, 8 | override val ticket: String = UUID.randomUUID().toString(), 9 | ) : PpssppRequest { 10 | override val event: String = PpssppCpuRegistersEvent.EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppMemoryBreakpointListRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppMemoryBreakpointListEvent 4 | import java.util.UUID 5 | 6 | data class PpssppMemoryBreakpointListRequest( 7 | override val ticket: String = UUID.randomUUID().toString(), 8 | ) : PpssppRequest { 9 | override val event: String = PpssppMemoryBreakpointListEvent.EVENT_NAME 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppHleFunctionListEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleFunction 4 | 5 | data class PpssppHleFunctionListEvent( 6 | val functions: List, 7 | override val ticket: String? 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "hle.func.list" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/TempVarnodeProvider.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.program.model.pcode.Varnode 4 | 5 | class TempVarnodeProvider( 6 | private val pCode: PcodeOpEmitter, 7 | initialSize: Int = 8, 8 | private val growSize: Int = 4 9 | ) { 10 | var currentSize = initialSize 11 | 12 | fun nextVarnode(): Varnode { 13 | val vn = pCode.getOrDefineVarnode("tmp$currentSize", currentSize) 14 | currentSize += growSize 15 | return vn 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/kotlin/ghidra/app/util/bin/format/elf/PspElfHeader.kt: -------------------------------------------------------------------------------- 1 | package ghidra.app.util.bin.format.elf 2 | 3 | import ghidra.app.util.bin.ByteProvider 4 | import java.util.function.Consumer 5 | 6 | open class PspElfHeader( 7 | provider: ByteProvider, 8 | val useRebootBinTypeBMapping: Boolean, 9 | errorConsumer: Consumer, 10 | ) : ElfHeader(provider, errorConsumer) { 11 | 12 | override fun e_machine(): Short { 13 | return PspElfConstants.EM_MIPS_PSP_HACK 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/resources/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppHleThread.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppHleThread( 4 | val id: Long, 5 | val name: String, 6 | val status: Int, 7 | val statuses: List, 8 | val pc: Long, 9 | val entry: Long, 10 | val initialStackSize: Long, 11 | val currentStackSize: Long, 12 | val priority: Int, 13 | val waitType: Int, 14 | val isCurrent: Boolean 15 | ) { 16 | fun meta() = PpssppHleThreadMeta(id) 17 | } 18 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppMemoryWriteRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppMemoryWriteEvent 4 | import java.util.UUID 5 | 6 | data class PpssppMemoryWriteRequest( 7 | val address: Long, 8 | val base64: String, 9 | override val ticket: String = UUID.randomUUID().toString(), 10 | ) : PpssppRequest { 11 | override val event: String = PpssppMemoryWriteEvent.EVENT_NAME 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuBreakpointListEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppCpuBreakpoint 4 | 5 | data class PpssppCpuBreakpointListEvent( 6 | val breakpoints: List, 7 | override val ticket: String? 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "cpu.breakpoint.list" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuBreakpointRemoveRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuBreakpointRemoveEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuBreakpointRemoveRequest( 7 | val address: Long, 8 | override val ticket: String = UUID.randomUUID().toString(), 9 | ) : PpssppRequest { 10 | override val event: String = PpssppCpuBreakpointRemoveEvent.EVENT_NAME 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuEvaluateRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuEvaluateEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuEvaluateRequest( 7 | val expression: String, 8 | val thread: Long? = null, 9 | override val ticket: String = UUID.randomUUID().toString(), 10 | ) : PpssppRequest { 11 | override val event: String = PpssppCpuEvaluateEvent.EVENT_NAME 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppMemoryBreakpointListEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppMemoryBreakpoint 4 | 5 | data class PpssppMemoryBreakpointListEvent( 6 | val breakpoints: List, 7 | override val ticket: String? 8 | ) : PpssppEvent { 9 | companion object { 10 | const val EVENT_NAME = "memory.breakpoint.list" 11 | } 12 | 13 | override val event: String = EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/VectorMapper.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | class VectorMapper( 4 | private val baseRegId: Int, 5 | transpose: Boolean, 6 | val size: Int, 7 | private val vfpuPcode: VfpuPcode 8 | ) { 9 | private val stride = if (transpose) 1 else 4 10 | val lastIndex = size - 1 11 | 12 | fun regNameAt(index: Int): String { 13 | return vfpuPcode.regIdToName(elementAt(index)) 14 | } 15 | 16 | private fun elementAt(index: Int): Int { 17 | return baseRegId + stride * index 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuBreakpointUpdateRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuBreakpointUpdateEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuBreakpointUpdateRequest( 7 | val address: Long, 8 | val enabled: Boolean, 9 | override val ticket: String = UUID.randomUUID().toString(), 10 | ) : PpssppRequest { 11 | override val event: String = PpssppCpuBreakpointUpdateEvent.EVENT_NAME 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppMemoryReadRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppMemoryReadEvent 4 | import java.util.UUID 5 | 6 | data class PpssppMemoryReadRequest( 7 | val address: Long, 8 | val size: Long, 9 | val replacements: Boolean = false, 10 | override val ticket: String = UUID.randomUUID().toString(), 11 | ) : PpssppRequest { 12 | override val event: String = PpssppMemoryReadEvent.EVENT_NAME 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppMemoryBreakpointRemoveRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppMemoryBreakpointRemoveEvent 4 | import java.util.UUID 5 | 6 | data class PpssppMemoryBreakpointRemoveRequest( 7 | val address: Long, 8 | val size: Long, 9 | override val ticket: String = UUID.randomUUID().toString(), 10 | ) : PpssppRequest { 11 | override val event: String = PpssppMemoryBreakpointRemoveEvent.EVENT_NAME 12 | } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppSetRegisterRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppSetRegisterEvent 4 | import java.util.UUID 5 | 6 | data class PpssppSetRegisterRequest( 7 | val thread: Long, 8 | val category: Int, 9 | val register: Int, 10 | val value: String, 11 | override val ticket: String = UUID.randomUUID().toString(), 12 | ) : PpssppRequest { 13 | override val event: String = PpssppSetRegisterEvent.EVENT_NAME 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/ghidra/app/util/bin/format/elf/relocation/AllegrexElfRelocationConstants.kt: -------------------------------------------------------------------------------- 1 | package ghidra.app.util.bin.format.elf.relocation 2 | 3 | object AllegrexElfRelocationConstants { 4 | const val R_MIPS_NONE = 0 5 | const val R_MIPS_16 = 1 6 | const val R_MIPS_32 = 2 7 | const val R_MIPS_26 = 4 8 | const val R_MIPS_HI16 = 5 9 | const val R_MIPS_LO16 = 6 10 | 11 | // Mapping for "new" type B relocations is rather arbitrary 12 | const val R_MIPS_X_HI16 = 13 13 | const val R_MIPS_X_J26 = 14 14 | const val R_MIPS_X_JAL26 = 15 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppMemoryBreakpointUpdateRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppMemoryBreakpointUpdateEvent 4 | import java.util.UUID 5 | 6 | data class PpssppMemoryBreakpointUpdateRequest( 7 | val address: Long, 8 | val size: Long, 9 | val enabled: Boolean, 10 | override val ticket: String = UUID.randomUUID().toString(), 11 | ) : PpssppRequest { 12 | override val event: String = PpssppMemoryBreakpointUpdateEvent.EVENT_NAME 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppCpuRegistersCategory.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppCpuRegistersCategory( 4 | val id: Int, 5 | val name: String, 6 | val registerNames: List, 7 | val uintValues: List, 8 | val floatValues: List 9 | ) { 10 | fun getRegisters(associateWithThreadId: Long): List { 11 | return registerNames.mapIndexed { index, name -> 12 | PpssppCpuRegister(associateWithThreadId, id, index, name, uintValues[index], floatValues[index]) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /data/languages/allegrex.ldefs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | Allegrex 32-bit processor, little endian 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppCpuBreakpointAddRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppCpuBreakpointAddEvent 4 | import java.util.UUID 5 | 6 | data class PpssppCpuBreakpointAddRequest( 7 | val address: Long, 8 | val enabled: Boolean, 9 | val log: Boolean, 10 | val condition: String?, 11 | val logFormat: String?, 12 | override val ticket: String = UUID.randomUUID().toString(), 13 | ) : PpssppRequest { 14 | override val event: String = PpssppCpuBreakpointAddEvent.EVENT_NAME 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/UpdateReason.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | object UpdateReason { 4 | const val INITIALIZED = "Initialized" 5 | const val REFRESHED = "Refreshed" 6 | const val PROCESS_CREATED = "Process created" 7 | const val PROCESS_EXITED = "Process exited" 8 | const val FOCUS_CHANGED = "Focus changed" 9 | const val RUNNING = "Running" 10 | const val STOPPED = "Stopped" 11 | const val EXECUTION_STATE_CHANGED = "Execution state change" 12 | const val STEP_COMPLETED = "Step completed" 13 | const val ENABLED_STATE_CHANGED = "Enabled state changed" 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sla 2 | *.zip 3 | ## Java 4 | 5 | *.class 6 | *.war 7 | *.ear 8 | hs_err_pid* 9 | 10 | ## IntelliJ IDEA 11 | 12 | .idea/ 13 | *.ipr 14 | *.iws 15 | *.iml 16 | out/ 17 | 18 | ## Eclipse 19 | 20 | .metadata 21 | tmp/ 22 | *.tmp 23 | *.bak 24 | *.swp 25 | *~.nib 26 | local.properties 27 | .settings/ 28 | .loadpath 29 | .pydevproject 30 | .project 31 | .classpath 32 | .externalToolBuilders/ 33 | *.launch 34 | 35 | ## NetBeans 36 | 37 | nbproject/private/ 38 | nbbuild/ 39 | dist/ 40 | nbdist/ 41 | nbactions.xml 42 | nb-configuration.xml 43 | 44 | ## Gradle 45 | 46 | .gradle 47 | build/ 48 | /target 49 | 50 | lib/ 51 | -------------------------------------------------------------------------------- /ghidra_scripts/PpssppImportSymFile.py: -------------------------------------------------------------------------------- 1 | # Imports function labels from PPSSPP .sym file. 2 | #@author Kotcrab 3 | #@category Data 4 | 5 | sym = askFile("Select PPSSPP .sym file", "Select") 6 | offsetBy = askInt("Offset addresses by", "Offset") 7 | skipZun = True 8 | makePrimary = True 9 | 10 | for line in file(sym.absolutePath): 11 | parts = line.split(" ") 12 | address = toAddr(long(parts[0], 16) + offsetBy) 13 | name = parts[1].rsplit(",", 1)[0] 14 | if skipZun and name.startswith("z_un_"): 15 | continue 16 | print "Create label", name, "at", address 17 | createLabel(address, name, makePrimary) 18 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/PpssppClient.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppEvent 4 | import allegrex.agent.ppsspp.client.model.request.PpssppRequest 5 | 6 | interface PpssppClient { 7 | suspend fun start() 8 | 9 | fun addEventListener(listener: PpssppEventListener) 10 | 11 | fun getBrief(): String 12 | 13 | fun close() 14 | 15 | fun isAlive(): Boolean 16 | 17 | suspend fun ping() 18 | 19 | suspend fun sendRequest(request: PpssppRequest) 20 | 21 | suspend fun sendRequestAndWait(request: PpssppRequest): T 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuStatusEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppCpuStatus 4 | 5 | data class PpssppCpuStatusEvent( 6 | val stepping: Boolean, 7 | val paused: Boolean, 8 | val pc: Long, 9 | val ticks: Long, 10 | override val ticket: String?, 11 | ) : PpssppEvent { 12 | companion object { 13 | const val EVENT_NAME = "cpu.status" 14 | } 15 | 16 | override val event: String = EVENT_NAME 17 | 18 | fun toCpuStatus(): PpssppCpuStatus { 19 | return PpssppCpuStatus(stepping, paused, pc, ticks) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/MatrixMapper.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | class MatrixMapper( 4 | private val baseRegId: Int, 5 | transpose: Boolean, 6 | val dimSize: Int, 7 | private val vfpuPcode: VfpuPcode 8 | ) { 9 | private val rowStride = if (transpose) 1 else 4 10 | private val columnStride = if (transpose) 4 else 1 11 | val lastDimIndex = dimSize - 1 12 | 13 | fun regNameAt(row: Int, column: Int): String { 14 | return vfpuPcode.regIdToName(elementAt(row, column)) 15 | } 16 | 17 | private fun elementAt(row: Int, column: Int): Int { 18 | return baseRegId + rowStride * row + columnStride * column 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppGameStatusEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppGame 4 | import allegrex.agent.ppsspp.client.model.PpssppGameStatus 5 | 6 | data class PpssppGameStatusEvent( 7 | val game: PpssppGame?, 8 | val paused: Boolean, 9 | override val ticket: String?, 10 | ) : PpssppEvent { 11 | companion object { 12 | const val EVENT_NAME = "game.status" 13 | } 14 | 15 | override val event: String = EVENT_NAME 16 | 17 | fun toGameStatus(): PpssppGameStatus { 18 | return PpssppGameStatus(game, paused) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppLogEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppLogMessage 4 | 5 | data class PpssppLogEvent( 6 | val timestamp: String, 7 | val header: String, 8 | val message: String, 9 | val level: Int, 10 | val channel: String, 11 | override val ticket: String? 12 | ) : PpssppEvent { 13 | companion object { 14 | const val EVENT_NAME = "log" 15 | } 16 | 17 | override val event: String = EVENT_NAME 18 | 19 | fun toLogMessage(): PpssppLogMessage { 20 | return PpssppLogMessage(timestamp, header, message, level, channel) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/util/CoroutineUtil.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.util 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.CoroutineStart 5 | import kotlinx.coroutines.future.future 6 | import java.util.concurrent.CompletableFuture 7 | import kotlin.coroutines.CoroutineContext 8 | import kotlin.coroutines.EmptyCoroutineContext 9 | 10 | fun CoroutineScope.futureVoid( 11 | context: CoroutineContext = EmptyCoroutineContext, 12 | start: CoroutineStart = CoroutineStart.DEFAULT, 13 | block: suspend CoroutineScope.() -> T 14 | ): CompletableFuture { 15 | return future(context, start, block).thenApply { null } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/PpssppLogMessage.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model 2 | 3 | data class PpssppLogMessage( 4 | val timestamp: String, 5 | val header: String, 6 | val message: String, 7 | val level: Int, 8 | val channel: String, 9 | ) { 10 | object LogLevel { 11 | const val NOTICE = 1 12 | const val ERROR = 2 13 | const val WARNING = 3 14 | const val INFO = 4 15 | const val DEBUG = 5 16 | const val VERBOSE = 6 17 | } 18 | 19 | fun isError(): Boolean { 20 | return level == LogLevel.ERROR 21 | } 22 | 23 | fun asFormattedMessage(): String { 24 | return "$timestamp $header $message" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/request/PpssppMemoryBreakpointAddRequest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.request 2 | 3 | import allegrex.agent.ppsspp.client.model.event.PpssppMemoryBreakpointAddEvent 4 | import java.util.UUID 5 | 6 | data class PpssppMemoryBreakpointAddRequest( 7 | val address: Long, 8 | val size: Long, 9 | val enabled: Boolean, 10 | val log: Boolean, 11 | val read: Boolean, 12 | val write: Boolean, 13 | val change: Boolean, 14 | val logFormat: String?, 15 | override val ticket: String = UUID.randomUUID().toString(), 16 | ) : PpssppRequest { 17 | override val event: String = PpssppMemoryBreakpointAddEvent.EVENT_NAME 18 | } 19 | -------------------------------------------------------------------------------- /data/languages/allegrex.pspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppTargetObject.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.PpssppApi 4 | import ghidra.dbg.agent.DefaultTargetObject 5 | import ghidra.dbg.target.TargetObject 6 | import kotlinx.coroutines.CoroutineScope 7 | 8 | abstract class PpssppTargetObject( 9 | private val ppssppModel: PpssppDebuggerObjectModel, parent: P, key: String, typeHint: String 10 | ) : DefaultTargetObject(ppssppModel, parent, key, typeHint) { 11 | val modelScope: CoroutineScope 12 | get() = ppssppModel.modelScope 13 | 14 | val api: PpssppApi 15 | get() = ppssppModel.api 16 | 17 | val session: PpssppModelTargetSession 18 | get() = ppssppModel.session 19 | 20 | override fun getModel(): PpssppDebuggerObjectModel { 21 | return ppssppModel 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ghidra_scripts/PpssppExportSymFile.py: -------------------------------------------------------------------------------- 1 | # Export function labels as a PPSSPP .sym file. 2 | #@author Kotcrab 3 | #@category Data 4 | 5 | def export(outSymPath, offsetBy): 6 | functions = currentProgram.getFunctionManager().getFunctions(True) 7 | with open(outSymPath, 'w') as outFile: 8 | for func in functions: 9 | off = func.getEntryPoint().offset + offsetBy 10 | name = func.getSignature().getName() 11 | size = func.getBody().getFirstRange().getLength() # not ideal but should cover most cases 12 | outFile.write("%08X %s,%04X\n" % (off, name, size)) 13 | 14 | sym = askFile("Select output PPSSPP .sym file", "Select") 15 | offsetBy = askInt("Offset addresses by", "Offset") 16 | 17 | if sym.exists(): 18 | overwrite = askYesNo("Warning", "File already exists, overwrite?") 19 | if overwrite: 20 | export(sym.absolutePath, offsetBy) 21 | else: 22 | export(sym.absolutePath, offsetBy) 23 | -------------------------------------------------------------------------------- /src/main/kotlin/ghidra/app/util/bin/format/elf/PspElfConstants.kt: -------------------------------------------------------------------------------- 1 | package ghidra.app.util.bin.format.elf 2 | 3 | @Suppress("MayBeConstant") 4 | object PspElfConstants { 5 | /** 6 | * This is a horrible hack. This arbitrary value is used as fake e_machine in ELF header. 7 | * Why? Basically when performing relocations the extension class loader mechanism can select built-in 8 | * MIPS_ElfRelocationHandler instead of AllegrexElfRelocationHandler. Both classes say then can relocate MIPS ELF. 9 | * This selection is done for every section and it involves converting set to a list, thus effectively choosing random 10 | * handler for each section. This will make MIPS_ElfRelocationHandler return false when checking for available 11 | * relocation handlers 12 | */ 13 | val EM_MIPS_PSP_HACK = 0x1337.toShort() 14 | 15 | val ET_PSP_PRX = 0xFFA0.toShort() 16 | 17 | val SHT_PSP_REL = 0x700000A0 18 | val SHT_PSP_REL_TYPE_B = 0x700000A1 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/ghidra/app/util/bin/format/elf/relocation/AllegrexElfRelocationExtension.kt: -------------------------------------------------------------------------------- 1 | package ghidra.app.util.bin.format.elf.relocation 2 | 3 | import ghidra.app.util.bin.format.elf.ElfRelocation 4 | import ghidra.program.model.data.CategoryPath 5 | import ghidra.program.model.data.DataType 6 | import ghidra.program.model.data.StructureDataType 7 | 8 | open class AllegrexElfRelocationExtension : ElfRelocation() { 9 | override fun getSymbolIndex(): Int { 10 | return 0 11 | } 12 | 13 | override fun getType(): Int { 14 | return (relocationInfo and 0xFF).toInt() 15 | } 16 | 17 | override fun toDataType(): DataType { 18 | val dtName = "Elf32_Allegrex_Rel" 19 | val struct = StructureDataType(CategoryPath("/ELF"), dtName, 0) 20 | struct.add(DWORD, "r_address", null) 21 | struct.add(BYTE, "r_type", null) 22 | struct.add(BYTE, "r_offsetIndex", null) 23 | struct.add(BYTE, "r_relocateToIndex", null) 24 | struct.add(BYTE, "r_unused", null) 25 | return struct 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Ghidra Allegrex 2 | 3 | on: [ push, pull_request, workflow_dispatch ] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | ghidra: [ '10.4' ] 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | - name: Setup Java 15 | uses: actions/setup-java@v3 16 | with: 17 | java-version: 17 18 | distribution: temurin 19 | cache: gradle 20 | - name: Setup Ghidra 21 | uses: er28-0652/setup-ghidra@0.0.6 22 | with: 23 | version: ${{ matrix.ghidra }} 24 | - name: Build Extension 25 | run: | 26 | chmod +x gradlew 27 | ./gradlew ghidraInstallThenPackage 28 | unzip ./build/dist/Allegrex.zip -d upload 29 | - name: Upload artifact 30 | uses: actions/upload-artifact@v3 31 | with: 32 | path: upload 33 | name: Allegrex-snapshot-for-Ghidra-${{ matrix.ghidra }} 34 | if-no-files-found: error 35 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/InjectVfpuWriteVector.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.app.plugin.processors.sleigh.SleighLanguage 4 | import ghidra.program.model.lang.InjectContext 5 | import ghidra.program.model.lang.InjectPayloadCallother 6 | import ghidra.program.model.listing.Program 7 | import ghidra.program.model.pcode.PcodeOp 8 | import ghidra.program.model.pcode.Varnode 9 | 10 | class InjectVfpuWriteVector( 11 | sourceName: String, 12 | private val language: SleighLanguage, 13 | private val uniqueBase: Long, 14 | private val maxUniqueBase: Long, 15 | private val createMapper: (VfpuPcode, Varnode) -> VectorMapper, 16 | private val vfpuPcode: VfpuPcode = DefaultVfpuPcode 17 | ) : InjectPayloadCallother(sourceName) { 18 | override fun getPcode(program: Program, con: InjectContext): Array? { 19 | var input = 0 20 | val baseReg = con.inputlist[input++] 21 | if (!baseReg.isRegister) { 22 | return null 23 | } 24 | 25 | val mapper = createMapper(vfpuPcode, baseReg) 26 | val pCode = PcodeOpEmitter(language, con.baseAddr, uniqueBase, maxUniqueBase) 27 | repeat(mapper.size) { i -> 28 | pCode.emitAssignVarnodeToRegister(mapper.regNameAt(i), con.inputlist[input++]) 29 | } 30 | return pCode.emittedOps() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/InjectVfpuLoadQ.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.app.plugin.processors.sleigh.SleighLanguage 4 | import ghidra.program.model.lang.InjectContext 5 | import ghidra.program.model.lang.InjectPayloadCallother 6 | import ghidra.program.model.listing.Program 7 | import ghidra.program.model.pcode.PcodeOp 8 | 9 | class InjectVfpuLoadQ( 10 | sourceName: String, 11 | private val language: SleighLanguage, 12 | private val uniqueBase: Long, 13 | private val maxUniqueBase: Long, 14 | private val vfpuPcode: VfpuPcode = DefaultVfpuPcode 15 | ) : InjectPayloadCallother(sourceName) { 16 | override fun getPcode(program: Program, con: InjectContext): Array? { 17 | var input = 0 18 | val baseReg = con.inputlist[input++] 19 | if (!baseReg.isRegister) { 20 | return null 21 | } 22 | val columnMode = con.inputlist[input++].offset == 0L 23 | 24 | val baseRegId = vfpuPcode.regVarnodeToRegId(baseReg) 25 | val mapper = VectorMapper(baseRegId, !columnMode, 4, vfpuPcode) 26 | 27 | val pCode = PcodeOpEmitter(language, con.baseAddr, uniqueBase, maxUniqueBase) 28 | repeat(mapper.size) { i -> 29 | pCode.emitAssignVarnodeToRegister(mapper.regNameAt(i), con.inputlist[input++]) 30 | } 31 | return pCode.emittedOps() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/InjectVfpuLoadQPart.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.app.plugin.processors.sleigh.SleighLanguage 4 | import ghidra.program.model.lang.InjectContext 5 | import ghidra.program.model.lang.InjectPayloadCallother 6 | import ghidra.program.model.listing.Program 7 | import ghidra.program.model.pcode.PcodeOp 8 | 9 | class InjectVfpuLoadQPart( 10 | sourceName: String, 11 | private val language: SleighLanguage, 12 | private val uniqueBase: Long, 13 | private val maxUniqueBase: Long, 14 | private val vfpuPcode: VfpuPcode = DefaultVfpuPcode 15 | ) : InjectPayloadCallother(sourceName) { 16 | override fun getPcode(program: Program, con: InjectContext): Array? { 17 | val baseReg = con.inputlist[0] 18 | if (!baseReg.isRegister) { 19 | return null 20 | } 21 | val columnMode = con.inputlist[1].offset == 0L 22 | val part = con.inputlist[2].offset.toInt() 23 | val value = con.inputlist[3] 24 | 25 | val baseRegId = vfpuPcode.regVarnodeToRegId(baseReg) 26 | val mapper = VectorMapper(baseRegId, !columnMode, 4, vfpuPcode) 27 | 28 | val pCode = PcodeOpEmitter(language, con.baseAddr, uniqueBase, maxUniqueBase) 29 | pCode.emitAssignVarnodeToRegister(mapper.regNameAt(part), value) 30 | return pCode.emittedOps() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/InjectVfpuSaveQPart.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.app.plugin.processors.sleigh.SleighLanguage 4 | import ghidra.program.model.lang.InjectContext 5 | import ghidra.program.model.lang.InjectPayloadCallother 6 | import ghidra.program.model.listing.Program 7 | import ghidra.program.model.pcode.PcodeOp 8 | 9 | class InjectVfpuSaveQPart( 10 | sourceName: String, 11 | private val language: SleighLanguage, 12 | private val uniqueBase: Long, 13 | private val maxUniqueBase: Long, 14 | private val vfpuPcode: VfpuPcode = DefaultVfpuPcode 15 | ) : InjectPayloadCallother(sourceName) { 16 | override fun getPcode(program: Program, con: InjectContext): Array? { 17 | val output = con.output[0] 18 | val baseReg = con.inputlist[0] 19 | if (!baseReg.isRegister) { 20 | return null 21 | } 22 | val columnMode = con.inputlist[1].offset == 0L 23 | val part = con.inputlist[2].offset.toInt() 24 | 25 | val baseRegId = vfpuPcode.regVarnodeToRegId(baseReg) 26 | val mapper = VectorMapper(baseRegId, !columnMode, 4, vfpuPcode) 27 | 28 | val pCode = PcodeOpEmitter(language, con.baseAddr, uniqueBase, maxUniqueBase) 29 | pCode.emitAssignRegisterToVarnode(output, mapper.regNameAt(part)) 30 | return pCode.emittedOps() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Ghidra Allegrex 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | ghidra: [ '10.4' ] 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | - name: Setup Java 18 | uses: actions/setup-java@v3 19 | with: 20 | java-version: 17 21 | distribution: temurin 22 | cache: gradle 23 | - name: Setup Ghidra 24 | uses: er28-0652/setup-ghidra@0.0.6 25 | with: 26 | version: ${{ matrix.ghidra }} 27 | - name: Build Extension 28 | run: | 29 | chmod +x gradlew 30 | ./gradlew ghidraInstallThenPackage 31 | - name: Upload artifact to release 32 | uses: svenstaro/upload-release-action@2.3.0 33 | with: 34 | repo_token: ${{ secrets.GITHUB_TOKEN }} 35 | release_name: ghidra-allegrex ${{ github.ref_name }} 36 | file: ./build/dist/Allegrex.zip 37 | asset_name: Allegrex-${{ github.ref_name }}-for-Ghidra-${{ matrix.ghidra }}.zip 38 | body: See [CHANGES](https://github.com/kotcrab/ghidra-allegrex/blob/master/CHANGES.md) file before updating. 39 | tag: ${{ github.ref }} 40 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/PpssppWsDebuggerModelFactory.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp 2 | 3 | import allegrex.agent.ppsspp.client.websocket.PpssppWsClient 4 | import allegrex.agent.ppsspp.model.PpssppDebuggerObjectModel 5 | import ghidra.dbg.DebuggerModelFactory 6 | import ghidra.dbg.DebuggerObjectModel 7 | import ghidra.dbg.util.ConfigurableFactory 8 | import java.util.concurrent.CompletableFuture 9 | 10 | @Suppress("unused") 11 | @ConfigurableFactory.FactoryDescription( 12 | brief = "PPSSPP WebSocket debugger (beta)", 13 | htmlDetails = """Connect to a running PPSSPP instance over WebSocket. 14 | Make sure "Allow remote debugger" is enabled in Developer tools. 15 | Note: This integration is in beta. Please report any issues 16 | to kotcrab/ghidra-allegrex GitHub repository""" 17 | ) 18 | class PpssppWsDebuggerModelFactory : DebuggerModelFactory { 19 | @JvmField 20 | @ConfigurableFactory.FactoryOption("Connection Address (empty to auto-detect)") 21 | val connectionUrlOption: ConfigurableFactory.Property = 22 | ConfigurableFactory.Property.fromAccessors(String::class.java, { connectionUrl }, { connectionUrl = it }) 23 | 24 | private var connectionUrl = "" 25 | 26 | override fun build(): CompletableFuture { 27 | val model = PpssppDebuggerObjectModel(PpssppWsClient(connectionUrl)) 28 | return model.start().thenApply { model } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/InjectVfpuWriteMatrixRow.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.app.plugin.processors.sleigh.SleighLanguage 4 | import ghidra.program.model.lang.InjectContext 5 | import ghidra.program.model.lang.InjectPayloadCallother 6 | import ghidra.program.model.listing.Program 7 | import ghidra.program.model.pcode.PcodeOp 8 | import ghidra.program.model.pcode.Varnode 9 | 10 | class InjectVfpuWriteMatrixRow( 11 | sourceName: String, 12 | private val language: SleighLanguage, 13 | private val uniqueBase: Long, 14 | private val maxUniqueBase: Long, 15 | private val createMapper: (VfpuPcode, Varnode, Boolean) -> MatrixMapper, 16 | private val vfpuPcode: VfpuPcode = DefaultVfpuPcode 17 | ) : InjectPayloadCallother(sourceName) { 18 | override fun getPcode(program: Program, con: InjectContext): Array? { 19 | var input = 0 20 | val baseReg = con.inputlist[input++] 21 | if (!baseReg.isRegister) { 22 | return null 23 | } 24 | val row = con.inputlist[input++].offset.toInt() 25 | 26 | val mapper = createMapper(vfpuPcode, baseReg, false) // write is always transpose = false 27 | val pCode = PcodeOpEmitter(language, con.baseAddr, uniqueBase, maxUniqueBase) 28 | repeat(mapper.dimSize) { i -> 29 | pCode.emitAssignVarnodeToRegister(mapper.regNameAt(row, i), con.inputlist[input++]) 30 | } 31 | return pCode.emittedOps() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetEnvironment.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import ghidra.dbg.target.TargetEnvironment 4 | import ghidra.dbg.target.TargetObject 5 | import ghidra.dbg.target.schema.TargetAttributeType 6 | import ghidra.dbg.target.schema.TargetElementType 7 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 8 | 9 | @TargetObjectSchemaInfo( 10 | name = PpssppModelTargetEnvironment.NAME, 11 | attributes = [TargetAttributeType(type = Void::class)], 12 | elements = [TargetElementType(type = Void::class)], 13 | ) 14 | class PpssppModelTargetEnvironment( 15 | process: PpssppModelTargetProcess 16 | ) : 17 | PpssppTargetObject( 18 | process.model, process, NAME, NAME 19 | ), 20 | TargetEnvironment { 21 | 22 | companion object { 23 | const val NAME = "Environment" 24 | 25 | const val ARCH = "Allegrex" 26 | const val DEBUGGER = "PPSSPP" 27 | const val OS = "default" 28 | const val ENDIAN = "Little" 29 | } 30 | 31 | init { 32 | changeAttributes( 33 | emptyList(), 34 | emptyList(), 35 | mapOf( 36 | TargetEnvironment.ARCH_ATTRIBUTE_NAME to ARCH, 37 | TargetEnvironment.DEBUGGER_ATTRIBUTE_NAME to DEBUGGER, 38 | TargetEnvironment.OS_ATTRIBUTE_NAME to OS, 39 | TargetEnvironment.ENDIAN_ATTRIBUTE_NAME to ENDIAN, 40 | ), 41 | UpdateReason.INITIALIZED 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetStackFrame.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppStackFrame 4 | import ghidra.dbg.target.TargetObject 5 | import ghidra.dbg.target.TargetStackFrame 6 | import ghidra.dbg.target.schema.TargetAttributeType 7 | import ghidra.dbg.target.schema.TargetElementType 8 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 9 | import ghidra.dbg.util.PathUtils 10 | 11 | @TargetObjectSchemaInfo( 12 | name = PpssppModelTargetStackFrame.NAME, 13 | elements = [TargetElementType(type = Void::class)], 14 | attributes = [TargetAttributeType(type = Void::class)] 15 | ) 16 | class PpssppModelTargetStackFrame( 17 | stack: PpssppModelTargetStack, 18 | private val level: Int 19 | ) : 20 | PpssppTargetObject( 21 | stack.model, stack, PathUtils.makeKey(PathUtils.makeIndex(level)), NAME 22 | ), 23 | TargetStackFrame { 24 | companion object { 25 | const val NAME = "StackFrame" 26 | } 27 | 28 | fun updateFrame(frame: PpssppStackFrame) { 29 | val pcAddr = getModel().addressFactory 30 | .defaultAddressSpace 31 | .getAddress(frame.pc.toString(16)) 32 | 33 | changeAttributes( 34 | emptyList(), 35 | mapOf( 36 | TargetStackFrame.PC_ATTRIBUTE_NAME to pcAddr, 37 | TargetStackFrame.DISPLAY_ATTRIBUTE_NAME to "#$level: ${frame.code}", 38 | ), 39 | UpdateReason.REFRESHED 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/platform/PpssppDebuggerMappingOpinion.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.platform 2 | 3 | import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOffer 4 | import ghidra.app.plugin.core.debug.mapping.DebuggerMappingOpinion 5 | import ghidra.app.plugin.core.debug.mapping.DefaultDebuggerMappingOffer 6 | import ghidra.dbg.target.TargetEnvironment 7 | import ghidra.dbg.target.TargetObject 8 | import ghidra.dbg.target.TargetProcess 9 | import ghidra.program.model.lang.CompilerSpecID 10 | import ghidra.program.model.lang.LanguageID 11 | 12 | @Suppress("unused") 13 | class PpssppDebuggerMappingOpinion : DebuggerMappingOpinion { 14 | companion object { 15 | private const val PPSSPP = "PPSSPP" 16 | private const val ALLEGREX = "Allegrex" 17 | private val LANG_ID_ALLEGREX_DEFAULT = LanguageID("$ALLEGREX:LE:32:default") 18 | private val COMP_ID_DEFAULT = CompilerSpecID("default") 19 | } 20 | 21 | private class PpssppAllegrexOffer(target: TargetObject) : DefaultDebuggerMappingOffer( 22 | target, 100, PPSSPP, LANG_ID_ALLEGREX_DEFAULT, COMP_ID_DEFAULT, emptySet() 23 | ) 24 | 25 | override fun offersForEnv(env: TargetEnvironment, target: TargetObject, includeOverrides: Boolean): Set { 26 | return when { 27 | env.debugger.equals(PPSSPP, ignoreCase = true) && env.architecture.equals(ALLEGREX, ignoreCase = true) -> { 28 | setOf(PpssppAllegrexOffer(target)) 29 | } 30 | else -> emptySet() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetSymbol.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleFunction 4 | import ghidra.dbg.target.TargetObject 5 | import ghidra.dbg.target.TargetSymbol 6 | import ghidra.dbg.target.schema.TargetAttributeType 7 | import ghidra.dbg.target.schema.TargetElementType 8 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 9 | import ghidra.dbg.util.PathUtils 10 | import ghidra.program.model.address.Address 11 | 12 | @TargetObjectSchemaInfo( 13 | name = PpssppModelTargetSymbol.NAME, 14 | elements = [TargetElementType(type = Void::class)], 15 | attributes = [TargetAttributeType(type = Void::class)] 16 | ) 17 | class PpssppModelTargetSymbol( 18 | namespace: PpssppModelTargetSymbolContainer, 19 | function: PpssppHleFunction 20 | ) : 21 | PpssppTargetObject( 22 | namespace.model, namespace, PathUtils.makeKey(PathUtils.makeIndex(function.address)), NAME 23 | ), 24 | TargetSymbol { 25 | 26 | companion object { 27 | const val NAME = "Symbol" 28 | } 29 | 30 | init { 31 | val address: Address = getModel().addressFactory 32 | .defaultAddressSpace 33 | .getAddress(function.address.toString(16)) 34 | 35 | changeAttributes( 36 | emptyList(), 37 | emptyList(), 38 | mapOf( 39 | TargetObject.DISPLAY_ATTRIBUTE_NAME to function.name, 40 | TargetObject.VALUE_ATTRIBUTE_NAME to address, 41 | TargetSymbol.SIZE_ATTRIBUTE_NAME to 4L, 42 | TargetSymbol.NAMESPACE_ATTRIBUTE_NAME to namespace, 43 | ), 44 | UpdateReason.INITIALIZED 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetStack.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.util.futureVoid 4 | import ghidra.dbg.DebuggerObjectModel 5 | import ghidra.dbg.target.TargetStack 6 | import ghidra.dbg.target.schema.TargetAttributeType 7 | import ghidra.dbg.target.schema.TargetObjectSchema 8 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 9 | 10 | @TargetObjectSchemaInfo( 11 | name = PpssppModelTargetStack.NAME, 12 | attributes = [TargetAttributeType(type = Void::class)], 13 | canonicalContainer = true, 14 | elementResync = TargetObjectSchema.ResyncMode.ONCE 15 | ) 16 | class PpssppModelTargetStack( 17 | thread: PpssppModelTargetThread, 18 | private val threadId: Long, 19 | ) : 20 | PpssppTargetObject( 21 | thread.model, thread, NAME, NAME 22 | ), 23 | TargetStack { 24 | 25 | companion object { 26 | const val NAME = "Stack" 27 | } 28 | 29 | private val targetFrames = mutableMapOf() 30 | 31 | override fun requestElements(refresh: DebuggerObjectModel.RefreshBehavior) = modelScope.futureVoid { 32 | val frames = api.backtraceThread(threadId) 33 | val newTargetFrames = frames 34 | .mapIndexed { level, frame -> 35 | getTargetFrame(level).apply { updateFrame(frame) } 36 | } 37 | setElements(newTargetFrames, UpdateReason.REFRESHED) 38 | } 39 | 40 | private fun getTargetFrame(level: Int): PpssppModelTargetStackFrame { 41 | return targetFrames.getOrPut(level) { PpssppModelTargetStackFrame(this, level) } 42 | } 43 | 44 | fun getFirstStackFrame(): PpssppModelTargetStackFrame? { 45 | return targetFrames[0] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetModule.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleModule 4 | import ghidra.dbg.target.TargetModule 5 | import ghidra.dbg.target.TargetObject 6 | import ghidra.dbg.target.schema.TargetAttributeType 7 | import ghidra.dbg.target.schema.TargetElementType 8 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 9 | import ghidra.dbg.util.PathUtils 10 | import ghidra.program.model.address.Address 11 | import ghidra.program.model.address.AddressRangeImpl 12 | 13 | @TargetObjectSchemaInfo( 14 | name = PpssppModelTargetModule.NAME, 15 | elements = [TargetElementType(type = Void::class)], 16 | attributes = [TargetAttributeType(type = Void::class)] 17 | ) 18 | class PpssppModelTargetModule( 19 | modules: PpssppModelTargetModuleContainer, 20 | module: PpssppHleModule 21 | ) : 22 | PpssppTargetObject( 23 | modules.model, modules, PathUtils.makeKey(PathUtils.makeIndex(module.address)), NAME 24 | ), 25 | TargetModule { 26 | 27 | companion object { 28 | const val NAME = "Module" 29 | } 30 | 31 | init { 32 | val startAddress: Address = getModel().addressFactory 33 | .defaultAddressSpace 34 | .getAddress(module.address.toString(16)) 35 | val addressRange = AddressRangeImpl(startAddress, module.size) 36 | 37 | changeAttributes( 38 | emptyList(), 39 | emptyList(), 40 | mapOf( 41 | TargetObject.DISPLAY_ATTRIBUTE_NAME to module.name, 42 | TargetModule.RANGE_ATTRIBUTE_NAME to addressRange, 43 | TargetModule.MODULE_NAME_ATTRIBUTE_NAME to module.name, 44 | ), 45 | UpdateReason.INITIALIZED 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetSymbolContainer.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleFunction 4 | import allegrex.agent.ppsspp.util.futureVoid 5 | import ghidra.dbg.DebuggerObjectModel 6 | import ghidra.dbg.target.TargetSymbolNamespace 7 | import ghidra.dbg.target.schema.TargetAttributeType 8 | import ghidra.dbg.target.schema.TargetObjectSchema 9 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 10 | 11 | @TargetObjectSchemaInfo( 12 | name = "SymbolContainer", 13 | attributes = [TargetAttributeType(type = Void::class)], 14 | canonicalContainer = true, 15 | elementResync = TargetObjectSchema.ResyncMode.ONCE 16 | ) 17 | class PpssppModelTargetSymbolContainer( 18 | process: PpssppModelTargetProcess, 19 | ) : 20 | PpssppTargetObject( 21 | process.model, process, NAME, "SymbolContainer" 22 | ), 23 | TargetSymbolNamespace { 24 | 25 | companion object { 26 | const val NAME = "Symbols" 27 | } 28 | 29 | private val targetSymbols = mutableMapOf() 30 | 31 | override fun requestElements(refresh: DebuggerObjectModel.RefreshBehavior) = modelScope.futureVoid { 32 | val newTargetSymbols = api.listFunctions() 33 | .map { getTargetSymbol(it) } 34 | val delta = setElements(newTargetSymbols, UpdateReason.REFRESHED) 35 | if (!delta.isEmpty) { 36 | targetSymbols.entries 37 | .removeIf { delta.removed.containsValue(it.value) } 38 | } 39 | } 40 | 41 | private fun getTargetSymbol(function: PpssppHleFunction): PpssppModelTargetSymbol { 42 | return targetSymbols.getOrPut(function) { PpssppModelTargetSymbol(this, function) } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppCpuRegistersEvent.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppCpuRegister 4 | import allegrex.agent.ppsspp.client.model.PpssppCpuRegistersCategory 5 | import allegrex.agent.ppsspp.client.model.PpssppException 6 | 7 | data class PpssppCpuRegistersEvent( 8 | val categories: List, 9 | override val ticket: String? 10 | ) : PpssppEvent { 11 | companion object { 12 | const val EVENT_NAME = "cpu.getAllRegs" 13 | 14 | private object Category { 15 | const val GPR = "GPR" 16 | const val FPU = "FPU" 17 | const val VFPU = "VFPU" 18 | } 19 | } 20 | 21 | override val event: String = EVENT_NAME 22 | 23 | fun getRegisters(associateWithThreadId: Long): List { 24 | return getGprRegisters(associateWithThreadId) + 25 | getFpuRegisters(associateWithThreadId) + 26 | getVfpuRegisters(associateWithThreadId) 27 | } 28 | 29 | fun getGprRegisters(associateWithThreadId: Long): List { 30 | return getCategoryByName(Category.GPR).getRegisters(associateWithThreadId) 31 | } 32 | 33 | fun getFpuRegisters(associateWithThreadId: Long): List { 34 | return getCategoryByName(Category.FPU).getRegisters(associateWithThreadId) 35 | } 36 | 37 | fun getVfpuRegisters(associateWithThreadId: Long): List { 38 | return getCategoryByName(Category.VFPU).getRegisters(associateWithThreadId) 39 | } 40 | 41 | private fun getCategoryByName(name: String): PpssppCpuRegistersCategory { 42 | return categories.firstOrNull { it.name.equals(name, ignoreCase = true) } 43 | ?: throw PpssppException("No such register category: $name") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/format/elf/relocation/AllegrexRelocationFixupHandler.kt: -------------------------------------------------------------------------------- 1 | package allegrex.format.elf.relocation 2 | 3 | import ghidra.app.plugin.core.reloc.RelocationFixupHandler 4 | import ghidra.app.util.opinion.PspElfLoader 5 | import ghidra.program.model.address.Address 6 | import ghidra.program.model.lang.Processor 7 | import ghidra.program.model.listing.Program 8 | import ghidra.program.model.reloc.Relocation 9 | 10 | @Suppress("unused") 11 | class AllegrexRelocationFixupHandler : RelocationFixupHandler() { 12 | override fun handlesProgram(program: Program): Boolean { 13 | if (PspElfLoader.PSP_ELF_NAME != program.executableFormat) { 14 | return false 15 | } 16 | val language = program.language 17 | if (language.languageDescription.size != 32) { 18 | return false 19 | } 20 | val processor = language.processor 21 | return processor == Processor.findOrPossiblyCreateProcessor("Allegrex") 22 | } 23 | 24 | override fun processRelocation( 25 | program: Program, relocation: Relocation, oldImageBase: Address, newImageBase: Address 26 | ): Boolean { 27 | when (val allegrexReloc = AllegrexRelocation.fromLongArray(relocation.values)) { 28 | is AllegrexRelocation.TypeA -> { 29 | AllegrexRelocationApplicator.applyTo( 30 | program, newImageBase, 31 | relocation.address, relocation.bytes, allegrexReloc, 32 | useInstructionStasher = true, addToRelocationTable = false 33 | ) 34 | } 35 | is AllegrexRelocation.TypeB -> { 36 | AllegrexRelocationApplicator.applyTo( 37 | program, newImageBase, allegrexReloc, 38 | origBytesProvider = { relocation.bytes }, 39 | useInstructionStasher = true, addToRelocationTable = false 40 | ) 41 | } 42 | } 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetModuleContainer.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleModule 4 | import allegrex.agent.ppsspp.client.model.PpssppHleModuleMeta 5 | import allegrex.agent.ppsspp.util.futureVoid 6 | import ghidra.dbg.DebuggerObjectModel 7 | import ghidra.dbg.target.TargetModuleContainer 8 | import ghidra.dbg.target.schema.TargetAttributeType 9 | import ghidra.dbg.target.schema.TargetObjectSchema 10 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 11 | 12 | @TargetObjectSchemaInfo( 13 | name = "ModuleContainer", 14 | attributes = [TargetAttributeType(type = Void::class)], 15 | canonicalContainer = true, 16 | elementResync = TargetObjectSchema.ResyncMode.ONCE 17 | ) 18 | class PpssppModelTargetModuleContainer( 19 | process: PpssppModelTargetProcess, 20 | ) : 21 | PpssppTargetObject( 22 | process.model, process, NAME, "ModuleContainer" 23 | ), 24 | TargetModuleContainer { 25 | 26 | companion object { 27 | const val NAME = "Modules" 28 | } 29 | 30 | private val targetModules = mutableMapOf() 31 | 32 | override fun requestElements(refresh: DebuggerObjectModel.RefreshBehavior) = modelScope.futureVoid { 33 | val newTargetModules = api.listModules() 34 | .map { getTargetModule(it) } 35 | val delta = setElements(newTargetModules, UpdateReason.REFRESHED) 36 | if (!delta.isEmpty) { 37 | targetModules.entries 38 | .removeIf { delta.removed.containsValue(it.value) } 39 | } 40 | } 41 | 42 | private fun getTargetModule(module: PpssppHleModule): PpssppModelTargetModule { 43 | return targetModules.getOrPut(module.meta()) { PpssppModelTargetModule(this, module) } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetRegister.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppCpuRegisterMeta 4 | import ghidra.dbg.target.TargetObject 5 | import ghidra.dbg.target.TargetRegister 6 | import ghidra.dbg.target.schema.TargetAttributeType 7 | import ghidra.dbg.target.schema.TargetElementType 8 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 9 | import ghidra.dbg.util.PathUtils 10 | import java.math.BigInteger 11 | 12 | @TargetObjectSchemaInfo( 13 | name = "RegisterDescriptor", 14 | elements = [TargetElementType(type = Void::class)], 15 | attributes = [TargetAttributeType(type = Void::class)] 16 | ) 17 | class PpssppModelTargetRegister( 18 | registers: PpssppModelTargetRegisterContainerAndBank, 19 | private val registerMeta: PpssppCpuRegisterMeta 20 | ) : 21 | PpssppTargetObject( 22 | registers.model, registers, PathUtils.makeKey(registerMeta.name), "Register" 23 | ), 24 | TargetRegister { 25 | 26 | init { 27 | changeAttributes( 28 | listOf(), 29 | listOf(), 30 | mapOf( 31 | TargetRegister.CONTAINER_ATTRIBUTE_NAME to registers, 32 | TargetRegister.BIT_LENGTH_ATTRIBUTE_NAME to registerMeta.bitLength, 33 | TargetObject.DISPLAY_ATTRIBUTE_NAME to "[${registerMeta.name}]", 34 | ), 35 | UpdateReason.INITIALIZED 36 | ) 37 | } 38 | 39 | fun updateValue(value: ByteArray) { 40 | val hexValue = BigInteger(1, value).toString(16) 41 | changeAttributes( 42 | emptyList(), emptyList(), mapOf( 43 | TargetRegister.VALUE_ATTRIBUTE_NAME to hexValue, 44 | TargetRegister.DISPLAY_ATTRIBUTE_NAME to "${registerMeta.name}: $hexValue", 45 | ), 46 | UpdateReason.REFRESHED 47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/InjectVfpuReadVector.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.app.plugin.processors.sleigh.SleighLanguage 4 | import ghidra.program.model.lang.InjectContext 5 | import ghidra.program.model.lang.InjectPayloadCallother 6 | import ghidra.program.model.listing.Program 7 | import ghidra.program.model.pcode.PcodeOp 8 | import ghidra.program.model.pcode.Varnode 9 | 10 | class InjectVfpuReadVector( 11 | sourceName: String, 12 | private val language: SleighLanguage, 13 | private val uniqueBase: Long, 14 | private val maxUniqueBase: Long, 15 | private val createMapper: (VfpuPcode, Varnode) -> VectorMapper, 16 | private val vfpuPcode: VfpuPcode = DefaultVfpuPcode 17 | ) : InjectPayloadCallother(sourceName) { 18 | override fun getPcode(program: Program, con: InjectContext): Array? { 19 | val output = con.output[0] 20 | val baseReg = con.inputlist[0] 21 | if (!baseReg.isRegister) { 22 | return null 23 | } 24 | 25 | val mapper = createMapper(vfpuPcode, baseReg) 26 | val pCode = PcodeOpEmitter(language, con.baseAddr, uniqueBase, maxUniqueBase) 27 | val varnodeProvider = TempVarnodeProvider(pCode) 28 | 29 | var currentVarnode = varnodeProvider.nextVarnode() 30 | pCode.emitPieceRegisters( 31 | currentVarnode, 32 | mapper.regNameAt(mapper.lastIndex), 33 | mapper.regNameAt(mapper.lastIndex - 1), 34 | ) 35 | repeat(mapper.size) { i -> 36 | val index = (mapper.lastIndex - i) 37 | if (index == mapper.lastIndex || index == mapper.lastIndex - 1) { 38 | return@repeat 39 | } 40 | val prevVarnode = currentVarnode 41 | currentVarnode = varnodeProvider.nextVarnode() 42 | pCode.emitPieceVarnodeRegister(currentVarnode, prevVarnode, mapper.regNameAt(index)) 43 | } 44 | pCode.emitAssignVarnodeToVarnode(output, currentVarnode) 45 | return pCode.emittedOps() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetMemoryRegion.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppMemoryRange 4 | import ghidra.dbg.target.TargetMemoryRegion 5 | import ghidra.dbg.target.TargetObject 6 | import ghidra.dbg.target.schema.TargetAttributeType 7 | import ghidra.dbg.target.schema.TargetElementType 8 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 9 | import ghidra.dbg.util.PathUtils 10 | import ghidra.program.model.address.Address 11 | import ghidra.program.model.address.AddressRangeImpl 12 | 13 | @TargetObjectSchemaInfo( 14 | name = PpssppModelTargetMemoryRegion.NAME, 15 | elements = [TargetElementType(type = Void::class)], 16 | attributes = [TargetAttributeType(type = Void::class)] 17 | ) 18 | class PpssppModelTargetMemoryRegion( 19 | memory: PpssppModelTargetProcessMemory, 20 | range: PpssppMemoryRange 21 | ) : 22 | PpssppTargetObject( 23 | memory.model, memory, PathUtils.makeKey(range.address.toString(16)), NAME 24 | ), 25 | TargetMemoryRegion { 26 | 27 | companion object { 28 | const val NAME = "MemoryRegion" 29 | } 30 | 31 | init { 32 | val startAddress: Address = getModel().addressFactory 33 | .defaultAddressSpace 34 | .getAddress(range.address.toString(16)) 35 | val addressRange = AddressRangeImpl(startAddress, range.size) 36 | 37 | changeAttributes( 38 | listOf(), 39 | mapOf( 40 | TargetMemoryRegion.MEMORY_ATTRIBUTE_NAME to memory, 41 | TargetMemoryRegion.RANGE_ATTRIBUTE_NAME to addressRange, 42 | TargetMemoryRegion.READABLE_ATTRIBUTE_NAME to true, 43 | TargetMemoryRegion.WRITABLE_ATTRIBUTE_NAME to true, 44 | TargetMemoryRegion.EXECUTABLE_ATTRIBUTE_NAME to true, 45 | TargetObject.DISPLAY_ATTRIBUTE_NAME to range.name 46 | ), 47 | UpdateReason.INITIALIZED 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/kotlin/allegrex/agent/ppsspp/client/websocket/PpssppWsClientManualTest.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.websocket 2 | 3 | import allegrex.agent.ppsspp.client.PpssppApi 4 | import allegrex.agent.ppsspp.client.PpssppEventListener 5 | import allegrex.agent.ppsspp.client.model.PpssppLogMessage 6 | import allegrex.agent.ppsspp.client.model.PpssppState 7 | import kotlinx.coroutines.CoroutineExceptionHandler 8 | import kotlinx.coroutines.CoroutineScope 9 | import kotlinx.coroutines.DelicateCoroutinesApi 10 | import kotlinx.coroutines.SupervisorJob 11 | import kotlinx.coroutines.cancel 12 | import kotlinx.coroutines.newSingleThreadContext 13 | import kotlinx.coroutines.runBlocking 14 | import org.apache.logging.log4j.LogManager 15 | 16 | @OptIn(DelicateCoroutinesApi::class) 17 | fun main() { 18 | val logger = LogManager.getLogger("main") 19 | val exceptionHandler = CoroutineExceptionHandler { _, cause -> 20 | logger.error("Unhandled error: ${cause.message ?: "unknown"}", cause) 21 | } 22 | val thread = newSingleThreadContext("TestThread") 23 | val scope = CoroutineScope(SupervisorJob() + thread + exceptionHandler) 24 | val client = PpssppWsClient() 25 | val api = PpssppApi(client) 26 | 27 | client.addEventListener( 28 | object : PpssppEventListener { 29 | override fun onStateChange(state: PpssppState, paused: Boolean) { 30 | logger.info("PPSSPP state change: $state, paused: $paused") 31 | } 32 | 33 | override fun onStepCompleted() { 34 | logger.info("PPSSPP step completed") 35 | } 36 | 37 | override fun onLog(message: PpssppLogMessage) { 38 | logger.debug("PPSSPP: ${message.asFormattedMessage().trim()}") 39 | } 40 | } 41 | ) 42 | 43 | runBlocking(scope.coroutineContext) { 44 | logger.info("Starting client") 45 | client.start() 46 | 47 | api.stepping() 48 | val threads = api.listThreads().first { it.name == "user_main" } 49 | api.backtraceThread(threads.id) 50 | 51 | logger.info(api.cpuStatus()) 52 | } 53 | 54 | logger.info("Closing client") 55 | client.close() 56 | scope.cancel() 57 | thread.close() 58 | } 59 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/pcode/InjectVfpuReadMatrix.kt: -------------------------------------------------------------------------------- 1 | package allegrex.pcode 2 | 3 | import ghidra.app.plugin.processors.sleigh.SleighLanguage 4 | import ghidra.program.model.lang.InjectContext 5 | import ghidra.program.model.lang.InjectPayloadCallother 6 | import ghidra.program.model.listing.Program 7 | import ghidra.program.model.pcode.PcodeOp 8 | import ghidra.program.model.pcode.Varnode 9 | 10 | class InjectVfpuReadMatrix( 11 | sourceName: String, 12 | private val language: SleighLanguage, 13 | private val uniqueBase: Long, 14 | private val maxUniqueBase: Long, 15 | private val createMapper: (VfpuPcode, Varnode, Boolean) -> MatrixMapper, 16 | private val vfpuPcode: VfpuPcode = DefaultVfpuPcode 17 | ) : InjectPayloadCallother(sourceName) { 18 | override fun getPcode(program: Program, con: InjectContext): Array? { 19 | val output = con.output[0] 20 | val baseReg = con.inputlist[0] 21 | if (!baseReg.isRegister) { 22 | return null 23 | } 24 | val transpose = con.inputlist[1].offset.toInt() != 0 25 | 26 | val mapper = createMapper(vfpuPcode, baseReg, transpose) 27 | val pCode = PcodeOpEmitter(language, con.baseAddr, uniqueBase, maxUniqueBase) 28 | val varnodeProvider = TempVarnodeProvider(pCode) 29 | 30 | var currentVarnode = varnodeProvider.nextVarnode() 31 | pCode.emitPieceRegisters( 32 | currentVarnode, 33 | mapper.regNameAt(mapper.lastDimIndex, mapper.lastDimIndex), 34 | mapper.regNameAt(mapper.lastDimIndex, mapper.lastDimIndex - 1) 35 | ) 36 | repeat(mapper.dimSize) { i -> 37 | repeat(mapper.dimSize) next@{ ii -> 38 | val row = (mapper.lastDimIndex - i) 39 | val column = (mapper.lastDimIndex - ii) 40 | if ((row == mapper.lastDimIndex && column == mapper.lastDimIndex) || (row == mapper.lastDimIndex && column == mapper.lastDimIndex - 1)) { 41 | return@next 42 | } 43 | val prevVarnode = currentVarnode 44 | currentVarnode = varnodeProvider.nextVarnode() 45 | pCode.emitPieceVarnodeRegister(currentVarnode, prevVarnode, mapper.regNameAt(row, column)) 46 | } 47 | } 48 | pCode.emitAssignVarnodeToVarnode(output, currentVarnode) 49 | return pCode.emittedOps() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/model/PpssppModelTargetThreadContainer.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.model 2 | 3 | import allegrex.agent.ppsspp.client.model.PpssppHleThread 4 | import allegrex.agent.ppsspp.client.model.PpssppHleThreadMeta 5 | import allegrex.agent.ppsspp.util.futureVoid 6 | import ghidra.dbg.DebuggerObjectModel 7 | import ghidra.dbg.target.schema.TargetAttributeType 8 | import ghidra.dbg.target.schema.TargetObjectSchema 9 | import ghidra.dbg.target.schema.TargetObjectSchemaInfo 10 | 11 | @TargetObjectSchemaInfo( 12 | name = "ThreadContainer", 13 | attributes = [TargetAttributeType(type = Void::class)], 14 | canonicalContainer = true, 15 | elementResync = TargetObjectSchema.ResyncMode.ONCE 16 | ) 17 | class PpssppModelTargetThreadContainer( 18 | process: PpssppModelTargetProcess, 19 | ) : 20 | PpssppTargetObject( 21 | process.model, process, NAME, "ThreadContainer" 22 | ) { 23 | companion object { 24 | const val NAME = "Threads" 25 | } 26 | 27 | private val targetThreads = mutableMapOf() 28 | 29 | override fun requestElements(refresh: DebuggerObjectModel.RefreshBehavior) = modelScope.futureVoid { 30 | updateUsingThreads(api.listThreads()) 31 | } 32 | 33 | fun updateUsingThreads(threads: List) { 34 | val newTargetThreads = threads 35 | .map { getTargetThread(it) } 36 | val delta = setElements(newTargetThreads, UpdateReason.REFRESHED) 37 | if (!delta.isEmpty) { 38 | targetThreads.entries 39 | .removeIf { delta.removed.containsValue(it.value) } 40 | } 41 | } 42 | 43 | private fun getTargetThread(thread: PpssppHleThread): PpssppModelTargetThread { 44 | return targetThreads.getOrPut(thread.meta()) { PpssppModelTargetThread(this, thread) } 45 | } 46 | 47 | fun getAnyThreadOrNull(): PpssppModelTargetThread? { 48 | return targetThreads.values.firstOrNull() 49 | } 50 | 51 | fun getThreadById(id: Long): PpssppModelTargetThread? { 52 | return targetThreads.values.firstOrNull { it.thread.id == id } 53 | } 54 | 55 | fun invalidateRegisterCaches() { 56 | targetThreads.forEach { (_, thread) -> 57 | thread.invalidateRegisterCaches() 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /data/patterns/Allegrex_patterns.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0x0800e003 0x........ 5 | 0x0800e003 0x........ 0x00000000 6 | 0x0800e003 0x........ 0x00000000 0x00000000 7 | 0x0800e003 0x........ 0x00000000 0x00000000 0x00000000 8 | 0x......08 ......00 0....... 0xbd 0x27 9 | 0x....0010 ......00 0....... 0xbd 0x27 10 | ..001000 00000... 0x20 0x03 0x0. 0x.. 0xbd 0x27 11 | 12 | 13 | ......00 1....... 10111101 00100111 14 | 0x......3c ......00 1....... 0xbd 0x27 15 | 0x...... 100011.. ......00 1....... 0xbd 0x27 16 | 0x......3c 0x...... 100011.. ......00 1....... 0xbd 0x27 17 | 0x....1c3c 0x....9c27 18 | 19 | 20 | 21 | 22 | 23 | 0x0800e003 0x........ 24 | 0x0800e003 0x........ 0x00000000 25 | 0x0800e003 0x........ 0x00000000 0x00000000 26 | 0x0800e003 0x........ 0x00000000 0x00000000 0x00000000 27 | 0x......08 ......00 0....... 0xbd 0x27 28 | 29 | 30 | 31 | 0x....063c 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/kotlin/allegrex/agent/ppsspp/client/model/event/PpssppEventMap.kt: -------------------------------------------------------------------------------- 1 | package allegrex.agent.ppsspp.client.model.event 2 | 3 | val ppssppEventMap = mapOf( 4 | PpssppCpuBreakpointAddEvent.EVENT_NAME to PpssppCpuBreakpointAddEvent::class.java, 5 | PpssppCpuBreakpointListEvent.EVENT_NAME to PpssppCpuBreakpointListEvent::class.java, 6 | PpssppCpuBreakpointRemoveEvent.EVENT_NAME to PpssppCpuBreakpointRemoveEvent::class.java, 7 | PpssppCpuBreakpointUpdateEvent.EVENT_NAME to PpssppCpuBreakpointUpdateEvent::class.java, 8 | 9 | PpssppCpuEvaluateEvent.EVENT_NAME to PpssppCpuEvaluateEvent::class.java, 10 | PpssppCpuRegistersEvent.EVENT_NAME to PpssppCpuRegistersEvent::class.java, 11 | PpssppCpuResumeEvent.EVENT_NAME to PpssppCpuResumeEvent::class.java, 12 | PpssppCpuStatusEvent.EVENT_NAME to PpssppCpuStatusEvent::class.java, 13 | PpssppCpuSteppingEvent.EVENT_NAME to PpssppCpuSteppingEvent::class.java, 14 | 15 | PpssppErrorEvent.EVENT_NAME to PpssppErrorEvent::class.java, 16 | 17 | PpssppGamePauseEvent.EVENT_NAME to PpssppGamePauseEvent::class.java, 18 | PpssppGameQuitEvent.EVENT_NAME to PpssppGameQuitEvent::class.java, 19 | PpssppGameResumeEvent.EVENT_NAME to PpssppGameResumeEvent::class.java, 20 | PpssppGameStartEvent.EVENT_NAME to PpssppGameStartEvent::class.java, 21 | PpssppGameStatusEvent.EVENT_NAME to PpssppGameStatusEvent::class.java, 22 | 23 | PpssppHleBacktraceEvent.EVENT_NAME to PpssppHleBacktraceEvent::class.java, 24 | PpssppHleFunctionListEvent.EVENT_NAME to PpssppHleFunctionListEvent::class.java, 25 | PpssppHleModuleListEvent.EVENT_NAME to PpssppHleModuleListEvent::class.java, 26 | PpssppHleThreadsListEvent.EVENT_NAME to PpssppHleThreadsListEvent::class.java, 27 | 28 | PpssppLogEvent.EVENT_NAME to PpssppLogEvent::class.java, 29 | 30 | PpssppMemoryBreakpointAddEvent.EVENT_NAME to PpssppMemoryBreakpointAddEvent::class.java, 31 | PpssppMemoryBreakpointListEvent.EVENT_NAME to PpssppMemoryBreakpointListEvent::class.java, 32 | PpssppMemoryBreakpointRemoveEvent.EVENT_NAME to PpssppMemoryBreakpointRemoveEvent::class.java, 33 | PpssppMemoryBreakpointUpdateEvent.EVENT_NAME to PpssppMemoryBreakpointUpdateEvent::class.java, 34 | 35 | PpssppMemoryMappingEvent.EVENT_NAME to PpssppMemoryMappingEvent::class.java, 36 | PpssppMemoryReadEvent.EVENT_NAME to PpssppMemoryReadEvent::class.java, 37 | PpssppMemoryWriteEvent.EVENT_NAME to PpssppMemoryWriteEvent::class.java, 38 | 39 | PpssppSetRegisterEvent.EVENT_NAME to PpssppSetRegisterEvent::class.java, 40 | ) 41 | -------------------------------------------------------------------------------- /src/main/kotlin/ghidra/app/util/opinion/PspElfLoader.kt: -------------------------------------------------------------------------------- 1 | package ghidra.app.util.opinion 2 | 3 | import ghidra.app.util.Option 4 | import ghidra.app.util.OptionUtils 5 | import ghidra.app.util.bin.ByteProvider 6 | import ghidra.app.util.bin.format.elf.ElfException 7 | import ghidra.app.util.bin.format.elf.PspElfHeader 8 | import ghidra.app.util.importer.MessageLog 9 | import ghidra.framework.model.DomainObject 10 | import ghidra.program.model.listing.Program 11 | import ghidra.util.task.TaskMonitor 12 | import java.io.IOException 13 | 14 | class PspElfLoader : ElfLoader() { 15 | companion object { 16 | const val PSP_ELF_NAME = "PSP Executable (ELF)" 17 | 18 | object Options { 19 | private const val COMMAND_LINE_ARG_PREFIX = "-psp" 20 | 21 | object UseRebootBinTypeBMapping { 22 | private const val NAME = "Use reboot.bin Type B Relocation Mapping" 23 | private const val DEFAULT = false 24 | private val TYPE = java.lang.Boolean::class.java // must be Java Boolean 25 | private const val COMMAND_LINE = "${COMMAND_LINE_ARG_PREFIX}-useRebootBinTypeBRelocationMapping" 26 | 27 | fun toOption(): Option { 28 | return Option(NAME, DEFAULT, TYPE, COMMAND_LINE) 29 | } 30 | 31 | fun getValue(options: List