2021-04-26 12:10:08 +02:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
2021-04-26 12:32:21 +02:00
# This working version corrected with the help of sickle :
# https://discord.com/channels/642647820683444236/663664210525290507/836029253593858060
# Corrected script by Sickle : http://psx.arthus.net/code/rawdog.py
# Sickle - 26/04/2021 :
# " Ooh, you were like frustratingly close dude! Few tiny issues:
# - first of your 3 rolling buffers was bugged (other 2 were spot on)
# - waiting too long between commands at points, unirom timed out
# - var i was missing the i += chunkSize so we were stuck in a loop there (e.g. tried to send a second chonk)
# - exit was gummed up with the main logic being in a while True: "
# As suggested:
# - Removed while True: loop
# - moved rolling buffer loops to WaitForResponse()
# - reduced sleeps
# - inc var i with chunkSize
2021-04-27 17:12:00 +02:00
#TODO
# - reduce/remove sleeps
# - keep listening!
2021-04-26 12:10:08 +02:00
import sys
2021-04-27 17:12:00 +02:00
import os
2021-04-26 12:10:08 +02:00
import serial
import time
2021-04-27 17:12:00 +02:00
import calendar
2021-04-26 12:10:08 +02:00
import math
2021-04-27 17:12:00 +02:00
import signal
2021-04-28 20:32:46 +02:00
DEBUG = 1
2021-04-27 17:12:00 +02:00
# Working directory
cwd = os . getcwd ( )
levelsFolder = cwd + os . sep + os . sep
# Receive commands from PSX
# ~ Run = True
Listen = 1
uniDebugMode = 0
Command = " "
memAddr = " "
flagAddr = " "
loadFile = " "
2021-04-28 20:32:46 +02:00
levelId = " "
2021-04-27 17:12:00 +02:00
# One byte
uno = int ( 1 ) . to_bytes ( 1 , byteorder = ' little ' , signed = False )
data = 0
# ~ dataSize = 0
# Serial connection setup
2021-04-26 12:10:08 +02:00
ser = serial . Serial ( ' /dev/ttyUSB0 ' )
2021-04-27 17:12:00 +02:00
# Unirom can do 115200 and 510000 ( https://github.com/JonathanDotCel/NOTPSXSerial/blob/bce29e87cb858769fe60eb34d8eb123f9f36c8db/NOTPSXSERIAL.CS#L842 )
2021-04-26 12:10:08 +02:00
ser . baudrate = ' 115200 '
2021-04-27 17:12:00 +02:00
# checkSum is the checkSum for the full data
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
checkSum = 0
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
# If set, it means the data transfer has been initiated
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
Transfer = 0
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
# Delay between write operations. These seem to be needed for the connection not to hang.
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
sleepTime = 0.08 # Seems like safe minimum
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
def sig_interrupt_handler ( signal , frame ) :
global Run
Run = False
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
def setDEBG ( ) :
global sleepTime , ser , uniDebugMode
if DEBUG :
print ( " Sending DEBG command... " )
ser . write ( bytes ( ' DEBG ' , ' ascii ' ) )
time . sleep ( sleepTime )
# Empty in waiting buffer
ser . reset_input_buffer ( )
time . sleep ( sleepTime )
uniDebugMode = 1
2021-04-26 12:10:08 +02:00
2021-04-26 12:32:21 +02:00
def WaitForResponse ( expectedAnswer ) :
2021-04-27 17:12:00 +02:00
# Get incoming data from the serial port in a rolling buffer
# when the content of the buffer corresponds to 'expectedAnswer', returns True
global DEBUG
2021-04-26 12:32:21 +02:00
responseBuffer = " "
2021-04-27 17:12:00 +02:00
success = False
2021-04-26 12:32:21 +02:00
while True :
2021-04-27 17:12:00 +02:00
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
print ( " Waiting for data in serial input buffer. " )
2021-04-27 17:12:00 +02:00
# If data in serial's incoming buffer
2021-04-26 12:32:21 +02:00
if ser . in_waiting :
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
print ( " Brace yourself, data is coming... " )
2021-04-27 17:12:00 +02:00
# Read 1 byte
byteValue = ser . read ( 1 )
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# Make sure byte value is < 128 so that it can be decoded to ascii
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
if byteValue [ 0 ] < 128 :
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
responseBuffer + = byteValue . decode ( ' ascii ' )
2021-04-26 12:32:21 +02:00
else :
responseBuffer + = ' . '
2021-04-27 17:12:00 +02:00
# Always keep response buffer 4 chars long
2021-04-26 12:32:21 +02:00
if len ( responseBuffer ) > 4 :
2021-04-27 17:12:00 +02:00
# Remove first char in buffer
2021-04-26 12:32:21 +02:00
responseBuffer = responseBuffer [ 1 : ]
2021-04-27 17:12:00 +02:00
# If response is ERR!, checksum check does not check, so check it again
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
if responseBuffer == " ERR! " :
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
print ( " Checksum error ! " )
2021-04-27 17:12:00 +02:00
success = False
2021-04-26 12:32:21 +02:00
break
2021-04-27 17:12:00 +02:00
# When expected response shows up, break from the while loop
if responseBuffer == expectedAnswer :
success = True
break
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
print ( " Got : " + responseBuffer )
2021-04-26 12:10:08 +02:00
2021-04-27 18:01:41 +02:00
responseBuffer = " "
2021-04-27 17:12:00 +02:00
return success
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
def CalculateChecksum ( inBytes , skipFirstSector = False ) :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
returnVal = 0 ;
2021-04-26 12:10:08 +02:00
i = 0
2021-04-27 17:12:00 +02:00
if skipFirstSector :
i = 2048
while i < len ( inBytes ) :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
returnVal + = inBytes [ i ] ;
2021-04-26 12:10:08 +02:00
i + = 1
2021-04-27 17:12:00 +02:00
return returnVal ;
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
def WriteBytes ( inData ) :
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
if DEBUG :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
print ( " Preparing to write bytes... " )
2021-04-26 12:32:21 +02:00
2021-04-27 18:01:41 +02:00
# The data needs to be split in 2K chunks
chunkSize = 2048
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# BEGIN WHILE DATA
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
i = 0
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
while i < len ( inData ) :
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# BEGIN WHILE TRUE
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
while True :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
# BEGIN TRY/EXCEPT
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
try :
# Calculate number of 2K chunks we're about to send
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
numChunk = math . ceil ( len ( inData ) / chunkSize )
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# Calculate current chunk
currentChunk = math . ceil ( ( i + 1 ) / chunkSize )
if DEBUG :
2021-05-02 12:52:09 +02:00
print ( str ( numChunk + 1 - currentChunk ) + " chunks of " + str ( chunkSize ) + " bytes to send " )
2021-04-27 17:12:00 +02:00
# Avoid going out of range
if ( i + chunkSize ) > len ( inData ) :
chunkSize = len ( inData ) - i
print ( " Writing chunk " + str ( currentChunk ) + " of " + str ( numChunk ) )
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# ~ ser.write(inData)
chunkChecksum = 0
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# Send inData in 2048B chunks
for byte in range ( chunkSize ) :
# Send byte
2021-05-02 12:52:09 +02:00
if DEBUG > 1 :
print ( " Writing " + str ( inData [ i + byte ] . to_bytes ( 1 , byteorder = ' little ' , signed = False ) ) + " to serial... " )
2021-04-27 17:12:00 +02:00
ser . write ( inData [ i + byte ] . to_bytes ( 1 , byteorder = ' little ' , signed = False ) )
# Calculate chunk checksum
chunkChecksum + = inData [ i + byte ]
time . sleep ( sleepTime )
if DEBUG :
print ( " Chunk cheksum : " + str ( chunkChecksum ) )
# Wait for output buffer to be empty
# REMOVE ? Is this needed ?
while ser . out_waiting :
print ( " * " )
wait + = 1
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
time . sleep ( sleepTime )
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# Wait for unirom to request the checksum
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
2021-04-27 17:12:00 +02:00
print ( " Chunk " + str ( currentChunk ) + " waiting for unirom to request checksum (CHEK)... " )
WaitForResponse ( " CHEK " )
# Send checksum
if DEBUG :
print ( " Sending checksum to unirom... " ) ;
# ~ chunkChecksum = 170
bytesChunkChecksum = chunkChecksum . to_bytes ( 4 , byteorder = ' little ' , signed = False )
ser . write ( bytesChunkChecksum )
# ~ time.sleep( sleepTime )
2021-04-26 12:10:08 +02:00
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
2021-04-27 17:12:00 +02:00
print ( " Waiting for unirom to request more data (MORE)... " )
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
# Wait for unirom to request MORE inData ( next chunk )
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
if not WaitForResponse ( " MORE " ) :
if DEBUG :
print ( " ERROR ! Retrying... " )
raise Exception ( )
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
if DEBUG :
print ( str ( currentChunk ) + " chunk sent with correct checksum. " )
# Increment i from chunkSize
i + = chunkSize
except Exception :
continue
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
# END TRY/EXCEPT
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
break
# END WHILE TRUE
numChunk = 0
# END WHILE DATA
def SendBin ( inData , memAddr ) :
global sleepTime
dataSize = len ( inData )
if DEBUG :
print ( " Data size : " + str ( dataSize ) )
# Prepare unirom for data reception - sent "SBIN" - received : "OKV2"
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
2021-04-27 17:12:00 +02:00
print ( " Sending SBIN command... " )
ser . write ( bytes ( ' SBIN ' , ' ascii ' ) )
time . sleep ( sleepTime )
# We're using unirom in debug mode, which means protocol version 2 is available
# Upgrade protocol - sent "UPV2" - received : "OKAY"
ser . write ( bytes ( ' UPV2 ' , ' ascii ' ) )
time . sleep ( sleepTime )
# Initialisation done, set flag
# ~ Init = 1
# From now on, we're using the rolling buffer
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
2021-04-27 17:12:00 +02:00
print ( " Waiting for OKAY... " )
WaitForResponse ( " OKAY " )
# Calculate data checkSum
checkSum = CalculateChecksum ( inData )
2021-04-27 18:01:41 +02:00
if DEBUG :
2021-04-27 17:12:00 +02:00
2021-04-27 18:01:41 +02:00
print ( " Data checkSum : " + str ( checkSum ) )
2021-04-27 17:12:00 +02:00
# Send memory address to load data to, size of data and checkSum
# Unirom expects unsigned longs ( 32bits ), byte endianness little
# Convert address from string to integer, then to ulong 32b
bytesAddr = int ( memAddr , 16 ) . to_bytes ( 4 , byteorder = ' little ' , signed = False )
# Write address to serial
ser . write ( bytesAddr )
time . sleep ( sleepTime )
# Convert and write int size to serial
bytesSize = dataSize . to_bytes ( 4 , byteorder = ' little ' , signed = False )
ser . write ( bytesSize )
time . sleep ( sleepTime )
# Convert and write int chekSum to serial
bytesChk = checkSum . to_bytes ( 4 , byteorder = ' little ' , signed = False )
ser . write ( bytesChk )
time . sleep ( sleepTime )
# Send dat data
WriteBytes ( inData )
def resetListener ( ) :
2021-05-02 12:52:09 +02:00
global checkSum , data , Listen , Transfer , dataSize , memAddr , loadFile , flagAddr , levelId
2021-04-27 17:12:00 +02:00
memAddr = " "
flagAddr = " "
loadFile = " "
checkSum = 0
data = 0
dataSize = 0
Transfer = 0
2021-05-02 12:52:09 +02:00
levelId = 0
2021-04-27 17:12:00 +02:00
Listen = 1
ser . reset_input_buffer ( )
ser . reset_output_buffer ( )
def main ( args ) :
while True :
2021-04-28 20:32:46 +02:00
global checkSum , data , Listen , Transfer , dataSize , memAddr , loadFile , flagAddr , levelId
2021-04-27 17:12:00 +02:00
# Flush serial buffers to avoid residual data
ser . reset_input_buffer ( )
ser . reset_output_buffer ( )
inputBuffer = " "
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
# Listen to incomming connections on serial
if Listen :
print ( " Listening for incoming data... " )
2021-04-26 12:10:08 +02:00
2021-04-27 18:01:41 +02:00
if DEBUG > 1 :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
print ( " memAddr : " + str ( memAddr ) + " - loadFile " + loadFile )
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
while True :
# If data on serial, fill buffer
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
while ser . in_waiting :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
inputBuffer + = ser . read ( ) . decode ( ' ascii ' )
if inputBuffer :
2021-04-26 12:10:08 +02:00
2021-04-27 18:01:41 +02:00
if DEBUG :
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
print ( " Incoming data : " + inputBuffer )
2021-04-26 12:10:08 +02:00
2021-04-28 20:32:46 +02:00
# parse command CMD:ARG1:ARG2(:ARGn)
2021-04-26 12:10:08 +02:00
2021-04-28 20:32:46 +02:00
argList = [ ]
2021-04-26 12:10:08 +02:00
2021-04-28 20:32:46 +02:00
argList = inputBuffer . split ( ' : ' )
# Send command
if argList [ 0 ] == " load " and len ( argList ) == 4 :
2021-04-26 12:10:08 +02:00
2021-04-28 20:32:46 +02:00
if len ( argList [ 1 ] ) < 8 or len ( argList [ 2 ] ) < 8 :
2021-04-27 17:12:00 +02:00
2021-04-28 20:32:46 +02:00
if DEBUG :
print ( " Wrong data format, aborting... " )
2021-04-27 17:12:00 +02:00
2021-04-28 20:32:46 +02:00
break
2021-04-27 17:12:00 +02:00
2021-04-28 20:32:46 +02:00
memAddr = argList [ 1 ]
flagAddr = argList [ 2 ]
loadFile = argList [ 3 ]
2021-04-27 17:12:00 +02:00
2021-04-28 20:32:46 +02:00
ser . reset_input_buffer ( )
inputBuffer = " "
if DEBUG > 1 :
print ( memAddr + " - " + flagAddr + " - " + loadFile )
Listen = 0
break
else :
ser . reset_input_buffer ( )
inputBuffer = " "
break
2021-04-27 17:12:00 +02:00
if memAddr and loadFile :
# Remove separator and ';1' at end of the string
fileClean = loadFile . split ( ' ; ' ) [ 0 ] [ 1 : ]
print ( " Received addresses and filename : " + memAddr + " - " + flagAddr + " - " + fileClean )
# TODO : replace with a proper level naming scheme
2021-04-27 18:01:41 +02:00
# right now, we're receiving currently loaded file
# so we have to switch manually here.
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
binFileName = " "
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
if fileClean == " level0.bin " :
2021-04-26 12:32:21 +02:00
2021-04-27 18:01:41 +02:00
binFileName = " Overlay.lvl1 "
2021-04-26 12:32:21 +02:00
2021-04-28 20:32:46 +02:00
levelId = 1
2021-04-27 17:12:00 +02:00
if fileClean == " level1.bin " :
2021-04-27 18:01:41 +02:00
binFileName = " Overlay.lvl0 "
2021-04-26 12:32:21 +02:00
2021-04-28 20:32:46 +02:00
levelId = 0
2021-04-27 17:12:00 +02:00
if DEBUG :
print (
2021-04-26 12:10:08 +02:00
2021-04-27 17:12:00 +02:00
" Load Data to : " + memAddr + " \n " +
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
" Reset flag at: " + flagAddr + " \n " +
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
" File : " + loadFile + " \n " +
2021-04-26 12:10:08 +02:00
2021-05-02 12:52:09 +02:00
" Bin : " + binFileName + " - ID : " + str ( levelId )
2021-04-27 17:12:00 +02:00
)
# Open file as binary if bin filename is defined
if binFileName :
binFile = open ( levelsFolder + binFileName , ' rb ' )
data = binFile . read ( )
Transfer = 1
else :
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
print ( " No filename provided, doing nothing " )
resetListener ( )
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
# If Init was set, initialize transfer and send data
2021-04-26 12:32:21 +02:00
2021-04-27 17:12:00 +02:00
if Transfer :
print ( " Initializing data transfer... " )
if not uniDebugMode :
# Set unirom to debugmode - sent : "DEBG" - received : "DEBGOKAY"
setDEBG ( )
# Send level data
SendBin ( data , memAddr )
# Set level changed flag
2021-05-02 12:52:09 +02:00
if DEBUG :
print ( " Sending value " + str ( levelId . to_bytes ( 1 , byteorder = ' little ' , signed = False ) ) + " to " + flagAddr )
time . sleep ( sleepTime )
2021-04-28 20:32:46 +02:00
SendBin ( levelId . to_bytes ( 1 , byteorder = ' little ' , signed = False ) , flagAddr )
2021-04-27 17:12:00 +02:00
# Reset everything
resetListener ( )
print ( " DONE! " )
2021-04-26 12:32:21 +02:00
2021-04-26 12:10:08 +02:00
return 0
if __name__ == ' __main__ ' :
import sys
sys . exit ( main ( sys . argv ) )