diff --git a/.github/workflows/build-uwp.yml b/.github/workflows/build-uwp.yml
new file mode 100644
index 000000000..f41d25473
--- /dev/null
+++ b/.github/workflows/build-uwp.yml
@@ -0,0 +1,230 @@
+name: UWP builds
+
+on:
+ workflow_dispatch:
+ workflow_call:
+ push:
+ branches:
+ - main
+ paths:
+ - CMakeLists.txt
+ - cmake/**
+ - src/**
+ - vendor/**
+ - .github/workflows/build-uwp.yml
+ pull_request:
+ paths:
+ - CMakeLists.txt
+ - cmake/**
+ - src/**
+ - vendor/**
+ - .github/workflows/build-uwp.yml
+
+jobs:
+ build-uwp:
+ name: Build UWP (${{ matrix.arch }})
+ runs-on: windows-2022
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - arch: x64
+ cmake_arch: x64
+ triplet: x64-uwp
+ - arch: arm64
+ cmake_arch: ARM64
+ triplet: arm64-uwp
+
+ env:
+ VCPKG_ROOT: ${{ github.workspace }}\vcpkg
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Checkout SDL2
+ uses: actions/checkout@v4
+ with:
+ repository: libsdl-org/SDL
+ ref: release-2.32.10
+ path: vendor/sdl2
+
+ - name: Build and install SDL2 for UWP
+ shell: pwsh
+ run: |
+ $sdlRoot = Join-Path $env:GITHUB_WORKSPACE 'sdl2-uwp'
+ $sdlBuild = Join-Path $env:GITHUB_WORKSPACE 'vendor\sdl2\build'
+
+ if (Test-Path $sdlRoot) {
+ Remove-Item $sdlRoot -Recurse -Force
+ }
+
+ cmake -S "$env:GITHUB_WORKSPACE\vendor\sdl2" -B $sdlBuild `
+ -G "Visual Studio 17 2022" `
+ -A ${{ matrix.cmake_arch }} `
+ -DCMAKE_SYSTEM_NAME=WindowsStore `
+ -DCMAKE_SYSTEM_VERSION=10.0 `
+ -DCMAKE_BUILD_TYPE=Release `
+ -DCMAKE_INSTALL_PREFIX="$sdlRoot" `
+ -DSDL_SHARED=OFF `
+ -DSDL_STATIC=ON `
+ -DSDL_TESTS=OFF `
+ -DSDL_STATIC_PIC=OFF
+
+ cmake --build $sdlBuild --config Release --parallel
+ cmake --install $sdlBuild --config Release
+
+ - name: Configure UWP build
+ shell: pwsh
+ run: |
+ $sdkRoot = "$env:ProgramFiles(x86)\Microsoft Visual Studio\2022\Enterprise"
+ if (-not (Test-Path $sdkRoot)) {
+ $sdkRoot = "$env:ProgramFiles(x86)\Microsoft Visual Studio\2022\BuildTools"
+ }
+ if (-not (Test-Path $sdkRoot)) {
+ $sdkRoot = "$env:ProgramFiles(x86)\Microsoft Visual Studio\2022\Community"
+ }
+
+ $sdlRoot = Join-Path $env:GITHUB_WORKSPACE 'sdl2-uwp'
+
+ cmake -S . -B build-uwp `
+ -G "Visual Studio 17 2022" `
+ -A ${{ matrix.cmake_arch }} `
+ -DCMAKE_SYSTEM_NAME=WindowsStore `
+ -DCMAKE_SYSTEM_VERSION=10.0 `
+ -DCMAKE_PREFIX_PATH="$sdlRoot" `
+ -DPLATFORM=uwp `
+ -DBACKEND=sdl2 `
+ -DCMAKE_BUILD_TYPE=Release `
+ -DWERROR=ON
+
+ - name: Build
+ shell: pwsh
+ run: |
+ cmake --build build-uwp --config Release --parallel
+
+ - name: Package Appx
+ shell: pwsh
+ run: |
+ $appRoot = Join-Path $env:GITHUB_WORKSPACE "build-uwp\package-${{ matrix.arch }}"
+ $assetsDir = Join-Path $appRoot 'Assets'
+ $packagePath = Join-Path $env:GITHUB_WORKSPACE "butterscotch-uwp-${{ matrix.arch }}.appx"
+ $manifestPath = Join-Path $appRoot 'AppxManifest.xml'
+ $exePath = Join-Path $env:GITHUB_WORKSPACE "build-uwp\Release\butterscotch.exe"
+ $sdlDll = Join-Path $env:GITHUB_WORKSPACE "sdl2-uwp\bin\SDL2.dll"
+
+ New-Item -ItemType Directory -Force -Path $appRoot, $assetsDir | Out-Null
+
+ Copy-Item $exePath (Join-Path $appRoot 'butterscotch.exe') -Force
+ if (Test-Path $sdlDll) {
+ Copy-Item $sdlDll (Join-Path $appRoot 'SDL2.dll') -Force
+ } else {
+ Write-Host 'No SDL2 DLL found in the install tree; assuming static linkage.'
+ }
+ Copy-Item "$env:GITHUB_WORKSPACE\vendor\gamecontrollerdb.txt" (Join-Path $appRoot 'gamecontrollerdb.txt') -Force
+
+ if (-not (Test-Path "$env:GITHUB_WORKSPACE\data.win")) {
+ New-Item -ItemType File -Force -Path (Join-Path $appRoot 'data.win') | Out-Null
+ } else {
+ Copy-Item "$env:GITHUB_WORKSPACE\data.win" (Join-Path $appRoot 'data.win') -Force
+ }
+
+ Add-Type -AssemblyName System.Drawing
+ foreach ($spec in @(
+ @{ Name = 'Square150x150Logo.png'; Width = 150; Height = 150 },
+ @{ Name = 'Square44x44Logo.png'; Width = 44; Height = 44 },
+ @{ Name = 'StoreLogo.png'; Width = 50; Height = 50 }
+ )) {
+ $bmp = New-Object System.Drawing.Bitmap($spec.Width, $spec.Height)
+ $g = [System.Drawing.Graphics]::FromImage($bmp)
+ $g.Clear([System.Drawing.Color]::FromArgb(255, 33, 33, 33))
+ $pen = New-Object System.Drawing.Pen([System.Drawing.Color]::FromArgb(255, 255, 255, 255)), 2
+ $g.DrawRectangle($pen, 1, 1, $spec.Width - 3, $spec.Height - 3)
+ $g.DrawLine($pen, 0, 0, $spec.Width, $spec.Height)
+ $g.DrawLine($pen, $spec.Width, 0, 0, $spec.Height)
+ $g.Dispose()
+ $bmp.Save((Join-Path $assetsDir $spec.Name), [System.Drawing.Imaging.ImageFormat]::Png)
+ $bmp.Dispose()
+ }
+
+ $manifest = @'
+
+
+
+
+ Butterscotch
+ Butterscotch
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ '@
+ $manifest | Set-Content -Path $manifestPath -Encoding UTF8
+
+ $makeAppx = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Recurse -Filter makeappx.exe -ErrorAction SilentlyContinue |
+ Sort-Object FullName -Descending |
+ Select-Object -First 1
+ if (-not $makeAppx) {
+ throw 'makeappx.exe not found in the Windows SDK.'
+ }
+
+ if (Test-Path $packagePath) {
+ Remove-Item $packagePath -Force
+ }
+
+ & $makeAppx.FullName pack /d $appRoot /p $packagePath /nc /o
+ if ($LASTEXITCODE -ne 0) {
+ throw 'makeappx failed.'
+ }
+
+ $cert = New-SelfSignedCertificate -CertStoreLocation Cert:\CurrentUser\My -Type Custom `
+ -Subject 'CN=ButterscotchDev' -KeySpec Signature -KeyExportPolicy Exportable `
+ -KeyAlgorithm RSA -KeyLength 2048 -HashAlgorithm SHA256 `
+ -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider'
+
+ $pfxPath = Join-Path $env:GITHUB_WORKSPACE "butterscotch-uwp-${{ matrix.arch }}.pfx"
+ Export-PfxCertificate -Cert $cert -FilePath $pfxPath -Password (ConvertTo-SecureString -String 'Butterscotch123!' -Force -AsPlainText) | Out-Null
+
+ $signtool = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |
+ Sort-Object FullName -Descending |
+ Select-Object -First 1
+ if (-not $signtool) {
+ throw 'signtool.exe not found in the Windows SDK.'
+ }
+
+ & $signtool.FullName sign /fd SHA256 /f $pfxPath /p 'Butterscotch123!' $packagePath
+ if ($LASTEXITCODE -ne 0) {
+ throw 'signtool failed.'
+ }
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: butterscotch-uwp-${{ matrix.arch }}
+ path: |
+ butterscotch-uwp-${{ matrix.arch }}.appx
+ butterscotch-uwp-${{ matrix.arch }}.pfx
+ build-uwp/Release/butterscotch.exe
+ build-uwp/Release/butterscotch.pdb
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8df58084b..1fde0daee 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -26,4 +26,7 @@ jobs:
uses: ./.github/workflows/build-other.yml
Compilers:
- uses: ./.github/workflows/build-old-compilers.yml
\ No newline at end of file
+ uses: ./.github/workflows/build-old-compilers.yml
+
+ UWP:
+ uses: ./.github/workflows/build-uwp.yml
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index fb3756c01..e5e775bfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ CMakeUserPresets.json
compat/config.mk
/.vs
*.log
+Packaging/ManualAppx/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8de80dba2..3dcc73d5e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,6 +71,12 @@ set(PLATFORM "cli" CACHE STRING "Platform backend")
set(BACKEND "" CACHE STRING "Desktop platform backend")
set(AUDIO_BACKEND "" CACHE STRING "Audio backend")
+if(PLATFORM STREQUAL "uwp")
+ if(BACKEND STREQUAL "" OR BACKEND STREQUAL "uwp" OR BACKEND STREQUAL "winrt")
+ set(BACKEND "sdl3" CACHE STRING "UWP uses SDL3-UWP backend" FORCE)
+ endif()
+endif()
+
if(PLATFORM STREQUAL "cli" AND BACKEND STREQUAL "")
message(FATAL_ERROR "You need to set a BACKEND!")
endif()
@@ -260,7 +266,7 @@ if(ENABLE_VM_EXCEPTIONS_LOGS)
add_compile_definitions(ENABLE_VM_EXCEPTIONS_LOGS)
endif()
-if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "switch")
+if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "switch" OR PLATFORM STREQUAL "uwp")
if(PLATFORM STREQUAL "vita")
# Normally, __VITA__ should already be defined, but for some reason,
# in my case, this didn't really work... so just in case, when we're
@@ -279,6 +285,13 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "swi
set(VM_TRACING_DEFAULT OFF)
set(VM_OPCODE_PROFILER_DEFAULT OFF)
set(VM_STUB_LOGS_DEFAULT OFF)
+ elseif(PLATFORM STREQUAL "uwp")
+ add_compile_definitions(PLATFORM_UWP)
+
+ set(VM_GML_PROFILER_DEFAULT ON)
+ set(VM_TRACING_DEFAULT ON)
+ set(VM_OPCODE_PROFILER_DEFAULT ON)
+ set(VM_STUB_LOGS_DEFAULT ON)
else()
set(VM_GML_PROFILER_DEFAULT ON)
set(VM_TRACING_DEFAULT ON)
@@ -305,6 +318,7 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "swi
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image)
if (MSVC)
target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/compat/getopt)
+ add_compile_definitions(NO_STRINGS_H)
add_compile_definitions(NO_STRTOK_R)
add_compile_definitions(NO_STRCASECMP)
endif()
@@ -341,7 +355,9 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "swi
# GLAD
add_library(glad STATIC vendor/glad/src/glad.c)
target_include_directories(glad PUBLIC vendor/glad/include)
- target_compile_options(glad PRIVATE -Wno-unused-but-set-variable)
+ if(NOT MSVC)
+ target_compile_options(glad PRIVATE -Wno-unused-but-set-variable)
+ endif()
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} glad)
endif()
if(NOT MSVC)
@@ -392,19 +408,62 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "swi
elseif(BACKEND STREQUAL "sdl2")
add_compile_definitions(USE_SDL2)
- find_package(PkgConfig REQUIRED)
- pkg_check_modules(SDL2 REQUIRED sdl2)
- target_include_directories(butterscotch PRIVATE ${SDL2_INCLUDE_DIRS})
- target_link_directories(butterscotch PRIVATE ${SDL2_LIBRARY_DIRS})
- set(BACKEND_LIBRARIES ${SDL2_LIBRARIES})
+ if(MSVC)
+ if(PLATFORM STREQUAL "uwp")
+ set(SDL2_UWP_ROOT "$ENV{SDL2_UWP_ROOT}" CACHE PATH "Path to the installed SDL2-UWP distribution")
+ if(EXISTS "${SDL2_UWP_ROOT}")
+ list(APPEND CMAKE_PREFIX_PATH "${SDL2_UWP_ROOT}")
+ endif()
+
+ find_package(SDL2 CONFIG REQUIRED
+ HINTS
+ "${SDL2_UWP_ROOT}"
+ "C:/SDL2-UWP"
+ PATH_SUFFIXES
+ lib/cmake/SDL2
+ cmake
+ )
+ else()
+ find_package(SDL2 CONFIG REQUIRED)
+ endif()
+ set(BACKEND_LIBRARIES SDL2::SDL2)
+ else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(SDL2 REQUIRED sdl2)
+ target_include_directories(butterscotch PRIVATE ${SDL2_INCLUDE_DIRS})
+ target_link_directories(butterscotch PRIVATE ${SDL2_LIBRARY_DIRS})
+ set(BACKEND_LIBRARIES ${SDL2_LIBRARIES})
+ endif()
elseif(BACKEND STREQUAL "sdl3")
add_compile_definitions(USE_SDL3)
- find_package(PkgConfig REQUIRED)
- pkg_check_modules(SDL3 REQUIRED sdl3)
- target_include_directories(butterscotch PRIVATE ${SDL3_INCLUDE_DIRS})
- target_link_directories(butterscotch PRIVATE ${SDL3_LIBRARY_DIRS})
- set(BACKEND_LIBRARIES ${SDL3_LIBRARIES})
+ if(PLATFORM STREQUAL "uwp")
+ set(SDL3_UWP_ROOT "C:/SDL-UWP" CACHE PATH "Path to the installed SDL-UWP distribution")
+ if(EXISTS "${SDL3_UWP_ROOT}")
+ list(APPEND CMAKE_PREFIX_PATH "${SDL3_UWP_ROOT}")
+ endif()
+
+ find_package(SDL3 CONFIG REQUIRED
+ HINTS
+ "${SDL3_UWP_ROOT}"
+ "C:/SDL-UWP"
+ PATH_SUFFIXES
+ lib/cmake/SDL3
+ cmake
+ )
+ set(BACKEND_LIBRARIES SDL3::SDL3)
+
+ elseif(MSVC)
+ find_package(SDL3 CONFIG REQUIRED)
+ set(BACKEND_LIBRARIES SDL3::SDL3)
+
+ else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(SDL3 REQUIRED sdl3)
+ target_include_directories(butterscotch PRIVATE ${SDL3_INCLUDE_DIRS})
+ target_link_directories(butterscotch PRIVATE ${SDL3_LIBRARY_DIRS})
+ set(BACKEND_LIBRARIES ${SDL3_LIBRARIES})
+ endif()
elseif(BACKEND STREQUAL "appkit")
add_compile_definitions(USE_APPKIT)
target_compile_definitions(butterscotch PRIVATE GL_SILENCE_DEPRECATION)
diff --git a/Packaging/Assets/Square150x150Logo.png b/Packaging/Assets/Square150x150Logo.png
new file mode 100644
index 000000000..518797aaa
Binary files /dev/null and b/Packaging/Assets/Square150x150Logo.png differ
diff --git a/Packaging/Assets/Square44x44Logo.png b/Packaging/Assets/Square44x44Logo.png
new file mode 100644
index 000000000..0ab661122
Binary files /dev/null and b/Packaging/Assets/Square44x44Logo.png differ
diff --git a/Packaging/Assets/StoreLogo.png b/Packaging/Assets/StoreLogo.png
new file mode 100644
index 000000000..946b86380
Binary files /dev/null and b/Packaging/Assets/StoreLogo.png differ
diff --git a/Packaging/Butterscotch.Package.wapproj b/Packaging/Butterscotch.Package.wapproj
new file mode 100644
index 000000000..cb9b2b44b
--- /dev/null
+++ b/Packaging/Butterscotch.Package.wapproj
@@ -0,0 +1,27 @@
+
+
+ {3B7A5DDE-03F0-4E72-8966-8CA3248F7D1E}
+ 10.0.26100.0
+ 10.0.19041.0
+ en-US
+ false
+ Windows.FullTrustApplication
+ AppContainer
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Packaging/Generate-UwpPackage.ps1 b/Packaging/Generate-UwpPackage.ps1
new file mode 100644
index 000000000..ff68935ce
--- /dev/null
+++ b/Packaging/Generate-UwpPackage.ps1
@@ -0,0 +1,217 @@
+param(
+ [string]$ProjectRoot = ((Resolve-Path (Join-Path $PSScriptRoot '..')).ProviderPath),
+ [string]$ExePath,
+ [string]$Sdl3Path,
+ [string]$AppName = 'Butterscotch',
+ [string]$Publisher = 'CN=ButterscotchDev',
+ [string]$Version = '1.0.0.0',
+ [string]$OutputRoot = ((Resolve-Path (Join-Path $PSScriptRoot '..')).ProviderPath + '\Packaging\ManualAppx'),
+ [switch]$SkipSign,
+ [string]$PfxPath
+)
+
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+
+function Resolve-RepoPath {
+ param(
+ [string]$Path,
+ [string]$ProjectRootOverride
+ )
+
+ if ([string]::IsNullOrWhiteSpace($Path)) {
+ return $null
+ }
+
+ if ([System.IO.Path]::IsPathRooted($Path)) {
+ return (Resolve-Path $Path -ErrorAction Stop).ProviderPath
+ }
+
+ $rootCandidates = @(
+ $ProjectRootOverride,
+ (Get-Location).Path
+ )
+
+ foreach ($root in $rootCandidates | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) {
+ $candidate = Join-Path $root $Path
+ if (Test-Path $candidate) {
+ return (Resolve-Path $candidate -ErrorAction Stop).ProviderPath
+ }
+ }
+
+ return (Resolve-Path $Path -ErrorAction Stop).ProviderPath
+}
+
+Write-Host "Project root: $ProjectRoot"
+Write-Host "Output root: $OutputRoot"
+
+if ([string]::IsNullOrWhiteSpace($ExePath)) {
+ $ExePath = Join-Path $ProjectRoot 'build-uwp\Debug\butterscotch.exe'
+} else {
+ $ExePath = Resolve-RepoPath -Path $ExePath -ProjectRootOverride $ProjectRoot
+}
+
+if ([string]::IsNullOrWhiteSpace($Sdl3Path)) {
+ $Sdl3Path = Join-Path $ProjectRoot 'build-uwp\Debug\SDL3.dll'
+} else {
+ $Sdl3Path = Resolve-RepoPath -Path $Sdl3Path -ProjectRootOverride $ProjectRoot
+}
+
+$dataWinPath = Join-Path $ProjectRoot 'data.win'
+$gameDbPath = Join-Path $ProjectRoot 'vendor\gamecontrollerdb.txt'
+
+foreach ($p in @($ExePath, $Sdl3Path, $dataWinPath, $gameDbPath)) {
+ if (-not (Test-Path $p)) {
+ throw "Required file not found: $p"
+ }
+}
+
+$stagingDir = Join-Path $OutputRoot 'Staging'
+$workingDir = Join-Path $stagingDir 'Butterscotch.appx'
+$assetsDir = Join-Path $workingDir 'Assets'
+$manifestPath = Join-Path $workingDir 'AppxManifest.xml'
+$packagePath = Join-Path $OutputRoot 'Butterscotch.appx'
+
+New-Item -ItemType Directory -Force -Path $OutputRoot | Out-Null
+New-Item -ItemType Directory -Force -Path $stagingDir | Out-Null
+New-Item -ItemType Directory -Force -Path $workingDir | Out-Null
+New-Item -ItemType Directory -Force -Path $assetsDir | Out-Null
+
+Write-Host "Creating placeholder app assets..."
+Add-Type -AssemblyName System.Drawing
+foreach ($spec in @(
+ @{ Name = 'Square150x150Logo.png'; Width = 150; Height = 150 },
+ @{ Name = 'Square44x44Logo.png'; Width = 44; Height = 44 },
+ @{ Name = 'StoreLogo.png'; Width = 50; Height = 50 }
+)) {
+ $bmp = New-Object System.Drawing.Bitmap($spec.Width, $spec.Height)
+ $g = [System.Drawing.Graphics]::FromImage($bmp)
+ $g.Clear([System.Drawing.Color]::FromArgb(255, 33, 33, 33))
+ $pen = New-Object System.Drawing.Pen([System.Drawing.Color]::FromArgb(255, 255, 255, 255)), 2
+ $g.DrawRectangle($pen, 1, 1, $spec.Width - 3, $spec.Height - 3)
+ $g.DrawLine($pen, 0, 0, $spec.Width, $spec.Height)
+ $g.DrawLine($pen, $spec.Width, 0, 0, $spec.Height)
+ $g.Dispose()
+ $bmp.Save((Join-Path $assetsDir $spec.Name), [System.Drawing.Imaging.ImageFormat]::Png)
+ $bmp.Dispose()
+}
+
+Write-Host "Copying runtime files..."
+Copy-Item $ExePath (Join-Path $workingDir 'butterscotch.exe') -Force
+Copy-Item $Sdl3Path (Join-Path $workingDir 'SDL3.dll') -Force
+Copy-Item $dataWinPath (Join-Path $workingDir 'data.win') -Force
+Copy-Item $gameDbPath (Join-Path $workingDir 'gamecontrollerdb.txt') -Force
+
+@'
+
+
+
+
+
+
+ Butterscotch
+ Butterscotch
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+'@ | Set-Content -Path $manifestPath -Encoding UTF8
+
+$makeAppx = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Recurse -Filter makeappx.exe -ErrorAction SilentlyContinue |
+ Sort-Object FullName -Descending |
+ Select-Object -First 1
+
+if (-not $makeAppx) {
+ throw "makeappx.exe not found. Install the Windows 10/11 SDK and ensure the kits bin directory is present."
+}
+
+$makeAppxPath = $makeAppx.FullName
+$packageOutputPath = Join-Path $OutputRoot 'Butterscotch.appx'
+
+if (Test-Path $packageOutputPath) {
+ Remove-Item $packageOutputPath -Recurse -Force -ErrorAction SilentlyContinue
+}
+
+Write-Host "Packaging with makeappx: $makeAppxPath"
+& $makeAppxPath pack /d $workingDir /p $packageOutputPath /nc /o
+if ($LASTEXITCODE -ne 0) {
+ throw "makeappx failed."
+}
+
+if (-not $SkipSign) {
+ $signtool = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |
+ Sort-Object FullName -Descending |
+ Select-Object -First 1
+
+ if (-not $signtool) {
+ throw "signtool.exe not found. Install the Windows SDK."
+ }
+
+ if (-not $PfxPath) {
+ $certPath = Join-Path $OutputRoot 'Butterscotch.pfx'
+ $certCerPath = Join-Path $OutputRoot 'Butterscotch.cer'
+ $makeCert = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Recurse -Filter makecert.exe -ErrorAction SilentlyContinue |
+ Sort-Object FullName -Descending |
+ Select-Object -First 1
+
+ if (-not $makeCert) {
+ throw "No certificate provided and no makecert.exe found. Pass -PfxPath or use -SkipSign to create an unsigned appx."
+ }
+
+ $certPassword = 'Butterscotch123!'
+ $pvkPath = Join-Path $OutputRoot 'Butterscotch.pvk'
+ $cerTempPath = Join-Path $OutputRoot 'Butterscotch-temp.cer'
+
+ & $makeCert.FullName -r -pe -n "CN=ButterscotchDev" -ss My -sr CurrentUser -a SHA256 -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv $pvkPath $cerTempPath
+ if ($LASTEXITCODE -ne 0) {
+ throw "makecert failed."
+ }
+
+ & 'C:\Program Files (x86)\Windows Kits\10\bin\x64\pvk2pfx.exe' -pvk $pvkPath -pi $certPassword -spc $cerTempPath -pfx $certPath -po $certPassword
+ if ($LASTEXITCODE -ne 0) {
+ throw "pvk2pfx failed."
+ }
+
+ Copy-Item $cerTempPath $certCerPath -Force
+ $PfxPath = $certPath
+ }
+
+ $signtoolPath = $signtool.FullName
+ & $signtoolPath sign /fd SHA256 /f $PfxPath /p 'Butterscotch123!' $packageOutputPath
+ if ($LASTEXITCODE -ne 0) {
+ throw "signtool failed."
+ }
+}
+
+Write-Host "Package created: $packageOutputPath"
diff --git a/Packaging/Package.appxmanifest b/Packaging/Package.appxmanifest
new file mode 100644
index 000000000..8f1f4e532
--- /dev/null
+++ b/Packaging/Package.appxmanifest
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+ Butterscotch
+ Butterscotch
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Packaging/obj/ARM64/Release/Butterscotch.Package.wapproj.CoreCompileInputs.cache b/Packaging/obj/ARM64/Release/Butterscotch.Package.wapproj.CoreCompileInputs.cache
new file mode 100644
index 000000000..c0f10963c
--- /dev/null
+++ b/Packaging/obj/ARM64/Release/Butterscotch.Package.wapproj.CoreCompileInputs.cache
@@ -0,0 +1 @@
+21fafa215c5059238da8534f830b3cb62bacf0b3744af02594840d5d79ebe1aa
diff --git a/Packaging/obj/ARM64/Release/Butterscotch.Package.wapproj.FileListAbsolute.txt b/Packaging/obj/ARM64/Release/Butterscotch.Package.wapproj.FileListAbsolute.txt
new file mode 100644
index 000000000..2a1acc9ec
--- /dev/null
+++ b/Packaging/obj/ARM64/Release/Butterscotch.Package.wapproj.FileListAbsolute.txt
@@ -0,0 +1 @@
+\\Mac\Home\Library\Mobile Documents\com~apple~CloudDocs\git\Butterscotch\Packaging\obj\ARM64\Release\Butterscotch.Package.wapproj.CoreCompileInputs.cache
diff --git a/Packaging/obj/Butterscotch.Package.wapproj.nuget.dgspec.json b/Packaging/obj/Butterscotch.Package.wapproj.nuget.dgspec.json
new file mode 100644
index 000000000..d90952365
--- /dev/null
+++ b/Packaging/obj/Butterscotch.Package.wapproj.nuget.dgspec.json
@@ -0,0 +1,92 @@
+{
+ "format": 1,
+ "restore": {
+ "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\Butterscotch.Package.wapproj": {}
+ },
+ "projects": {
+ "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\Butterscotch.Package.wapproj": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\Butterscotch.Package.wapproj",
+ "projectName": "Butterscotch.Package",
+ "projectPath": "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\Butterscotch.Package.wapproj",
+ "packagesPath": "C:\\Users\\emma\\.nuget\\packages\\",
+ "outputPath": "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\obj\\",
+ "projectStyle": "PackageReference",
+ "UsingMicrosoftNETSdk": false,
+ "fallbackFolders": [
+ "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
+ ],
+ "configFilePaths": [
+ "C:\\Users\\emma\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net451"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net451": {
+ "framework": "uap10.0.19041",
+ "targetAlias": "net451",
+ "projectReferences": {}
+ }
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.400"
+ },
+ "frameworks": {
+ "net451": {
+ "framework": "uap10.0.19041",
+ "targetAlias": "net451",
+ "imports": [
+ "netcoreapp3.1",
+ "netcoreapp3.0",
+ "netcoreapp2.0",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481",
+ "net11.0-windows10.0.26100",
+ "net461"
+ ],
+ "assetTargetFallback": true,
+ "warn": true
+ }
+ },
+ "runtimes": {
+ "win10-arm": {
+ "#import": []
+ },
+ "win10-arm-aot": {
+ "#import": []
+ },
+ "win10-arm64-aot": {
+ "#import": []
+ },
+ "win10-x64": {
+ "#import": []
+ },
+ "win10-x64-aot": {
+ "#import": []
+ },
+ "win10-x86": {
+ "#import": []
+ },
+ "win10-x86-aot": {
+ "#import": []
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Packaging/obj/Butterscotch.Package.wapproj.nuget.g.props b/Packaging/obj/Butterscotch.Package.wapproj.nuget.g.props
new file mode 100644
index 000000000..6c00a6167
--- /dev/null
+++ b/Packaging/obj/Butterscotch.Package.wapproj.nuget.g.props
@@ -0,0 +1,16 @@
+
+
+
+ True
+ NuGet
+ $(MSBuildThisFileDirectory)project.assets.json
+ $(UserProfile)\.nuget\packages\
+ C:\Users\emma\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages
+ PackageReference
+ 7.0.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Packaging/obj/Butterscotch.Package.wapproj.nuget.g.targets b/Packaging/obj/Butterscotch.Package.wapproj.nuget.g.targets
new file mode 100644
index 000000000..35a7576c5
--- /dev/null
+++ b/Packaging/obj/Butterscotch.Package.wapproj.nuget.g.targets
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Packaging/obj/project.assets.json b/Packaging/obj/project.assets.json
new file mode 100644
index 000000000..004223644
--- /dev/null
+++ b/Packaging/obj/project.assets.json
@@ -0,0 +1,105 @@
+{
+ "version": 3,
+ "targets": {
+ "UAP,Version=v10.0.19041": {},
+ "UAP,Version=v10.0.19041/win10-arm": {},
+ "UAP,Version=v10.0.19041/win10-arm-aot": {},
+ "UAP,Version=v10.0.19041/win10-arm64-aot": {},
+ "UAP,Version=v10.0.19041/win10-x64": {},
+ "UAP,Version=v10.0.19041/win10-x64-aot": {},
+ "UAP,Version=v10.0.19041/win10-x86": {},
+ "UAP,Version=v10.0.19041/win10-x86-aot": {}
+ },
+ "libraries": {},
+ "projectFileDependencyGroups": {
+ "UAP,Version=v10.0.19041": []
+ },
+ "packageFolders": {
+ "C:\\Users\\emma\\.nuget\\packages\\": {},
+ "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
+ },
+ "project": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\Butterscotch.Package.wapproj",
+ "projectName": "Butterscotch.Package",
+ "projectPath": "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\Butterscotch.Package.wapproj",
+ "packagesPath": "C:\\Users\\emma\\.nuget\\packages\\",
+ "outputPath": "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\obj\\",
+ "projectStyle": "PackageReference",
+ "UsingMicrosoftNETSdk": false,
+ "fallbackFolders": [
+ "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
+ ],
+ "configFilePaths": [
+ "C:\\Users\\emma\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net451"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "uap10.0.19041": {
+ "framework": "uap10.0.19041",
+ "targetAlias": "net451",
+ "projectReferences": {}
+ }
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.400"
+ },
+ "frameworks": {
+ "uap10.0.19041": {
+ "framework": "uap10.0.19041",
+ "targetAlias": "net451",
+ "imports": [
+ "netcoreapp3.1",
+ "netcoreapp3.0",
+ "netcoreapp2.0",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481",
+ "net11.0-windows10.0.26100",
+ "net461"
+ ],
+ "assetTargetFallback": true,
+ "warn": true
+ }
+ },
+ "runtimes": {
+ "win10-arm": {
+ "#import": []
+ },
+ "win10-arm-aot": {
+ "#import": []
+ },
+ "win10-arm64-aot": {
+ "#import": []
+ },
+ "win10-x64": {
+ "#import": []
+ },
+ "win10-x64-aot": {
+ "#import": []
+ },
+ "win10-x86": {
+ "#import": []
+ },
+ "win10-x86-aot": {
+ "#import": []
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Packaging/obj/project.nuget.cache b/Packaging/obj/project.nuget.cache
new file mode 100644
index 000000000..d536266f2
--- /dev/null
+++ b/Packaging/obj/project.nuget.cache
@@ -0,0 +1,8 @@
+{
+ "version": 2,
+ "dgSpecHash": "JpUNLatKo9Q=",
+ "success": true,
+ "projectFilePath": "\\\\Mac\\Home\\Library\\Mobile Documents\\com~apple~CloudDocs\\git\\Butterscotch\\Packaging\\Butterscotch.Package.wapproj",
+ "expectedPackageFiles": [],
+ "logs": []
+}
\ No newline at end of file
diff --git a/src/uwp/main.c b/src/uwp/main.c
new file mode 100644
index 000000000..52b1c2d3d
--- /dev/null
+++ b/src/uwp/main.c
@@ -0,0 +1,80 @@
+#include
+#include
+#include
+#include "log.h"
+
+#if defined(USE_SDL3)
+#include
+#endif
+
+void platformLog(const logType type, const char *format, va_list va) {
+ FILE *out = stderr;
+ const char *prefix = "";
+
+ switch (type) {
+ case LOG_TYPE_NORMAL:
+ out = stdout;
+ break;
+ case LOG_TYPE_WARNING:
+ prefix = "Warning: ";
+ break;
+ case LOG_TYPE_ERROR:
+ prefix = "Error: ";
+ break;
+ case LOG_TYPE_DEBUG:
+ prefix = "Debug: ";
+ break;
+ }
+
+ fputs(prefix, out);
+ vfprintf(out, format, va);
+}
+
+int main(int argc, char* argv[]) {
+ (void)argc;
+ printf("hello\n");
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ CommandLineArgs args = {0};
+
+ args.exitAtFrame = -1;
+#ifdef ENABLE_VM_TRACING
+ args.traceBytecodeAfterFrame = 0;
+#endif
+ args.speedMultiplier = 1.0;
+ args.fastForwardSpeed = 0.0;
+ args.osType = OS_UWP;
+ args.profilerFramesBetween = 0;
+ args.loadType = DATAWINLOADTYPE_LOAD_IN_MEMORY_AHEAD_OF_TIME;
+ args.lazyRooms = true;
+ args.lazyTextures = true;
+ args.lazyAudio = true;
+#if defined(ENABLE_MODERN_GL)
+ args.renderer = MODERN_GL;
+#else
+ args.renderer = SOFTWARE;
+#endif
+
+ char resolvedDataWinPath[2048] = {0};
+ const char *basePath = argv[0] ? argv[0] : "";
+ if (basePath[0] != '\0') {
+ strncpy(resolvedDataWinPath, basePath, sizeof(resolvedDataWinPath) - 1);
+ bsGetDirname(resolvedDataWinPath);
+ if (resolvedDataWinPath[0] != '\0' && resolvedDataWinPath[strlen(resolvedDataWinPath) - 1] != '/' && resolvedDataWinPath[strlen(resolvedDataWinPath) - 1] != '\\') {
+ strncat(resolvedDataWinPath, "/", sizeof(resolvedDataWinPath) - strlen(resolvedDataWinPath) - 1);
+ }
+ strncat(resolvedDataWinPath, "data.win", sizeof(resolvedDataWinPath) - strlen(resolvedDataWinPath) - 1);
+ if (access(resolvedDataWinPath, F_OK) == 0) {
+ args.dataWinPath = resolvedDataWinPath;
+ }
+ }
+
+ if (args.dataWinPath == NULL) {
+ args.dataWinPath = "data.win";
+ }
+
+ int ret = loop(args, argv[0]);
+ freeCommandLineArgs(&args);
+ return ret;
+}
diff --git a/src/uwp/stb_impl.c b/src/uwp/stb_impl.c
new file mode 100644
index 000000000..2554a8720
--- /dev/null
+++ b/src/uwp/stb_impl.c
@@ -0,0 +1,6 @@
+#define STB_IMAGE_IMPLEMENTATION
+#define STBI_NO_STDIO
+#include "stb_image.h"
+
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "stb_image_write.h"