3dcam-headers/loadlvl.py

625 lines
16 KiB
Python
Raw Normal View History

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))