bashdosboxxdotool

DOSBox screen capture to specific file


I have a project where I am trying to 'stream' a DOS game, working in DOSBox on Ubuntu. The idea is to take screenshots of the screen, then upload these to a server. To take screenshots from DOSBox, I use xdotool to trigger the key combination "Ctrl+F5", which triggers a screen capture. The screenshot is then saved into the /capture folder, from which I can read the file.

The problem is that the screenshots are named progName_000.png, progName_001.png, ... I would prefer that a single file is overriden. Is it possible to achieve?

Currently I am using the horrendous bash code below:

WID=`xdotool search --limit 1 --name "DOSBox" 2>/dev/null`
while [ 1 ]; do
    fN=`ls ./DOSBox/capture/ | head -1`
    cp ./DOSBox/capture/$fN ./img.png
    rm ./DOSBox/capture/*

    xdotool key --window $WID Ctrl+F5
    sleep 0.10;
done

Every 100 ms, I read the capture file, take the first file that I encounter, copy it to ./img.png, and clear the capture folder, and take another screenshot. What is a better alternative?

(P.S: The above code is simplified; normally I copy the captured images to more than one image; img0.png and img1.png, so that while one is being written on, the other can be read, like page flipping. Anyway.)


Solution

  • I found a solution by tampering with the compiled DOSBox binary directly. I could have edited the source and simply built the project, but where is the fun in that?

    The binary file is in /usr/bin/dosbox. I opened gdb in a terminal as root and wrote

    gdb -write -silent /usr/bin/dosbox
    

    This is the solution for my DOSBox 0.74 binary, compiled for Ubuntu 14.04.

    First, the screenshot filenames include a 3-digit number, such as 'whatevs_000.png'. It was likely that the filename was constructed as a string using the sprintf function call, using a format string that included either %3d or %03d. A lucky search with objdump -s /usr/bin/dosbox | grep "\%03" revealed that the string "%s%c%s%03d%s" was indeed stored at 0x5fa9c7.

    After setting a hardware access watchpoint with awatch *0x5fa9c7, I runned the program in gdb (several continues might be required). Once the DOSBox boots, I used Ctrl+F5 to take a screenshot and trap the program at the instruction in the sprintf function. After several ups, I was in the main program flow (address 0x4a9949). disassing upwards, I discovered a for loop that had standard library calls to achieve directory listing. I detected the entry point of the loop (using two continue statements in the loop, which I verified later by checking the source code), and replaced the for-loop termination statement (0x4a9854) with an unconditional jump to the end of the loop (0x4a9908) as below:

    set write on
    
    set *(unsigned char*)0x4a9854 = 0xeb     Short jump to 0x4a98d4
    set *(unsigned char*)0x4a9855 = 0x7e
    
    set *(unsigned char*)0x4a9856 = 0x90     Fill the remaining
    set *(unsigned char*)0x4a9857 = 0x90     bytes of the previous
    set *(unsigned char*)0x4a9858 = 0x90     instruction with NOPs
    set *(unsigned char*)0x4a9859 = 0x90     because why not
    
    set *(unsigned char*)0x4a98d4 = 0xeb     Short jump to 0x4a9908
    set *(unsigned char*)0x4a98d5 = 0x32
    

    (I make two short jumps to jump over a longer range because this is the only opcode I could remember by heart and I am too sleepy to Google the rest)

    After this point, DOSBox always overrides filename_000.png, filename being the name of the binary being emulated.