openmsx-control using Python sockets in Windows

By NLeseul

Supporter (13)

NLeseul さんの画像

19-02-2022, 17:27

I don't suppose there are any Python scripts out there that use socket-based control of openMSX and work in a Windows environment, are there?

I've been poking at the socket for a while now, but haven't been able to get it to respond to anything, and I'm not sure whether I'm doing the SSPI handshake wrong, or if I'm just being dumb with socket programming in general.

I've been looking at the SSPI code from openmsx-debugger, but the Python SSPI API seems different enough from the native C++ API that it's not immediately obvious to me how to translate it.

So if there's a working example out there already, it sure would save me a lot of sads. Thanks. Smile

ログイン/登録して投稿

By mcolom

Champion (322)

mcolom さんの画像

19-02-2022, 19:24

I did a small script related to that, since some time ago I needed to get as many savestates as possible for my "lstate" program (https://github.com/mcolom/MSX_lstate). So my scripts connects to openMSX, powers on, throttle off, and saves the state inside a loop which runs many CAS files.

I can paste a piece of code, just as an example. It's for Linux using pipes. I know you asked specifically for Windows and sockets, but that's all I have.

    def command(proc, command):
        write(proc, f'{command}')
        return read(proc)

    openmsx_command = f'openmsx -control stdio -machine Sony_HB-20P -cassetteplayer {filename}'.replace('&', '\\&')
    proc = subprocess.Popen(openmsx_command,
                            shell=True,
                            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    read(proc)

    command(proc, 'set power on')
    command(proc, 'set throttle off')
    sleep(2)
    
    command(proc, f'savestate {name}')
    sleep(1)

By Manuel

Ascended (19685)

Manuel さんの画像

19-02-2022, 19:30

The Python based catapult might be worth a look. I don't know if it's working on Windows though. See https://github.com/openMSX/catapult let me know whether it works or not, please!

By NLeseul

Supporter (13)

NLeseul さんの画像

19-02-2022, 23:52

Yeah, Catapult works fine on Windows. It launches openMSX with stdio enabled and uses that to issue control commands, though, instead of sockets.

Using stdio for control is fine, of course; I could undoubtedly write a perfectly good utility using that method. It would be mildly more convenient to be able to attach to a running openMSX as needed, though, the way the debugger does.

By Manuel

Ascended (19685)

Manuel さんの画像

20-02-2022, 00:44

Ah, right, it uses stdio, I forgot... Well, I am not running Windows and as I never used SSPI APIs, I doubt I have a better idea than you have. So I hope someone else will step up.

By S0urceror

Master (220)

S0urceror さんの画像

20-02-2022, 08:44

I successfully made SSPI work from Typescript in my VSCode (Dezog) debugger for OpenMSX. You can check my GitHub for the code.
https://github.com/S0urceror/DeZog/blob/master/src/remotes/openmsx/openmsxremote.ts

By NLeseul

Supporter (13)

NLeseul さんの画像

20-02-2022, 20:15

Thanks! That at least confirmed for me that "Negotiate" is the correct package name to use and got me to poke at it a little more.

I finally thought to compare the network traffic my script is generating to that generated by the debugger. Turns out it was mostly just dumb on my part; the SSPI example I'd copied was using the Python "struct" module to format the message size, but without specifying an endianness for the size. Explicitly changing my code to encode the size of the SSPI messages as big-endian made it work fine, and pretty much with default options.

So, here's a barebones Windows-friendly Python script for instructing openMSX to do something:

import socket
import time

import sspi

if __name__ == '__main__':
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("localhost", 9957))
    print("Connected")
    
    sspi_auth = sspi.ClientAuth("Negotiate")
    auth_data = None
    while True:
        auth_status, auth_out = sspi_auth.authorize(auth_data)
        
        sock.send(len(auth_out[0].Buffer).to_bytes(4, 'big'))
        sock.send(auth_out[0].Buffer), len(auth_out[0].Buffer)
        
        if auth_status == 0:
            break
            
        auth_in_size_data = sock.recv(4)
        auth_in_size = int.from_bytes(auth_in_size_data, 'big')
        auth_data = sock.recv(auth_in_size)
    
    print("Authenticated")
    
    sock.sendall(b'<openmsx-control>')
    print(sock.recv(1024))
    
    sock.sendall(b'<command>set pause off</command>')
    print(sock.recv(1024))
    
    time.sleep(1)
    sock.sendall(b'<command>set pause on</command>')
    print(sock.recv(1024))
    
    sock.sendall(b'</openmsx-control>')
    
    sock.close()

By Manuel

Ascended (19685)

Manuel さんの画像

20-02-2022, 22:04

Thanks for that!