From d50d783426cbc2ec3c4479091b76354bcc0ad594 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Sun, 18 Jun 2023 08:51:01 +0200 Subject: [PATCH] Early firmware update support --- .gitignore | 1 + README.rst | 10 ++++ bin/prepare-firmware.sh | 65 ++++++++++++++++++++++++ config.php.dist | 3 ++ www/.htaccess | 10 ++++ www/check.php | 109 ++++++++++++++++++++++++++++++++++------ 6 files changed, 183 insertions(+), 15 deletions(-) create mode 100755 bin/prepare-firmware.sh diff --git a/.gitignore b/.gitignore index fb2c680..8875713 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /config.php /data/profiles.sqlite3 /README.html +/www/firmware/*/update.img diff --git a/README.rst b/README.rst index 12d13d6..a71e12f 100644 --- a/README.rst +++ b/README.rst @@ -25,6 +25,16 @@ Setup 6. Setup the Apache web server and point the virtual host to the ``www/`` directory. +Firmware updates +================ +Firmware update diff files are needed. +It is possible to send out full firmware files, but they +will reset the user data. + +When you have such an update, put it into ``www/firmware/$version/update.img`` +and run ``./bin/prepare-firmware.sh www/firmware/$version/``. + + About ===== This server software was written by `Christian Weiske `_ diff --git a/bin/prepare-firmware.sh b/bin/prepare-firmware.sh new file mode 100755 index 0000000..de31b89 --- /dev/null +++ b/bin/prepare-firmware.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Prepare a firmware update file so it can be used for GameStick firmware updates +set -e + +if ! command -v xortool-xor 2>&1 > /dev/null; then + echo "Error: xortool-xor not found (pip install xortool)" + exit 3 +fi +if ! command -v xxd 2>&1 > /dev/null; then + echo "Error: xxd not found" + exit 3 +fi + +if [ $# -lt 1 ]; then + echo "Error: Firmware directory missing (www/firmware/x.y.z)" + exit 1 +fi + +if [ ! -d "$1" ]; then + echo "Error: Firmware directory does not exist" + exit 2 +fi + +fwDir=$1 +fwFile=$1/update.img + +if [ ! -f "$fwFile" ]; then + echo "Error: Firmware directory has no update.img" + exit 2 +fi + +cd "$fwDir" +rm chunk-* || true +rm tmp-* || true +split --bytes=102400 --numeric-suffixes --suffix-length=4 $(basename "$fwFile") tmp-part- + +#xor the files with the secret key +for partfile in tmp-part-*; do + #"91" (hex 5b, binary 1011011) is the xor key + xortool-xor -h 5b --no-newline -f "$partfile" > "tmp-xor-$partfile" +done +#"sign" the files by prefixing the binary sha1sum +for xorfile in tmp-xor-*; do + #"91" (hex 5b, binary 1011011) is the xor key + num=$(echo $xorfile | sed 's/tmp-xor-tmp-part-//') + sha1sum "$xorfile" |cut -d' ' -f 1 | xxd -r -p > chunk-$num + cat $xorfile >> chunk-$num +done + +rm tmp-* || true + +#remove leading zeros +for num in $(seq 0 999); do + if [ -f "chunk-0$num" ]; then + mv "chunk-0$num" "chunk-$num" + elif [ -f "chunk-00$num" ]; then + mv "chunk-00$num" "chunk-$num" + elif [ -f "chunk-000$num" ]; then + mv "chunk-000$num" "chunk-$num" + fi +done + +#those files may not contain newlines +ls -1 chunk-* | wc -l | tr -d '\n' > numchunks +stat --printf=%s update.img > filesize diff --git a/config.php.dist b/config.php.dist index 913d730..8205473 100644 --- a/config.php.dist +++ b/config.php.dist @@ -6,6 +6,9 @@ $GLOBALS['whitelistedHardwareIds'] = [ $GLOBALS['verificationCodePrefix'] = ''; +//offer a certain firmware version, even for downgrading +//$GLOBALS['firmwareVersion'] = '0.9.2071'; + //popular games. first in array means most popular $GLOBALS['popular'] = [ 'de.eiswuxe.blookid' diff --git a/www/.htaccess b/www/.htaccess index 7426b7e..77e123f 100644 --- a/www/.htaccess +++ b/www/.htaccess @@ -22,3 +22,13 @@ RewriteRule ^api/rest/user/game/(.*)/achievement/list/view.json;jsessionid=(.*)$ RewriteRule ^connect_check.php$ - [R=204,L] RewriteRule ^generate_204 - [R=204,L] + + +RewriteCond "%{QUERY_STRING}" "version=([^&]*)&i=-2$" +RewriteRule ^firmware/download$ /firmware/%1/filesize [END] + +RewriteCond "%{QUERY_STRING}" "version=([^&]*)&i=-1$" +RewriteRule ^firmware/download$ /firmware/%1/numchunks [END] + +RewriteCond "%{QUERY_STRING}" "version=([^&]*)&i=([0-9]+)$" +RewriteRule ^firmware/download$ /firmware/%1/chunk-%2 [END] diff --git a/www/check.php b/www/check.php index b228166..e3f4161 100644 --- a/www/check.php +++ b/www/check.php @@ -1,17 +1,96 @@ -{"available":false} + +$gsData = json_decode($_POST['v']); +if ($gsData === null) { + header('HTTP/1.0 400 Bad Request'); + header('Content-Type: text/plain'); + echo "POST data invalid\n"; + exit(1); +} + +if (!isset($gsData->hwid) + || !isset($gsData->major) + || !isset($gsData->minor) + || !isset($gsData->revision) + || !isset($gsData->platform) +) { + header('HTTP/1.0 400 Bad Request'); + header('Content-Type: text/plain'); + echo "POST data incomplete\n"; + exit(1); +} + +$gsVersion = $gsData->major . '.' . $gsData->minor . '.' . $gsData->revision; + +$requireUpdate = false; +if (isset($GLOBALS['firmwareVersion'])) { + $expectedVersion = $GLOBALS['firmwareVersion']; + if ($gsVersion != $expectedVersion) { + $requireUpdate = true; + } +} else { + $expectedVersion = '0.9.2071'; + if (version_compare($expectedVersion, $gsVersion, '>')) { + $requireUpdate = true; + } +} +if (!$requireUpdate) { + header('HTTP/1.0 200 OK'); + header('Content-type: application/json'); + echo '{"available":false}' . "\n"; + exit(0); +} + +list($major, $minor, $revision) = explode('.', $expectedVersion); + + +$firmwareDir = $rootDir . '/www/firmware/' . $expectedVersion; +$firmwareFile = $firmwareDir . '/update.img'; + +if (!file_exists($firmwareFile)) { + header('HTTP/1.0 200 OK'); + header('Content-type: application/json'); + header('X-Problem: Firmware file missing'); + echo '{"available":false}' . "\n"; + exit(0); +} + +$data = [ + 'available' => true, + 'major' => $major, + 'minor' => $minor, + 'revision' => $revision, + 'forced' => false, + 'name' => $expectedVersion, + 'description' => file_get_contents($firmwareDir . '/changelog.txt'), + 'timestamp' => filemtime($firmwareFile), + 'url' => 'http://update.gamestickservices.net/firmware/download?version=' . $expectedVersion, +]; + +$json = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + +header('HTTP/1.0 200 OK'); +header('Content-Type: application/json'); +echo $json . "\n"; -- 2.30.2