├── 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 |
17 |
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