Installing PDX files on hardware from the command line with MacOS?

# software versions
$ ex -s +'%s/<[^>].\{-}>//ge' +'%s/\s\+//e' +'%norm J' +'g/^$/d' +%p +q! /System/Library/CoreServices/SystemVersion.plist | grep -E 'ProductName|ProductVersion' | sed 's/^[^ ]* //g' | sed 'N; s/\n/ /g'
macOS 12.6
$ sysctl -n machdep.cpu.brand_string
Apple M1 Max
$ cat "${HOME}/Developer/PlaydateSDK/VERSION.txt"
1.12.3

I am trying to build a demo based on the CAPI examples and install it on hardware from the command line with macOS. My understanding is that Windows has a pdutil install subcommand, but macOS does not. The following works if manually entered, but I am having trouble getting it to run in a script. Also, I need to press "A" on the Playdate hardware after unmounting.

# install
SLEEP_TIME="15"
PDUTIL_DEVICE="$(ls /dev/cu.usbmodemPD* | head -n 1)"
PRODUCT="$(cat Source/pdxinfo | grep name | cut -d "=" -f 2-).pdx"
make device
pdc Source "${PRODUCT}"
pdutil "${PDUTIL_DEVICE}" datadisk
# sleep code for script, but it is not reliable at all
printf "Waiting for ${SLEEP_TIME} seconds ... "
sleep "${SLEEP_TIME}"
echo "done!"
cp -r "${PRODUCT}" "/Volumes/PLAYDATE/Games/${PRODUCT}"
MOUNT_DEVICE="$(diskutil list | grep PLAYDATE | grep -oE '[^ ]+$')"
diskutil unmount "${MOUNT_DEVICE}"

# run
pdutil "${PDUTIL_DEVICE}" run "/Games/${PRODUCT}"

Is there a better approach?

3 Likes

An intersting challenge!

I managed to fix it :slight_smile:

Make sure pdc and pdutil are available on the PATH

WARNING: This script only copies changed files to the playdate for a nice data transfer speedup. However, it only looks at file size to detect change. So if you are tweaking parameters setting for example fuel amount from 500 to 510, the file size doesn't change and the file will not be updated.
See console output to verify which files were updated.
To send all files to the playdate, remove --size-only from this script.

This works on Mac 12.6:

until ls /dev/cu.usbmodemPD*
do
  echo "Playdate not found. Is it connected to USB and unlocked?"
  sleep 1
done

PDUTIL_DEVICE="$(ls /dev/cu.usbmodemPD* | head -n 1)"
echo "device $PDUTIL_DEVICE"


# Get Game name from pdxinfo and remove whitespace. pdutil cannot run files with whitespace in them
PRODUCT="$(cat Source/pdxinfo | grep name | cut -d "=" -f 2- | sed '/^$/d;s/[[:blank:]]//g').pdx"

echo "Compiling C projects and libs"
make device

echo "PDUTIL_DEVICE ${PDUTIL_DEVICE}"
echo "PRODUCT ${PRODUCT}"
pdc Source "${PRODUCT}"
pdutil "${PDUTIL_DEVICE}" datadisk

echo "Waiting for Data Disk to be mounted ... "
until [ -d /Volumes/PLAYDATE/GAMES ]
do
     sleep 1
done
echo "Game Dir mounted"

# Only copy files with changed file sizes. To copy all files, remove "--size-only"
rsync -zarv --size-only --prune-empty-dirs "${PRODUCT}" "/Volumes/PLAYDATE/Games/"
MOUNT_DEVICE="$(diskutil list | grep PLAYDATE | grep -oE '[^ ]+$')"
diskutil unmount "${MOUNT_DEVICE}"
diskutil eject PLAYDATE

echo "Waiting for USB Device to be mounted ... "
until ls "${PDUTIL_DEVICE}"
do
     sleep 1
done
echo "Usb Device Connected"

# run
echo "Running ${PRODUCT}"
pdutil "${PDUTIL_DEVICE}" run "/Games/${PRODUCT}"
2 Likes

If anyone can figure out a reliable way to find out which files were actually changed so that we can transfer just those files, please let me know!

Just guessing here; but I think it currently doesn't work because the playdate doesn't retain the last modified date of the files on your pc. Therefore, the rsync utility always thinks the files are changed.
this is because I also see mp3 files being re-send every time, which should not be modified by pdc

One solution is to use a local file to keep track of the last upload time. It looks something like this.

UPLOAD_TOKEN="Source/pdxinfo"
mkdir -p "${UPLOAD_TOKEN%/*}"
[[ -f "${UPLOAD_TOKEN}" ]] || touch "${UPLOAD_TOKEN}"
LAST_UPLOAD=$(date -r "${UPLOAD_TOKEN}" "+%Y%m%d%H%M")
FILES=$(find . -type f -name "*")
for FILE in ${FILES}
do
  UPDATED=$(date -r "${FILE}" "+%Y%m%d%H%M")
  if [ "${UPDATED}" -lt "${LAST_UPLOAD}" ]
  then
    echo "Uploading \"${FILE}\"..."
    # actually copy ${FILE} here
  fi
done
touch ${UPLOAD_TOKEN}

Naturally, the tracking file will need to have the date set to the past before a fresh reinstall.

DATE="195710041928.34"
UPLOAD_TOKEN="Source/pdxinfo"
mkdir -p "${UPLOAD_TOKEN%/*}"
touch -t "${DATE}" "${UPLOAD_TOKEN}"

EDIT: The upload script itself could be used as the tracking file if you do not want to modify any of the project files. Note that using one centralized upload script is not a solution that keeps track of upload times for individual projects. Also, here is a loop to generate test files.

OLD_DATE="196907161332.00"
NEW_DATE="226907161332.00"
for INDEX in {1..3}
do
  touch -t "${OLD_DATE}" "old_file_${INDEX}"
  touch -t "${NEW_DATE}" "new_file_${INDEX}"
done

Hey you seem to be pretty quick with bash scripting :slight_smile:

I'm afraid keeping a timestamp of that last sync isn't going to help, as the timestamps of all files gwet updated by pdc when it copies the files from the source to pdx.

See Pdc does not retain timestamps of files when copying them from source to pdx

In that case, it sounds like the current tooling simply does not support optimized uploads. New tooling would need to built. Maybe pdc could be called on file individually based on timestamps, but I would make Panic aware of the problem and then just work with the current limitations.