Function bodies 388 total
MainActivity class · kotlin · L28-L171 (144 LOC)app/ps2client/src/androidMain/kotlin/com/my/psremoteplay/app/ps2client/MainActivity.kt
class MainActivity : ComponentActivity() {
private var viewModel: Ps2ClientViewModel? = null
private var controllerState = ControllerState()
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).apply {
hide(WindowInsetsCompat.Type.systemBars())
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
setContent {
val deps = remember { AndroidPs2ClientDependencies(StreamingPreset.H264_HW) }
val vm = viewModel { Ps2ClientViewModel(deps) }
viewModel = vm
LaunchedEffect(Unit) {
vm.effects.collectLMainActivity class · kotlin · L15-L43 (29 LOC)app/ps3/src/androidMain/kotlin/com/my/psremoteplay/app/ps3/MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
val deps = remember { AndroidPs3Dependencies() }
val viewModel = viewModel { Ps3ViewModel(deps) }
LaunchedEffect(Unit) {
viewModel.effects.collectLatest { effect ->
when (effect) {
is Ps3Effect.CopyToClipboard -> {
val clipboard = getSystemService(CLIPBOARD_SERVICE) as android.content.ClipboardManager
clipboard.setPrimaryClip(android.content.ClipData.newPlainText("logs", effect.text))
}
is Ps3Effect.ShowMessage -> {
android.widget.Toast.makeText(this@MainActivity, effect.message, android.widget.Toast.LENGTH_SHORT).show()
}
}
AndroidControllerInput class · kotlin · L16-L50 (35 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/AndroidControllerInput.kt
class AndroidControllerInput(private val logger: PremoLogger) : ControllerInputSender {
private var socket: Socket? = null
private var isConnected = false
override suspend fun connect(ps3Ip: String, sessionId: String, authToken: String) = withContext(Dispatchers.IO) {
try {
socket = Socket(ps3Ip, PremoConstants.PORT)
isConnected = true
logger.log("PAD", "[ANDROID] Controller connected")
} catch (e: Exception) {
logger.error("PAD", "[ANDROID] Connection failed", e)
isConnected = false
}
}
override suspend fun sendState(state: ControllerState) = withContext(Dispatchers.IO) {
if (!isConnected) return@withContext
try {
PadProtocol.buildPadPacket(state)
// TODO: Queue to batch, send when full
} catch (e: Exception) {
logger.error("PAD", "[ANDROID] Send error", e)
}
}
override suspend fun disconnect() = withContAndroidPremoCrypto class · kotlin · L10-L26 (17 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/AndroidPremoCrypto.kt
object AndroidPremoCrypto : PremoCrypto {
override fun aesEncrypt(data: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
return cipher.doFinal(data)
}
override fun aesDecrypt(data: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
return cipher.doFinal(data)
}
override fun base64Encode(data: ByteArray): String = Base64.getEncoder().encodeToString(data)
override fun base64Decode(data: String): ByteArray = Base64.getDecoder().decode(data)
override fun randomBytes(count: Int): ByteArray = ByteArray(count).also { SecureRandom().nextBytes(it) }
}AndroidPremoSession class · kotlin · L9-L227 (219 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/AndroidPremoSession.kt
class AndroidPremoSession(private val crypto: PremoCrypto, private val logger: PremoLogger) : PremoSessionHandler {
private var socket: Socket? = null
override suspend fun createSession(ps3Ip: String, config: SessionConfig): Result<SessionResponse> =
withContext(Dispatchers.IO) {
try {
val sock = Socket(ps3Ip, PremoConstants.PORT)
val request = buildSessionRequest(config, crypto)
logger.log("SESSION", "Sending session request to $ps3Ip:${PremoConstants.PORT}")
sock.getOutputStream().write(request.toByteArray(Charsets.US_ASCII))
sock.getOutputStream().flush()
val responseBytes = ByteArray(4096)
val n = sock.getInputStream().read(responseBytes)
val responseText = if (n > 0) String(responseBytes, 0, n, Charsets.US_ASCII) else ""
logger.log("SESSION", "Response:\n$responseText")
sock.close()
AndroidPs3Discoverer class · kotlin · L11-L115 (105 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/AndroidPs3Discoverer.kt
class AndroidPs3Discoverer(private val logger: PremoLogger) : Ps3Discoverer {
override suspend fun discover(timeoutMs: Int): Ps3Info? = withContext(Dispatchers.IO) {
discoverViaBroadcast(timeoutMs)
?: discoverViaSubnet(timeoutMs)
}
override suspend fun discoverDirect(ip: String, timeoutMs: Int): Ps3Info? = withContext(Dispatchers.IO) {
val udpResult = try {
logger.log("DISCOVERY", "Trying UDP SRCH to $ip:${PremoConstants.PORT}...")
sendSrchTo(InetAddress.getByName(ip), timeoutMs)
} catch (e: Exception) {
logger.log("DISCOVERY", "UDP SRCH failed: ${e.message}")
null
}
if (udpResult != null) return@withContext udpResult
return@withContext try {
logger.log("DISCOVERY", "Trying TCP connection to $ip:${PremoConstants.PORT}...")
val socket = Socket()
socket.connect(InetSocketAddress(ip, PremoConstants.PORT), timeoutMs)
sockAndroidVideoRenderer class · kotlin · L17-L49 (33 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/AndroidVideoRenderer.kt
class AndroidVideoRenderer(private val logger: PremoLogger) : VideoRenderer {
private val _currentFrame = MutableStateFlow<ImageBitmap?>(null)
override val currentFrame: StateFlow<ImageBitmap?> = _currentFrame.asStateFlow()
private var packetCount = 0
override suspend fun start() = withContext(Dispatchers.IO) {
logger.log("VIDEO", "[ANDROID] Video renderer initialized")
}
override suspend fun onStreamPacket(header: ByteArray, payload: ByteArray, isEncrypted: Boolean) =
withContext(Dispatchers.IO) {
val magic = header[1].toInt() and 0xFF
packetCount++
when (magic) {
0xFF, 0xFE -> {
if (packetCount % 30 == 0) {
logger.log("VIDEO", "[ANDROID] H.264 frame ${packetCount}: ${payload.size} bytes")
}
// TODO: Queue to MediaCodec decoder
}
0xFD -> logger.log("VIDEO", "[ANDROID] Flush siIf a scraper extracted this row, it came from Repobility (https://repobility.com)
AndroidDependencies class · kotlin · L10-L30 (21 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/di/AndroidDependencies.kt
class AndroidDependencies : PlatformDependencies {
override val logger: PremoLogger = object : PremoLogger {
override fun log(tag: String, message: String) {
android.util.Log.d(tag, message)
}
override fun error(tag: String, message: String, throwable: Throwable?) {
android.util.Log.e(tag, message, throwable)
}
}
override val crypto: PremoCrypto = AndroidPremoCrypto
override val discoverer: Ps3Discoverer = AndroidPs3Discoverer(logger)
override val sessionHandler: PremoSessionHandler = AndroidPremoSession(crypto, logger)
override val registration: PremoRegistration = StubRegistration()
override val videoDecoder: VideoDecoder = StubVideoDecoder()
override val audioDecoder: AudioDecoder = StubAudioDecoder()
override val videoRenderer: VideoRenderer = AndroidVideoRenderer(logger)
override val audioRenderer: AudioRenderer = StubAudioRenderer(logger)
override val upscaleFilter: UpscaleFilter MainActivity class · kotlin · L15-L44 (30 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
// ViewModel survives configuration changes (rotation) with this factory pattern
val deps = remember { AndroidDependencies() }
val viewModel = viewModel { RemotePlayViewModel(deps) }
LaunchedEffect(Unit) {
viewModel.effects.collectLatest { effect ->
when (effect) {
is RemotePlayEffect.CopyToClipboard -> {
val clipboard = getSystemService(CLIPBOARD_SERVICE) as android.content.ClipboardManager
clipboard.setPrimaryClip(android.content.ClipData.newPlainText("logs", effect.text))
}
is RemotePlayEffect.ShowMessage -> {
android.widget.Toast.makeText(this@MainActivity,AndroidPlatform class · kotlin · L5-L7 (3 LOC)composeApp/src/androidMain/kotlin/com/my/psoldremoteplay/Platform.android.kt
class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
}RemotePlayViewModel class · kotlin · L12-L389 (378 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/presentation/RemotePlayViewModel.kt
class RemotePlayViewModel(private val deps: PlatformDependencies) : ViewModel() {
private val _state = MutableStateFlow(RemotePlayState())
val state: StateFlow<RemotePlayState> = _state.asStateFlow()
private val _effects = Channel<RemotePlayEffect>(Channel.BUFFERED)
val effects: Flow<RemotePlayEffect> = _effects.receiveAsFlow()
private val logger = object : PremoLogger {
override fun log(tag: String, message: String) {
addLog(tag, message, isError = false)
deps.logger.log(tag, message)
}
override fun error(tag: String, message: String, throwable: Throwable?) {
val errMsg = "$message${throwable?.let { "\n ${it::class.simpleName}: ${it.message}" } ?: ""}"
addLog(tag, errMsg, isError = true)
deps.logger.error(tag, message, throwable)
}
}
fun onIntent(intent: RemotePlayIntent) {
when (intent) {
is RemotePlayIntent.UpdateIp -> updateState { copy(pCompanion class · kotlin · L380-L388 (9 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/presentation/RemotePlayViewModel.kt
companion object {
fun cleanIp(raw: String): String {
var ip = raw.trim()
ip = ip.removePrefix("http://").removePrefix("https://")
ip = ip.trimEnd('/')
ip = ip.split(":").first()
return ip
}
}ControllerButtons class · kotlin · L13-L31 (19 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/ControllerInput.kt
object ControllerButtons {
const val CROSS = 0x0001
const val CIRCLE = 0x0002
const val SQUARE = 0x0004
const val TRIANGLE = 0x0008
const val L1 = 0x0010
const val R1 = 0x0020
const val L2 = 0x0040
const val R2 = 0x0080
const val SELECT = 0x0100
const val START = 0x0200
const val L3 = 0x0400
const val R3 = 0x0800
const val UP = 0x1000
const val DOWN = 0x2000
const val LEFT = 0x4000
const val RIGHT = 0x8000
const val PS = 0x10000
}PadProtocol class · kotlin · L13-L72 (60 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/PadProtocol.kt
object PadProtocol {
/**
* Build a single 128-byte pad packet from ControllerState.
*/
fun buildPadPacket(state: ControllerState): ByteArray {
val packet = ByteArray(128)
// Magic constant
packet[0x00] = 0x74
// Left analog stick (center = 0x80, range 0x00-0xFF)
packet[0x04] = (0x80 + (state.leftStickX * 127)).roundToInt().toByte()
packet[0x05] = (0x80 + (state.leftStickY * 127)).roundToInt().toByte()
// Right analog stick
packet[0x08] = (0x80 + (state.rightStickX * 127)).roundToInt().toByte()
packet[0x09] = (0x80 + (state.rightStickY * 127)).roundToInt().toByte()
// Buttons — two bytes: high byte + low byte
// High byte [0x05]: L2/R2/L1/R1/Triangle/Circle/Cross/Square
// Low byte [0x07]: Select/L3/R3/Start/Up/Right/Down/Left
packet[0x05] = buildButtonByte(state.buttons, listOf(
ControllerButtons.SQUARE,
ControllerButtons.CROSS,
PremoConstants class · kotlin · L3-L87 (85 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/PremoConstants.kt
object PremoConstants {
const val PORT = 9293
const val PREMO_VERSION_MAJOR = 0
const val PREMO_VERSION_MINOR = 3
const val USER_AGENT = "premo/1.0.0 libhttp/1.0.0"
const val FRAME_WIDTH = 480
const val FRAME_HEIGHT = 272
// UDP discovery
val SRCH_PACKET = byteArrayOf(0x53, 0x52, 0x43, 0x48) // "SRCH"
val RESP_MAGIC = byteArrayOf(0x52, 0x45, 0x53, 0x50) // "RESP"
// Static session keys (same for ALL PS3s — from Open-RP + Ghidra confirmation)
val SKEY0 = byteArrayOf(
0xD1.toByte(), 0xB2.toByte(), 0x12, 0xEB.toByte(),
0x73, 0x86.toByte(), 0x6C, 0x7B,
0x12, 0xA7.toByte(), 0x5E, 0x0C,
0x04, 0xC6.toByte(), 0xB8.toByte(), 0x91.toByte()
)
val SKEY1 = byteArrayOf(
0x1F, 0xD5.toByte(), 0xB9.toByte(), 0xFA.toByte(),
0x71, 0xB8.toByte(), 0x96.toByte(), 0x81.toByte(),
0xB2.toByte(), 0x87.toByte(), 0x92.toByte(), 0xE2.toByte(),
0x6F, 0x38, 0xC3.toByte(), 0x6F
)
val SKEY2 =Repobility · code-quality intelligence platform · https://repobility.com
StubVideoDecoder class · kotlin · L12-L17 (6 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/StubDecoders.kt
class StubVideoDecoder : VideoDecoder {
override suspend fun start(width: Int, height: Int, codecName: String) {}
override suspend fun decode(nalData: ByteArray): VideoFrame? = null
override suspend fun flush() {}
override suspend fun stop() {}
}StubAudioDecoder class · kotlin · L22-L27 (6 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/StubDecoders.kt
class StubAudioDecoder : AudioDecoder {
override suspend fun start(sampleRate: Int, channels: Int, codecName: String) {}
override suspend fun decode(data: ByteArray): AudioBuffer? = null
override suspend fun flush() {}
override suspend fun stop() {}
}StubRegistration class · kotlin · L9-L25 (17 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/StubImplementations.kt
class StubRegistration : PremoRegistration {
override suspend fun register(
ps3Ip: String,
pin: String,
deviceId: ByteArray,
deviceMac: ByteArray,
deviceName: String,
platformType: Int
): Result<RegistrationResult> {
return Result.failure(
UnsupportedOperationException(
"Registration encryption not yet solved. " +
"Use HEN PS3 with pre-extracted keys or wait for community breakthrough."
)
)
}
}StubControllerInput class · kotlin · L27-L31 (5 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/StubImplementations.kt
class StubControllerInput : ControllerInputSender {
override suspend fun connect(ps3Ip: String, sessionId: String, authToken: String) {}
override suspend fun sendState(state: ControllerState) {}
override suspend fun disconnect() {}
}LoggingVideoRenderer class · kotlin · L33-L54 (22 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/StubImplementations.kt
class LoggingVideoRenderer(private val logger: PremoLogger) : VideoRenderer {
private var packetCount = 0
private val _currentFrame = MutableStateFlow<ImageBitmap?>(null)
override val currentFrame: StateFlow<ImageBitmap?> = _currentFrame.asStateFlow()
override suspend fun start() {
logger.log("VIDEO", "Video renderer started")
}
override suspend fun onStreamPacket(header: ByteArray, payload: ByteArray, isEncrypted: Boolean) {
packetCount++
if (packetCount % 30 == 0) {
logger.log("VIDEO", "Received $packetCount packets, last payload: ${payload.size} bytes")
}
}
override suspend fun stop() {
logger.log("VIDEO", "Video renderer stopped ($packetCount total packets)")
packetCount = 0
_currentFrame.value = null
}
}StubAudioRenderer class · kotlin · L56-L68 (13 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/StubImplementations.kt
class StubAudioRenderer(private val logger: PremoLogger) : AudioRenderer {
override suspend fun start() {
logger.log("AUDIO", "Audio renderer started")
}
override suspend fun onAudioBuffer(buffer: AudioBuffer) {
logger.log("AUDIO", "Audio buffer: ${buffer.samples.size} bytes @ ${buffer.sampleRate}Hz")
}
override suspend fun stop() {
logger.log("AUDIO", "Audio renderer stopped")
}
}PassthroughUpscaler class · kotlin · L8-L10 (3 LOC)composeApp/src/commonMain/kotlin/com/my/psoldremoteplay/protocol/upscale/PassthroughUpscaler.kt
class PassthroughUpscaler : UpscaleFilter {
override suspend fun process(frame: VideoFrame): VideoFrame = frame
}DesktopDependencies class · kotlin · L10-L31 (22 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/di/DesktopDependencies.kt
class DesktopDependencies : PlatformDependencies {
override val logger: PremoLogger = object : PremoLogger {
override fun log(tag: String, message: String) {
println("[$tag] $message")
}
override fun error(tag: String, message: String, throwable: Throwable?) {
System.err.println("[ERROR:$tag] $message")
throwable?.printStackTrace(System.err)
}
}
override val crypto: PremoCrypto = JvmPremoCrypto
override val discoverer: Ps3Discoverer = JvmPs3Discoverer(logger)
override val sessionHandler: PremoSessionHandler = JvmPremoSession(crypto, logger)
override val registration: PremoRegistration = JvmPremoRegistration(crypto, logger)
override val videoDecoder: VideoDecoder = StubVideoDecoder()
override val audioDecoder: AudioDecoder = StubAudioDecoder()
override val videoRenderer: VideoRenderer = JvmVideoRenderer(logger)
override val audioRenderer: AudioRenderer = StubAudioRenderer(logger)Repobility (the analyzer behind this table) · https://repobility.com
JvmControllerInput class · kotlin · L19-L54 (36 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/JvmControllerInput.kt
class JvmControllerInput(private val logger: PremoLogger) : ControllerInputSender {
private var socket: Socket? = null
private var isConnected = false
override suspend fun connect(ps3Ip: String, sessionId: String, authToken: String) = withContext(Dispatchers.IO) {
try {
socket = Socket(ps3Ip, PremoConstants.PORT)
isConnected = true
logger.log("PAD", "[DESKTOP] Controller connected to $ps3Ip")
} catch (e: Exception) {
logger.error("PAD", "[DESKTOP] Controller connection failed", e)
isConnected = false
}
}
override suspend fun sendState(state: ControllerState) = withContext(Dispatchers.IO) {
if (!isConnected) return@withContext
try {
val packet = PadProtocol.buildPadPacket(state)
logger.log("PAD", "[DESKTOP] Pad state: buttons=${"%05X".format(state.buttons)}, sticks=(${state.leftStickX.toInt()},${state.leftStickY.toInt()})")
// TODOJvmPremoCrypto class · kotlin · L10-L26 (17 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/JvmPremoCrypto.kt
object JvmPremoCrypto : PremoCrypto {
override fun aesEncrypt(data: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
return cipher.doFinal(data)
}
override fun aesDecrypt(data: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
return cipher.doFinal(data)
}
override fun base64Encode(data: ByteArray): String = Base64.getEncoder().encodeToString(data)
override fun base64Decode(data: String): ByteArray = Base64.getDecoder().decode(data)
override fun randomBytes(count: Int): ByteArray = ByteArray(count).also { SecureRandom().nextBytes(it) }
}JvmPremoRegistration class · kotlin · L19-L89 (71 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/JvmPremoRegistration.kt
class JvmPremoRegistration(private val crypto: PremoCrypto, private val logger: PremoLogger) : PremoRegistration {
override suspend fun register(
ps3Ip: String,
pin: String,
deviceId: ByteArray,
deviceMac: ByteArray,
deviceName: String,
platformType: Int
): Result<RegistrationResult> = withContext(Dispatchers.IO) {
try {
logger.log("REGIST", "=== REGISTRATION STRATEGY ORCHESTRATOR ===")
logger.log("REGIST", "PS3: $ps3Ip, PIN: $pin")
logger.log("REGIST", "Testing ${RegistrationStrategies.allStrategies.count { it.isEnabled }} enabled strategies...")
logger.log("REGIST", "")
val attempts = mutableListOf<RegistrationAttempt>()
for (strategy in RegistrationStrategies.allStrategies) {
if (!strategy.isEnabled) {
logger.log("REGIST", "⊘ SKIPPED: ${strategy.name} (disabled)")
logger.log("REGIST", " ReJvmPremoSession class · kotlin · L10-L279 (270 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/JvmPremoSession.kt
class JvmPremoSession(private val crypto: PremoCrypto, private val logger: PremoLogger) : PremoSessionHandler {
private var socket: Socket? = null
override suspend fun createSession(ps3Ip: String, config: SessionConfig): Result<SessionResponse> =
withContext(Dispatchers.IO) {
try {
val sock = Socket(ps3Ip, PremoConstants.PORT)
val out = sock.getOutputStream()
val inp = sock.getInputStream()
val request = buildSessionRequest(config, crypto)
logger.log("SESSION", "Sending session request to $ps3Ip:${PremoConstants.PORT}")
logger.log("SESSION", request)
out.write(request.toByteArray(Charsets.US_ASCII))
out.flush()
val responseBytes = readHttpResponse(inp)
val responseText = String(responseBytes, Charsets.US_ASCII)
logger.log("SESSION", "Response:\n$responseText")
sockJvmPs3Discoverer class · kotlin · L11-L161 (151 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/JvmPs3Discoverer.kt
class JvmPs3Discoverer(private val logger: PremoLogger) : Ps3Discoverer {
override suspend fun discover(timeoutMs: Int): Ps3Info? = withContext(Dispatchers.IO) {
// Try broadcast first, then directed if we have a subnet
discoverViaBroadcast(timeoutMs)
?: discoverViaSubnet(timeoutMs)
}
private fun discoverViaBroadcast(timeoutMs: Int): Ps3Info? {
return try {
logger.log("DISCOVERY", "Trying 255.255.255.255 broadcast on port ${PremoConstants.PORT}...")
val socket = DatagramSocket()
socket.broadcast = true
socket.soTimeout = timeoutMs
val srch = PremoConstants.SRCH_PACKET
val broadcastAddr = InetAddress.getByName("255.255.255.255")
val sendPacket = DatagramPacket(srch, srch.size, broadcastAddr, PremoConstants.PORT)
logger.log("DISCOVERY", "Sending SRCH packet (${srch.size} bytes) to 255.255.255.255:${PremoConstants.PORT}")
socket.senJvmVideoRenderer class · kotlin · L17-L49 (33 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/JvmVideoRenderer.kt
class JvmVideoRenderer(private val logger: PremoLogger) : VideoRenderer {
private val _currentFrame = MutableStateFlow<androidx.compose.ui.graphics.ImageBitmap?>(null)
override val currentFrame: StateFlow<androidx.compose.ui.graphics.ImageBitmap?> = _currentFrame.asStateFlow()
private var packetCount = 0
override suspend fun start() = withContext(Dispatchers.IO) {
logger.log("VIDEO", "[DESKTOP] Video renderer initialized")
}
override suspend fun onStreamPacket(header: ByteArray, payload: ByteArray, isEncrypted: Boolean) =
withContext(Dispatchers.IO) {
val magic = header[1].toInt() and 0xFF
packetCount++
when (magic) {
0xFF, 0xFE -> {
if (packetCount % 30 == 0) {
logger.log("VIDEO", "[DESKTOP] H.264 frame ${packetCount}: ${payload.size} bytes")
}
// TODO: Decode H.264 NAL data and update _currentFrame
KeyboardController class · kotlin · L25-L100 (76 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/KeyboardController.kt
object KeyboardController {
private var currentState = ControllerState()
private val pressedKeys = mutableSetOf<Key>()
fun handleKeyDown(event: KeyEvent): ControllerState? {
// Note: KeyEvent in Compose uses nativeKeyCode, need to map to Key objects
// For now, this is a placeholder that returns null
return null
}
fun handleKeyUp(event: KeyEvent): ControllerState? {
// Note: KeyEvent in Compose uses nativeKeyCode, need to map to Key objects
// For now, this is a placeholder that returns null
return null
}
private fun updateState(): ControllerState? {
var newState = ControllerState()
// D-Pad (arrow keys)
if (pressedKeys.contains(Key.DirectionUp)) newState = newState.copy(buttons = newState.buttons or ControllerButtons.UP)
if (pressedKeys.contains(Key.DirectionDown)) newState = newState.copy(buttons = newState.buttons or ControllerButtons.DOWN)
if (pressedKeys.containsDesktopPlatform class · kotlin · L3-L5 (3 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/Platform.desktop.kt
class DesktopPlatform : Platform {
override val name: String = "Desktop JVM ${System.getProperty("java.version")}"
}Open data scored by Repobility · https://repobility.com
PhoneTypePS4FormulaStrategy class · kotlin · L15-L262 (248 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/RegistrationStrategyImpl.kt
class PhoneTypePS4FormulaStrategy : RegistrationStrategy {
override val name = "Phone Type PS4 Formula"
override val description = "Phone Type (1) with PIN as 4-byte BE + 4 zeros"
override val isEnabled = false // Already tested and failed
override suspend fun attemptRegistration(
ps3Ip: String,
pin: String,
deviceId: ByteArray,
deviceMac: ByteArray,
deviceName: String,
crypto: PremoCrypto,
logger: PremoLogger
): RegistrationAttempt = withContext(Dispatchers.IO) {
simpleRegisterAttempt(ps3Ip, pin, deviceId, deviceMac, deviceName, 1, false, crypto, logger, name)
}
private fun deriveKeyPhone(keyMaterial: ByteArray): ByteArray {
val key = ByteArray(16)
for (i in 0..15) {
key[i] = (((keyMaterial[i].toInt() and 0xFF) - i - 0x28) xor (PremoConstants.REG_XOR_PHONE[i].toInt() and 0xFF)).toByte()
}
return key
}
private fun deriveIvPhone(contextBytesPCTypePS4FormulaStrategy class · kotlin · L275-L465 (191 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/RegistrationStrategyImpl.kt
class PCTypePS4FormulaStrategy : RegistrationStrategy {
override val name = "PC Type PS4 Formula"
override val description = "PC Type (2) with PIN-derived key material (4-byte BE + 12 zeros) and 480-byte body prefix"
override val isEnabled = true // HIGH PRIORITY - new approach
override suspend fun attemptRegistration(
ps3Ip: String,
pin: String,
deviceId: ByteArray,
deviceMac: ByteArray,
deviceName: String,
crypto: PremoCrypto,
logger: PremoLogger
): RegistrationAttempt = withContext(Dispatchers.IO) {
try {
logger.log("REGIST", "▶ $name")
val bodyText = buildString {
append("Client-Type: VITA\r\n")
append("Client-Id: ${deviceId.joinToString("") { "%02x".format(it) }}\r\n")
append("Client-Mac: ${deviceMac.joinToString("") { "%02x".format(it) }}\r\n")
append("Client-Nickname: $deviceName\r\n")
}
PSPTypePS4FormulaStrategy class · kotlin · L476-L652 (177 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/RegistrationStrategyImpl.kt
class PSPTypePS4FormulaStrategy : RegistrationStrategy {
override val name = "PSP Type PS4 Formula"
override val description = "PSP Type (0) with PIN as 4-byte BE + 4 zeros"
override val isEnabled = true // NEW - Never tested yet
override suspend fun attemptRegistration(
ps3Ip: String,
pin: String,
deviceId: ByteArray,
deviceMac: ByteArray,
deviceName: String,
crypto: PremoCrypto,
logger: PremoLogger
): RegistrationAttempt = withContext(Dispatchers.IO) {
try {
logger.log("REGIST", "▶ $name")
val bodyText = buildString {
append("Client-Type: Phone\r\n") // PSP type uses "Phone" client type
append("Client-Id: ${deviceId.joinToString("") { "%02x".format(it) }}\r\n")
append("Client-Mac: ${deviceMac.joinToString("") { "%02x".format(it) }}\r\n")
append("Client-Nickname: $deviceName\r\n")
}
val PhoneTypeBELonglongStrategy class · kotlin · L659-L676 (18 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/RegistrationStrategyImpl.kt
class PhoneTypeBELonglongStrategy : RegistrationStrategy {
override val name = "Phone Type BE Longlong"
override val description = "Phone Type (1) with PIN as 8-byte BE longlong"
override val isEnabled = false
override suspend fun attemptRegistration(
ps3Ip: String,
pin: String,
deviceId: ByteArray,
deviceMac: ByteArray,
deviceName: String,
crypto: PremoCrypto,
logger: PremoLogger
): RegistrationAttempt = withContext(Dispatchers.IO) {
logger.log("REGIST", "▶ $name (disabled)")
RegistrationAttempt(name, false, error = "Disabled - already tested and failed")
}
}PhoneTypeLELonglongStrategy class · kotlin · L683-L700 (18 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/RegistrationStrategyImpl.kt
class PhoneTypeLELonglongStrategy : RegistrationStrategy {
override val name = "Phone Type LE Longlong"
override val description = "Phone Type (1) with PIN as 8-byte LE longlong"
override val isEnabled = false
override suspend fun attemptRegistration(
ps3Ip: String,
pin: String,
deviceId: ByteArray,
deviceMac: ByteArray,
deviceName: String,
crypto: PremoCrypto,
logger: PremoLogger
): RegistrationAttempt = withContext(Dispatchers.IO) {
logger.log("REGIST", "▶ $name (disabled)")
RegistrationAttempt(name, false, error = "Disabled - already tested and failed")
}
}RegistrationStrategies class · kotlin · L58-L123 (66 LOC)composeApp/src/desktopMain/kotlin/com/my/psoldremoteplay/RegistrationStrategy.kt
object RegistrationStrategies {
/**
* Strategy 2: Phone Type (1) with PS4 Formula
* IV Context: PIN as 4-byte BE uint + 4 zeros
* STATUS: TESTED - FAILED (403 Forbidden)
*
* Hypothesis: PS4 registration uses similar pattern, worth testing
* Result: Does not match PS3 registration encryption
*/
val phoneTypePS4Formula = PhoneTypePS4FormulaStrategy()
/**
* Strategy 3: PC Type (2) with PS4 Formula
* IV Context: PIN as 4-byte BE uint + 4 zeros
* Key Material: PIN as 4-byte BE + 12 zeros (PIN-derived)
* STATUS: NEW - From VAIO DLL Analysis
*
* Insight: VRPSDK.dll analysis shows PC type uses PIN-derived key material,
* unlike Phone type which uses random material. Different body structure.
*
* Reference: research/pupps3/ghidra_findings/22_VAIO_DLL_ANALYSIS.md page 312
*/
val pcTypePS4Formula = PCTypePS4FormulaStrategy()
/**
* Strategy 4: Phone Type with 8-Byte BE Longlong
* IIosDependencies class · kotlin · L14-L69 (56 LOC)composeApp/src/iosMain/kotlin/com/my/psoldremoteplay/di/IosDependencies.kt
class IosDependencies : PlatformDependencies {
override val logger: PremoLogger = object : PremoLogger {
override fun log(tag: String, message: String) {
println("[$tag] $message")
}
override fun error(tag: String, message: String, throwable: Throwable?) {
System.err.println("[ERROR:$tag] $message")
throwable?.printStackTrace(System.err)
}
}
override val crypto: PremoCrypto = object : PremoCrypto {
override fun aesEncrypt(data: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
error("iOS crypto not implemented yet")
}
override fun aesDecrypt(data: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
error("iOS crypto not implemented yet")
}
override fun base64Encode(data: ByteArray): String {
error("iOS crypto not implemented yet")
}
override fun base64Decode(data: String): ByteArray {
error("iOS IOSPlatform class · kotlin · L5-L7 (3 LOC)composeApp/src/iosMain/kotlin/com/my/psoldremoteplay/Platform.ios.kt
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
StubAudioDecoder class · kotlin · L6-L11 (6 LOC)core/audio/src/commonMain/kotlin/com/my/psremoteplay/core/audio/stubs/StubAudioDecoder.kt
class StubAudioDecoder : AudioDecoder {
override suspend fun start(sampleRate: Int, channels: Int, codecName: String) {}
override suspend fun decode(data: ByteArray): AudioBuffer? = null
override suspend fun flush() {}
override suspend fun stop() {}
}StubAudioRenderer class · kotlin · L7-L19 (13 LOC)core/audio/src/commonMain/kotlin/com/my/psremoteplay/core/audio/stubs/StubAudioRenderer.kt
class StubAudioRenderer(private val logger: Logger) : AudioRenderer {
override suspend fun start() {
logger.log("AUDIO", "Audio renderer started")
}
override suspend fun onAudioBuffer(buffer: AudioBuffer) {
logger.log("AUDIO", "Audio buffer: ${buffer.samples.size} bytes @ ${buffer.sampleRate}Hz")
}
override suspend fun stop() {
logger.log("AUDIO", "Audio renderer stopped")
}
}ControllerButtons class · kotlin · L13-L31 (19 LOC)core/model/src/commonMain/kotlin/com/my/psremoteplay/core/model/input/ControllerState.kt
object ControllerButtons {
const val CROSS = 0x0001
const val CIRCLE = 0x0002
const val SQUARE = 0x0004
const val TRIANGLE = 0x0008
const val L1 = 0x0010
const val R1 = 0x0020
const val L2 = 0x0040
const val R2 = 0x0080
const val SELECT = 0x0100
const val START = 0x0200
const val L3 = 0x0400
const val R3 = 0x0800
const val UP = 0x1000
const val DOWN = 0x2000
const val LEFT = 0x4000
const val RIGHT = 0x8000
const val PS = 0x10000
}StubControllerInput class · kotlin · L3-L7 (5 LOC)core/model/src/commonMain/kotlin/com/my/psremoteplay/core/model/input/StubControllerInput.kt
class StubControllerInput : ControllerInputSender {
override suspend fun connect(params: Map<String, String>) {}
override suspend fun sendState(state: ControllerState) {}
override suspend fun disconnect() {}
}PassthroughUpscaler class · kotlin · L5-L7 (3 LOC)core/upscale/src/commonMain/kotlin/com/my/psremoteplay/core/upscale/PassthroughUpscaler.kt
class PassthroughUpscaler : UpscaleFilter {
override suspend fun process(frame: VideoFrame): VideoFrame = frame
}AndroidVideoRenderer class · kotlin · L13-L53 (41 LOC)core/video/src/androidMain/kotlin/com/my/psremoteplay/core/video/AndroidVideoRenderer.kt
class AndroidVideoRenderer(private val logger: Logger) : VideoRenderer {
private val _currentFrame = MutableStateFlow<ImageBitmap?>(null)
override val currentFrame: StateFlow<ImageBitmap?> = _currentFrame.asStateFlow()
private var packetCount = 0
override suspend fun start() {
logger.log("VIDEO", "[ANDROID] Video renderer initialized")
}
override suspend fun onStreamPacket(header: ByteArray, payload: ByteArray, isEncrypted: Boolean) =
withContext(Dispatchers.IO) {
packetCount++
// Detect JPEG data (SOI marker: 0xFF 0xD8)
if (payload.size >= 2 &&
payload[0] == 0xFF.toByte() &&
payload[1] == 0xD8.toByte()
) {
try {
val bitmap = BitmapFactory.decodeByteArray(payload, 0, payload.size)
if (bitmap != null) {
_currentFrame.value = bitmap.asImageBitmap()
}
}LoggingVideoRenderer class · kotlin · L10-L31 (22 LOC)core/video/src/commonMain/kotlin/com/my/psremoteplay/core/video/stubs/LoggingVideoRenderer.kt
class LoggingVideoRenderer(private val logger: Logger) : VideoRenderer {
private var packetCount = 0
private val _currentFrame = MutableStateFlow<ImageBitmap?>(null)
override val currentFrame: StateFlow<ImageBitmap?> = _currentFrame.asStateFlow()
override suspend fun start() {
logger.log("VIDEO", "Video renderer started")
}
override suspend fun onStreamPacket(header: ByteArray, payload: ByteArray, isEncrypted: Boolean) {
packetCount++
if (packetCount % 30 == 0) {
logger.log("VIDEO", "Received $packetCount packets, last payload: ${payload.size} bytes")
}
}
override suspend fun stop() {
logger.log("VIDEO", "Video renderer stopped ($packetCount total packets)")
packetCount = 0
_currentFrame.value = null
}
}StubVideoDecoder class · kotlin · L6-L11 (6 LOC)core/video/src/commonMain/kotlin/com/my/psremoteplay/core/video/stubs/StubVideoDecoder.kt
class StubVideoDecoder : VideoDecoder {
override suspend fun start(width: Int, height: Int, codecName: String) {}
override suspend fun decode(nalData: ByteArray): VideoFrame? = null
override suspend fun flush() {}
override suspend fun stop() {}
}Repobility · code-quality intelligence platform · https://repobility.com
JvmVideoRenderer class · kotlin · L13-L52 (40 LOC)core/video/src/desktopMain/kotlin/com/my/psremoteplay/core/video/JvmVideoRenderer.kt
class JvmVideoRenderer(private val logger: Logger) : VideoRenderer {
private val _currentFrame = MutableStateFlow<ImageBitmap?>(null)
override val currentFrame: StateFlow<ImageBitmap?> = _currentFrame.asStateFlow()
private var packetCount = 0
override suspend fun start() = withContext(Dispatchers.IO) {
logger.log("VIDEO", "[DESKTOP] Video renderer initialized")
}
override suspend fun onStreamPacket(header: ByteArray, payload: ByteArray, isEncrypted: Boolean) =
withContext(Dispatchers.IO) {
packetCount++
// Detect JPEG data (SOI marker: 0xFF 0xD8)
if (payload.size >= 2 &&
payload[0] == 0xFF.toByte() &&
payload[1] == 0xD8.toByte()
) {
try {
val skiaImage = SkiaImage.makeFromEncoded(payload)
_currentFrame.value = skiaImage.toComposeImageBitmap()
} catch (e: Exception) {
if (paNalUnitProtocol class · kotlin · L12-L86 (75 LOC)feature/ps2/protocol/src/commonMain/kotlin/com/my/psremoteplay/feature/ps2/protocol/NalUnitProtocol.kt
object NalUnitProtocol {
const val HEADER_SIZE = 9
const val FLAG_KEYFRAME: Int = 0x01
const val FLAG_SPS: Int = 0x02
const val FLAG_PPS: Int = 0x04
private const val NAL_TYPE_IDR = 5
private const val NAL_TYPE_SPS = 7
private const val NAL_TYPE_PPS = 8
fun wrapNalUnit(nalUnit: ByteArray, sequenceNumber: Int, timestampMs: Long): ByteArray {
val flags = detectFlags(nalUnit)
val packet = ByteArray(HEADER_SIZE + nalUnit.size)
packet[0] = (sequenceNumber ushr 24 and 0xFF).toByte()
packet[1] = (sequenceNumber ushr 16 and 0xFF).toByte()
packet[2] = (sequenceNumber ushr 8 and 0xFF).toByte()
packet[3] = (sequenceNumber and 0xFF).toByte()
val ts = timestampMs.toInt()
packet[4] = (ts ushr 24 and 0xFF).toByte()
packet[5] = (ts ushr 16 and 0xFF).toByte()
packet[6] = (ts ushr 8 and 0xFF).toByte()
packet[7] = (ts and 0xFF).toByte()
packet[8] = flags.toByte()
Ps2Protocol class · kotlin · L5-L105 (101 LOC)feature/ps2/protocol/src/commonMain/kotlin/com/my/psremoteplay/feature/ps2/protocol/Ps2Protocol.kt
object Ps2Protocol {
const val DEFAULT_PORT = 9295
// Message types
const val VIDEO_FRAME: Byte = 0x01
const val CONTROLLER_STATE: Byte = 0x03
const val SERVER_INFO: Byte = 0x10
const val STREAM_STATS: Byte = 0x04
const val CLIENT_HELLO: Byte = 0x20
const val CONTROLLER_PAYLOAD_SIZE = 14
/**
* Builds a wire frame: 4-byte BE length (type + payload size) + type byte + payload.
*/
fun buildFrame(type: Byte, payload: ByteArray): ByteArray {
val frameLength = 1 + payload.size // type + payload
val frame = ByteArray(4 + frameLength)
// 4-byte big-endian length
frame[0] = (frameLength shr 24 and 0xFF).toByte()
frame[1] = (frameLength shr 16 and 0xFF).toByte()
frame[2] = (frameLength shr 8 and 0xFF).toByte()
frame[3] = (frameLength and 0xFF).toByte()
// type
frame[4] = type
// payload
payload.copyInto(frame, destinationOffset = 5)
return frame
page 1 / 8next ›