diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d99b51e1e9..227038f5cb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -850,6 +850,189 @@ jobs:
path: build/bundles
retention-days: 5
+ windows-appx:
+ name: Windows Store
+ needs:
+ - source
+ - windows
+
+ if: needs.source.outputs.is_tag == 'true'
+ runs-on: windows-latest
+
+ steps:
+ - name: Download source
+ uses: actions/download-artifact@v2
+ with:
+ name: internal-source
+
+ - name: Unpack source
+ shell: bash
+ run: |
+ tar -xf source.tar.gz --strip-components=1
+
+ - name: Download x86 build
+ uses: actions/download-artifact@v2
+ with:
+ name: openttd-windows-x86
+
+ - name: Download x64 build
+ uses: actions/download-artifact@v2
+ with:
+ name: openttd-windows-x64
+
+ - name: Download arm64 build
+ uses: actions/download-artifact@v2
+ with:
+ name: openttd-windows-arm64
+
+ - name: Unpack builds
+ shell: bash
+ run: |
+ mkdir builds
+ cd builds
+
+ function extract {
+ mkdir $1
+
+ # Extract the zip version of the release
+ unzip ../openttd-*-windows-$2.zip -d $1
+
+ # Remove the extraneous directory
+ mv $1/openttd-*-windows-$2/* $1/
+ rmdir $1/openttd-*-windows-$2
+
+ # Move the openttd.exe to the '{arch}-binaries' folder
+ mkdir $1-binaries
+ mv $1/openttd.exe $1-binaries/
+ }
+
+ extract x86 win32
+ extract x64 win64
+ extract arm64 arm64
+
+ # Use the "x86" folder as the source of the common binaries (lang files, etc) and remove the others
+ mv x86 common-binaries
+ rm -rf x64 arm64
+
+ - name: Install OpenGFX
+ shell: bash
+ run: |
+ mkdir -p builds/common-binaries/baseset
+ cd builds/common-binaries/baseset
+
+ echo "::group::Download OpenGFX"
+ curl -L https://cdn.openttd.org/opengfx-releases/7.1/opengfx-7.1-all.zip -o opengfx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenGFX"
+ unzip opengfx-all.zip
+ tar xf opengfx-*.tar
+ echo "::endgroup::"
+
+ rm -f opengfx-all.zip opengfx-*.tar
+
+ - name: Install OpenMSX
+ shell: bash
+ run: |
+ mkdir -p builds/common-binaries/baseset
+ cd builds/common-binaries/baseset
+
+ echo "::group::Download OpenMSX"
+ curl -L https://cdn.openttd.org/openmsx-releases/0.4.2/openmsx-0.4.2-all.zip -o openmsx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenGFX"
+ unzip openmsx-all.zip
+ tar xf openmsx-*.tar
+ echo "::endgroup::"
+
+ rm -f openmsx-all.zip openmsx-*.tar
+
+ - name: Install OpenSFX
+ shell: bash
+ run: |
+ mkdir -p builds/common-binaries/baseset/opensfx
+ cd builds/common-binaries/baseset/opensfx
+
+ echo "::group::Download OpenSFX"
+ curl -L https://cdn.openttd.org/opensfx-releases/1.0.3/opensfx-1.0.3-all.zip -o opensfx-all.zip
+ echo "::endgroup::"
+
+ echo "::group::Unpack OpenSFX"
+ unzip opensfx-all.zip
+ tar xf opensfx-*.tar
+ echo "::endgroup::"
+
+ rm -f opensfx-all.zip opensfx-*.tar
+
+ - name: Generate signing certificate
+ shell: cmd
+ run: |
+ cd builds
+
+ REM We need to provide a signed .appx to the Windows Store, so generate a certificate with password 'password'
+ call ..\os\windows\winstore\generate-key.bat "CN=78024DA8-4BE4-4C77-B12E-547BBF7359D2" password cert.pfx
+
+ - name: Generate assets
+ shell: cmd
+ run: |
+ cd os\windows\winstore
+ call generate-assets.bat
+
+ - name: Prepare manifests
+ shell: cmd
+ run: |
+ cd builds
+ mkdir manifests
+
+ REM Set the version environment variable
+ call ..\os\windows\winstore\set-version.bat x86-binaries\openttd.exe
+
+ call ..\os\windows\winstore\prepare-manifests.bat manifests "CN=78024DA8-4BE4-4C77-B12E-547BBF7359D2" "57420OpenTTDDevelopers.OpenTTDofficial"
+
+ - name: Prepare binaries
+ shell: bash
+ run: |
+ cd builds
+
+ # Copy the Windows Store assets
+ mkdir common-binaries/Assets
+ cp -R ../os/windows/winstore/assets-common/* common-binaries/Assets/
+
+ mkdir Assets
+ cp -R ../os/windows/winstore/assets/* Assets/
+
+ cp manifests/*.xml .
+
+ - name: Build
+ shell: cmd
+ run: |
+ REM Add the Windows SDK tools to the PATH
+
+ echo|set /p="SET VS_INSTALLDIR=" > _vspath.bat
+ vswhere -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath >> _vspath.bat
+ call _vspath.bat
+ call "%VS_INSTALLDIR%\Common7\Tools\VsDevCmd.bat"
+
+ REM Set the version environment variable
+ call os\windows\winstore\set-version.bat builds\x86-binaries\openttd.exe
+
+ cd builds
+ mkdir output
+
+ REM Build and sign the package
+ makeappx build /v /f PackagingLayout.xml /op output\ /bv %OTTD_VERSION% /pv %OTTD_VERSION% /ca
+ SignTool sign /fd sha256 /a /f cert.pfx /p password "output\OpenTTD.appxbundle"
+
+ - name: Store appx
+ uses: actions/upload-artifact@v2
+ with:
+ name: openttd-windows-store
+ path: |
+ builds/output/OpenTTD.appxbundle
+ builds/cert.pfx
+ retention-days: 5
+
upload:
name: Upload (AWS)
needs:
diff --git a/os/windows/winstore/generate-assets.bat b/os/windows/winstore/generate-assets.bat
new file mode 100644
index 0000000000..387fd57b85
--- /dev/null
+++ b/os/windows/winstore/generate-assets.bat
@@ -0,0 +1,2 @@
+@echo off
+powershell -File "%~dp0generate-assets.ps1"
diff --git a/os/windows/winstore/generate-assets.ps1 b/os/windows/winstore/generate-assets.ps1
new file mode 100644
index 0000000000..89c48e6ecf
--- /dev/null
+++ b/os/windows/winstore/generate-assets.ps1
@@ -0,0 +1,48 @@
+function ResizeImage() {
+ param([String]$sourcePath, [Int]$targetWidth, [Int]$targetHeight, [String]$targetPath)
+
+ Add-Type -AssemblyName "System.Drawing"
+
+ $img = [System.Drawing.Image]::FromFile($sourcePath)
+
+ $ratioX = $targetWidth / $img.Width;
+ $ratioY = $targetHeight / $img.Height;
+
+ $ratio = $ratioY
+
+ if ($ratioX -le $ratioY) {
+ $ratio = $ratioX
+ }
+
+ $newWidth = [int] ($img.Width * $ratio)
+ $newHeight = [int] ($img.Height * $ratio)
+
+ $resizedImage = New-Object System.Drawing.Bitmap($targetWidth, $targetHeight)
+ $graph = [System.Drawing.Graphics]::FromImage($resizedImage)
+ $graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
+
+ $graph.Clear([System.Drawing.Color]::Transparent)
+ $graph.DrawImage($img, $targetWidth / 2 - $newWidth / 2, $targetHeight / 2 - $newHeight / 2, $newWidth, $newHeight)
+
+ $resizedImage.Save($targetPath)
+ $resizedImage.Dispose()
+ $img.Dispose()
+}
+
+$logoPath = "..\..\..\media\openttd.2048.png"
+
+# Create the main image assets required for the Windows Store
+New-Item -Path "." -Name "assets" -ItemType "directory" -Force
+ResizeImage $logoPath 1240 1240 "assets\LargeTile.png"
+ResizeImage $logoPath 284 284 "assets\SmallTile.png"
+ResizeImage $logoPath 2480 1200 "assets\SplashScreen.png"
+ResizeImage $logoPath 176 176 "assets\Square44x44Logo.png"
+Copy-Item "assets\Square44x44Logo.png" -Destination "assets\Square44x44Logo.targetsize-44_altform-unplated.png"
+ResizeImage $logoPath 600 600 "assets\Square150x150Logo.png"
+Copy-Item "assets\Square150x150Logo.png" -Destination "assets\Square150x150Logo.targetsize-150_altform-unplated.png"
+ResizeImage $logoPath 200 200 "assets\StoreLogo.png"
+ResizeImage $logoPath 1240 600 "assets\Wide310x150Logo.png"
+
+# Copy the logo for the store for the common package
+New-Item -Path "." -Name "assets-common" -ItemType "directory" -Force
+Copy-Item "assets\StoreLogo.png" -Destination "assets-common\StoreLogo.png"
diff --git a/os/windows/winstore/generate-key.bat b/os/windows/winstore/generate-key.bat
new file mode 100644
index 0000000000..859d595d9c
--- /dev/null
+++ b/os/windows/winstore/generate-key.bat
@@ -0,0 +1,2 @@
+@echo off
+powershell -File "%~dp0generate-key.ps1" %1 %2 %3
diff --git a/os/windows/winstore/generate-key.ps1 b/os/windows/winstore/generate-key.ps1
new file mode 100644
index 0000000000..f59eb038e4
--- /dev/null
+++ b/os/windows/winstore/generate-key.ps1
@@ -0,0 +1,21 @@
+[CmdletBinding()]
+[Alias()]
+Param
+(
+ # Publisher ("CN=xyz")
+ [Parameter(Mandatory=$true, Position=0)]
+ $Publisher,
+
+ # Password
+ [Parameter(Mandatory=$true, Position=1)]
+ $PasswordParam,
+
+ # Filename
+ [Parameter(Mandatory=$true, Position=2)]
+ $OutputFilename
+)
+
+$cert = New-SelfSignedCertificate -Type Custom -Subject $Publisher -KeyUsage DigitalSignature -FriendlyName "OpenTTD signing certificate" -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")
+
+$password = ConvertTo-SecureString -String $PasswordParam -Force -AsPlainText
+Export-PfxCertificate -cert "Cert:\CurrentUser\My\$($cert.Thumbprint)" -FilePath $OutputFilename -Password $password
diff --git a/os/windows/winstore/manifests/AssetsPackage.appxmanifest b/os/windows/winstore/manifests/AssetsPackage.appxmanifest
new file mode 100644
index 0000000000..a0e8f29eda
--- /dev/null
+++ b/os/windows/winstore/manifests/AssetsPackage.appxmanifest
@@ -0,0 +1,68 @@
+
+
+
+
+ false
+ OpenTTD (official)
+ OpenTTD Developers
+ Assets\StoreLogoCommon.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/os/windows/winstore/manifests/Package.appxmanifest b/os/windows/winstore/manifests/Package.appxmanifest
new file mode 100644
index 0000000000..7eb4a5c2b7
--- /dev/null
+++ b/os/windows/winstore/manifests/Package.appxmanifest
@@ -0,0 +1,83 @@
+
+
+
+
+ OpenTTD (official)
+ OpenTTD Developers
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/os/windows/winstore/manifests/PackagingLayout.xml b/os/windows/winstore/manifests/PackagingLayout.xml
new file mode 100644
index 0000000000..dc16e45fa8
--- /dev/null
+++ b/os/windows/winstore/manifests/PackagingLayout.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/os/windows/winstore/prepare-manifests.bat b/os/windows/winstore/prepare-manifests.bat
new file mode 100644
index 0000000000..634b51e5ff
--- /dev/null
+++ b/os/windows/winstore/prepare-manifests.bat
@@ -0,0 +1,2 @@
+@echo off
+powershell -File "%~dp0prepare-manifests.ps1" %1 %2 %3 %OTTD_VERSION%
diff --git a/os/windows/winstore/prepare-manifests.ps1 b/os/windows/winstore/prepare-manifests.ps1
new file mode 100644
index 0000000000..4bd1f4cb2f
--- /dev/null
+++ b/os/windows/winstore/prepare-manifests.ps1
@@ -0,0 +1,42 @@
+[CmdletBinding()]
+[Alias()]
+Param
+(
+ # Output folder
+ [Parameter(Mandatory=$true, Position=0)]
+ $OutputFolder,
+
+ # Publisher ("CN=xyz")
+ [Parameter(Mandatory=$true, Position=1)]
+ $Publisher,
+
+ # IdentityName
+ [Parameter(Mandatory=$true, Position=2)]
+ $IdentityName,
+
+ # Version
+ [Parameter(Mandatory=$true, Position=3)]
+ $AppVersion
+)
+
+function Prepare-Manifest {
+ param (
+ $Architecture
+ )
+
+ (Get-Content "$($PSScriptRoot)\manifests\Package.appxmanifest").replace('$PUBLISHER$', $Publisher).replace('$IDENTITY_NAME$', $IdentityName).replace('$VERSION$', $AppVersion).replace('$ARCHITECTURE$', $Architecture) | Set-Content "$($OutputFolder)\Package-$($Architecture).appxmanifest"
+}
+
+# Prepare the application binary manifests
+Prepare-Manifest x86
+Prepare-Manifest x64
+Prepare-Manifest arm64
+
+# Prepare the assets package manifest
+(Get-Content "$($PSScriptRoot)\manifests\AssetsPackage.appxmanifest").replace('$PUBLISHER$', $Publisher).replace('$IDENTITY_NAME$', $IdentityName).replace('$VERSION$', $AppVersion) | Set-Content "$($OutputFolder)\AssetsPackage.appxmanifest"
+
+# Prepare the overall package manifest
+(Get-Content "$($PSScriptRoot)\manifests\Package.appxmanifest").replace('$PUBLISHER$', $Publisher).replace('$IDENTITY_NAME$', $IdentityName).replace('$VERSION$', $AppVersion).replace(' ProcessorArchitecture="$ARCHITECTURE$"', '') | Set-Content "$($OutputFolder)\Package.appxmanifest"
+
+# Copy the PackagingLayout XML file
+(Get-Content "$($PSScriptRoot)\manifests\PackagingLayout.xml") | Set-Content "$($OutputFolder)\PackagingLayout.xml"
diff --git a/os/windows/winstore/set-version.bat b/os/windows/winstore/set-version.bat
new file mode 100644
index 0000000000..f198661787
--- /dev/null
+++ b/os/windows/winstore/set-version.bat
@@ -0,0 +1,14 @@
+@echo off
+if [%1]==[] goto err
+
+powershell -File "%~dp0set-version.ps1" %1 > "%temp%\ottd-set-version.bat"
+if not errorlevel 0 goto err
+call "%temp%\ottd-set-version.bat"
+del /q "%temp%\ottd-set-version.bat"
+
+@rem Version number will now be in %OTTD_VERSION%
+
+goto :eof
+
+:err
+echo Please supply the path of openttd.exe as the argument to this batch file.
diff --git a/os/windows/winstore/set-version.ps1 b/os/windows/winstore/set-version.ps1
new file mode 100644
index 0000000000..ba048581e2
--- /dev/null
+++ b/os/windows/winstore/set-version.ps1
@@ -0,0 +1,23 @@
+[CmdletBinding()]
+[Alias()]
+Param
+(
+ # EXE path
+ [Parameter(Mandatory=$true, Position=0)]
+ $ExePath
+)
+
+try
+{
+ $versionInfo = (Get-Item "$ExePath").VersionInfo
+
+ # Generate the app version - the build number (MS calls it revision) is always 0 because the Windows Store requires that
+ $AppVersion = "$($versionInfo.FileMajorPart).$($versionInfo.FileMinorPart).$($versionInfo.FileBuildPart).0"
+
+ Write-Output "SET OTTD_VERSION=$($AppVersion)"
+}
+catch
+{
+ Write-Output "@ECHO Error retrieving EXE version - did you provide a path?"
+ exit 1
+}