diff --git a/.gitignore b/.gitignore index e49baff2e..a41dabf27 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,7 @@ __pycache__ # other repos /dep/mac + +# UWP crap +Generated Files +/packages diff --git a/dep/msvc/vsprops/Base.props b/dep/msvc/vsprops/Base.props index 1f5d32f77..b43e5326f 100644 --- a/dep/msvc/vsprops/Base.props +++ b/dep/msvc/vsprops/Base.props @@ -30,15 +30,23 @@ - - + + _HAS_EXCEPTIONS=0;%(PreprocessorDefinitions) false - + + + NotUsing + + _UWP;%(PreprocessorDefinitions) + + + + Disabled _DEBUG;%(PreprocessorDefinitions) @@ -67,7 +75,7 @@ true - + MaxSpeed true diff --git a/dep/msvc/vsprops/Configurations.props b/dep/msvc/vsprops/Configurations.props index 9d1cad756..f05c5447d 100644 --- a/dep/msvc/vsprops/Configurations.props +++ b/dep/msvc/vsprops/Configurations.props @@ -49,18 +49,59 @@ Release x64 + + DebugUWP + ARM64 + + + DebugUWP + Win32 + + + DebugUWP + x64 + + + ReleaseUWP + ARM64 + + + ReleaseUWP + Win32 + + + ReleaseUWP + x64 + + + true + + + + $(MSBuildProjectName) $(MSBuildProjectName) - + Win32Proj 10.0 + + true + en-US + 15.0 + true + Windows Store + 10.0 + 10.0.19041.0 + 10.0.17134.0 + + $(SolutionDir)build\$(Configuration)-$(Platform)\ diff --git a/dep/msvc/vsprops/StaticLibrary.props b/dep/msvc/vsprops/StaticLibrary.props index f0cef39c4..6c8f21e65 100644 --- a/dep/msvc/vsprops/StaticLibrary.props +++ b/dep/msvc/vsprops/StaticLibrary.props @@ -8,6 +8,12 @@ StaticLibrary + + true + false + false + + @@ -19,4 +25,4 @@ - \ No newline at end of file + diff --git a/dep/msvc/vsprops/Targets.props b/dep/msvc/vsprops/Targets.props index 490f98933..2d3e2bf8f 100644 --- a/dep/msvc/vsprops/Targets.props +++ b/dep/msvc/vsprops/Targets.props @@ -1,4 +1,14 @@ - \ No newline at end of file + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + diff --git a/dep/msvc/vsprops/Toolkit.props b/dep/msvc/vsprops/Toolkit.props index c2de1e9cb..5babeaf17 100644 --- a/dep/msvc/vsprops/Toolkit.props +++ b/dep/msvc/vsprops/Toolkit.props @@ -6,10 +6,10 @@ false - + true - + false \ No newline at end of file diff --git a/dep/vulkan-loader/include/vulkan_loader.h b/dep/vulkan-loader/include/vulkan_loader.h index bf6eb17b4..b2b3fc9ef 100644 --- a/dep/vulkan-loader/include/vulkan_loader.h +++ b/dep/vulkan-loader/include/vulkan_loader.h @@ -20,10 +20,12 @@ #endif // require vista+ +#ifndef WINAPI_FAMILY #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif #define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif #include diff --git a/duckstation-uwp.sln b/duckstation-uwp.sln new file mode 100644 index 000000000..4011ac1d9 --- /dev/null +++ b/duckstation-uwp.sln @@ -0,0 +1,376 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29230.47 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glad", "dep\glad\glad.vcxproj", "{43540154-9E1E-409C-834F-B84BE5621388}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{BA490C0E-497D-4634-A21E-E65012006385}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "dep\imgui\imgui.vcxproj", "{BB08260F-6FBC-46AF-8924-090EE71360C6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common.vcxproj", "{EE054E08-3799-4A59-A422-18259C105FFD}" + ProjectSection(ProjectDependencies) = postProject + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2} = {7F909E29-4808-4BD9-A60C-56C51A3AAEC2} + {43540154-9E1E-409C-834F-B84BE5621388} = {43540154-9E1E-409C-834F-B84BE5621388} + {ED601289-AC1A-46B8-A8ED-17DB9EB73423} = {ED601289-AC1A-46B8-A8ED-17DB9EB73423} + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017} = {425D6C99-D1C8-43C2-B8AC-4D7B1D941017} + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035} = {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035} + {7FF9FDB9-D504-47DB-A16A-B08071999620} = {7FF9FDB9-D504-47DB-A16A-B08071999620} + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3} = {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxproj", "{868B98C8-65A1-494B-8346-250A73A48C0A}" + ProjectSection(ProjectDependencies) = postProject + {EE054E08-3799-4A59-A422-18259C105FFD} = {EE054E08-3799-4A59-A422-18259C105FFD} + {BB08260F-6FBC-46AF-8924-090EE71360C6} = {BB08260F-6FBC-46AF-8924-090EE71360C6} + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB} = {8906836E-F06E-46E8-B11A-74E5E8C7B8FB} + {ED601289-AC1A-46B8-A8ED-17DB9EB73423} = {ED601289-AC1A-46B8-A8ED-17DB9EB73423} + {09553C96-9F39-49BF-8AE6-7ACBD07C410C} = {09553C96-9F39-49BF-8AE6-7ACBD07C410C} + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035} = {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035} + {7FF9FDB9-D504-47DB-A16A-B08071999620} = {7FF9FDB9-D504-47DB-A16A-B08071999620} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stb", "dep\stb\stb.vcxproj", "{ED601289-AC1A-46B8-A8ED-17DB9EB73423}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simpleini", "dep\simpleini\simpleini.vcxproj", "{3773F4CC-614E-4028-8595-22E08CA649E3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyxml2", "dep\tinyxml2\tinyxml2.vcxproj", "{933118A9-68C5-47B4-B151-B03C93961623}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "dep\minizip\minizip.vcxproj", "{8BDA439C-6358-45FB-9994-2FF083BABE06}" + ProjectSection(ProjectDependencies) = postProject + {7FF9FDB9-D504-47DB-A16A-B08071999620} = {7FF9FDB9-D504-47DB-A16A-B08071999620} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "dep\zlib\zlib.vcxproj", "{7FF9FDB9-D504-47DB-A16A-B08071999620}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libchdr", "dep\libchdr\libchdr.vcxproj", "{425D6C99-D1C8-43C2-B8AC-4D7B1D941017}" + ProjectSection(ProjectDependencies) = postProject + {DD944834-7899-4C1C-A4C1-064B5009D239} = {DD944834-7899-4C1C-A4C1-064B5009D239} + {7FF9FDB9-D504-47DB-A16A-B08071999620} = {7FF9FDB9-D504-47DB-A16A-B08071999620} + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D} = {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC", "dep\libFLAC\libFLAC.vcxproj", "{97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lzma", "dep\lzma\lzma.vcxproj", "{DD944834-7899-4C1C-A4C1-064B5009D239}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "frontend-common", "src\frontend-common\frontend-common.vcxproj", "{6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}" + ProjectSection(ProjectDependencies) = postProject + {075CED82-6A20-46DF-94C7-9624AC9DDBEB} = {075CED82-6A20-46DF-94C7-9624AC9DDBEB} + {8BDA439C-6358-45FB-9994-2FF083BABE06} = {8BDA439C-6358-45FB-9994-2FF083BABE06} + {933118A9-68C5-47B4-B151-B03C93961623} = {933118A9-68C5-47B4-B151-B03C93961623} + {868B98C8-65A1-494B-8346-250A73A48C0A} = {868B98C8-65A1-494B-8346-250A73A48C0A} + {3773F4CC-614E-4028-8595-22E08CA649E3} = {3773F4CC-614E-4028-8595-22E08CA649E3} + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64} = {4BA0A6D4-3AE1-42B2-9347-096FD023FF64} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xxhash", "dep\xxhash\xxhash.vcxproj", "{09553C96-9F39-49BF-8AE6-7ACBD07C410C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scmversion", "src\scmversion\scmversion.vcxproj", "{075CED82-6A20-46DF-94C7-9624AC9DDBEB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glslang", "dep\glslang\glslang.vcxproj", "{7F909E29-4808-4BD9-A60C-56C51A3AAEC2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vulkan-loader", "dep\vulkan-loader\vulkan-loader.vcxproj", "{9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vixl", "dep\vixl\vixl.vcxproj", "{8906836E-F06E-46E8-B11A-74E5E8C7B8FB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsamplerate", "dep\libsamplerate\libsamplerate.vcxproj", "{39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rcheevos", "dep\rcheevos\rcheevos.vcxproj", "{4BA0A6D4-3AE1-42B2-9347-096FD023FF64}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-uwp", "src\duckstation-uwp\duckstation-uwp.vcxproj", "{E2A6CEA9-9537-4C61-B637-81F1F17EF638}" + ProjectSection(ProjectDependencies) = postProject + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6} = {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + DebugUWP|ARM64 = DebugUWP|ARM64 + DebugUWP|x64 = DebugUWP|x64 + DebugUWP|x86 = DebugUWP|x86 + ReleaseUWP|ARM64 = ReleaseUWP|ARM64 + ReleaseUWP|x64 = ReleaseUWP|x64 + ReleaseUWP|x86 = ReleaseUWP|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {43540154-9E1E-409C-834F-B84BE5621388}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {43540154-9E1E-409C-834F-B84BE5621388}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {43540154-9E1E-409C-834F-B84BE5621388}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {43540154-9E1E-409C-834F-B84BE5621388}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {43540154-9E1E-409C-834F-B84BE5621388}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {43540154-9E1E-409C-834F-B84BE5621388}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {43540154-9E1E-409C-834F-B84BE5621388}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {43540154-9E1E-409C-834F-B84BE5621388}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {43540154-9E1E-409C-834F-B84BE5621388}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {43540154-9E1E-409C-834F-B84BE5621388}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {43540154-9E1E-409C-834F-B84BE5621388}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {43540154-9E1E-409C-834F-B84BE5621388}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {BB08260F-6FBC-46AF-8924-090EE71360C6}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {EE054E08-3799-4A59-A422-18259C105FFD}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {EE054E08-3799-4A59-A422-18259C105FFD}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {EE054E08-3799-4A59-A422-18259C105FFD}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {EE054E08-3799-4A59-A422-18259C105FFD}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {EE054E08-3799-4A59-A422-18259C105FFD}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {EE054E08-3799-4A59-A422-18259C105FFD}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {EE054E08-3799-4A59-A422-18259C105FFD}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {EE054E08-3799-4A59-A422-18259C105FFD}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {EE054E08-3799-4A59-A422-18259C105FFD}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {EE054E08-3799-4A59-A422-18259C105FFD}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {EE054E08-3799-4A59-A422-18259C105FFD}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {EE054E08-3799-4A59-A422-18259C105FFD}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {868B98C8-65A1-494B-8346-250A73A48C0A}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {868B98C8-65A1-494B-8346-250A73A48C0A}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {868B98C8-65A1-494B-8346-250A73A48C0A}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {3773F4CC-614E-4028-8595-22E08CA649E3}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {3773F4CC-614E-4028-8595-22E08CA649E3}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {3773F4CC-614E-4028-8595-22E08CA649E3}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {3773F4CC-614E-4028-8595-22E08CA649E3}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {3773F4CC-614E-4028-8595-22E08CA649E3}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {933118A9-68C5-47B4-B151-B03C93961623}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {933118A9-68C5-47B4-B151-B03C93961623}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {933118A9-68C5-47B4-B151-B03C93961623}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {933118A9-68C5-47B4-B151-B03C93961623}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {933118A9-68C5-47B4-B151-B03C93961623}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {933118A9-68C5-47B4-B151-B03C93961623}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {933118A9-68C5-47B4-B151-B03C93961623}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {933118A9-68C5-47B4-B151-B03C93961623}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {933118A9-68C5-47B4-B151-B03C93961623}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {933118A9-68C5-47B4-B151-B03C93961623}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {933118A9-68C5-47B4-B151-B03C93961623}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {933118A9-68C5-47B4-B151-B03C93961623}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {8BDA439C-6358-45FB-9994-2FF083BABE06}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {7FF9FDB9-D504-47DB-A16A-B08071999620}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {DD944834-7899-4C1C-A4C1-064B5009D239}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {DD944834-7899-4C1C-A4C1-064B5009D239}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {DD944834-7899-4C1C-A4C1-064B5009D239}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {DD944834-7899-4C1C-A4C1-064B5009D239}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {DD944834-7899-4C1C-A4C1-064B5009D239}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {6245DEC8-D2DA-47EE-A373-CBD6FCF3ECE6}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {09553C96-9F39-49BF-8AE6-7ACBD07C410C}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {075CED82-6A20-46DF-94C7-9624AC9DDBEB}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|ARM64.ActiveCfg = DebugUWP|ARM64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|ARM64.Build.0 = DebugUWP|ARM64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|ARM64.Deploy.0 = DebugUWP|ARM64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|x64.ActiveCfg = DebugUWP|x64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|x64.Build.0 = DebugUWP|x64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|x64.Deploy.0 = DebugUWP|x64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|x86.ActiveCfg = DebugUWP|Win32 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|x86.Build.0 = DebugUWP|Win32 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.DebugUWP|x86.Deploy.0 = DebugUWP|Win32 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|ARM64.ActiveCfg = ReleaseUWP|ARM64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|ARM64.Build.0 = ReleaseUWP|ARM64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|ARM64.Deploy.0 = ReleaseUWP|ARM64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|x64.ActiveCfg = ReleaseUWP|x64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|x64.Build.0 = ReleaseUWP|x64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|x64.Deploy.0 = ReleaseUWP|x64 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|x86.ActiveCfg = ReleaseUWP|Win32 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|x86.Build.0 = ReleaseUWP|Win32 + {E2A6CEA9-9537-4C61-B637-81F1F17EF638}.ReleaseUWP|x86.Deploy.0 = ReleaseUWP|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {43540154-9E1E-409C-834F-B84BE5621388} = {BA490C0E-497D-4634-A21E-E65012006385} + {BB08260F-6FBC-46AF-8924-090EE71360C6} = {BA490C0E-497D-4634-A21E-E65012006385} + {ED601289-AC1A-46B8-A8ED-17DB9EB73423} = {BA490C0E-497D-4634-A21E-E65012006385} + {3773F4CC-614E-4028-8595-22E08CA649E3} = {BA490C0E-497D-4634-A21E-E65012006385} + {933118A9-68C5-47B4-B151-B03C93961623} = {BA490C0E-497D-4634-A21E-E65012006385} + {8BDA439C-6358-45FB-9994-2FF083BABE06} = {BA490C0E-497D-4634-A21E-E65012006385} + {7FF9FDB9-D504-47DB-A16A-B08071999620} = {BA490C0E-497D-4634-A21E-E65012006385} + {425D6C99-D1C8-43C2-B8AC-4D7B1D941017} = {BA490C0E-497D-4634-A21E-E65012006385} + {97CBD3CB-CBC7-4D52-ABDE-F0AE7B794A5D} = {BA490C0E-497D-4634-A21E-E65012006385} + {DD944834-7899-4C1C-A4C1-064B5009D239} = {BA490C0E-497D-4634-A21E-E65012006385} + {09553C96-9F39-49BF-8AE6-7ACBD07C410C} = {BA490C0E-497D-4634-A21E-E65012006385} + {7F909E29-4808-4BD9-A60C-56C51A3AAEC2} = {BA490C0E-497D-4634-A21E-E65012006385} + {9C8DDEB0-2B8F-4F5F-BA86-127CDF27F035} = {BA490C0E-497D-4634-A21E-E65012006385} + {8906836E-F06E-46E8-B11A-74E5E8C7B8FB} = {BA490C0E-497D-4634-A21E-E65012006385} + {39F0ADFF-3A84-470D-9CF0-CA49E164F2F3} = {BA490C0E-497D-4634-A21E-E65012006385} + {4BA0A6D4-3AE1-42B2-9347-096FD023FF64} = {BA490C0E-497D-4634-A21E-E65012006385} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {26E40B32-7C1D-48D0-95F4-1A500E054028} + EndGlobalSection +EndGlobal diff --git a/src/common/assert.cpp b/src/common/assert.cpp index 03ce57d41..6d83991dc 100644 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp @@ -3,7 +3,7 @@ #include #include -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) #include "windows_headers.h" #include #include @@ -13,7 +13,7 @@ static std::mutex s_AssertFailedMutex; static inline void FreezeThreads(void** ppHandle) { -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { @@ -43,7 +43,7 @@ static inline void FreezeThreads(void** ppHandle) static inline void ResumeThreads(void* pHandle) { -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) HANDLE hSnapshot = (HANDLE)pHandle; if (pHandle != INVALID_HANDLE_VALUE) { @@ -79,7 +79,7 @@ void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* char szMsg[512]; std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)", szMessage, szFunction, szFile, uLine); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast(std::strlen(szMsg)), NULL, NULL); OutputDebugStringA(szMsg); @@ -114,7 +114,7 @@ void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* char szMsg[512]; std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)", szMessage, szFunction, szFile, uLine); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast(std::strlen(szMsg)), NULL, NULL); OutputDebugStringA(szMsg); diff --git a/src/common/byte_stream.cpp b/src/common/byte_stream.cpp index 53ba73242..1ae2d82b7 100644 --- a/src/common/byte_stream.cpp +++ b/src/common/byte_stream.cpp @@ -884,7 +884,7 @@ std::unique_ptr ByteStream_OpenFileStream(const char* fileName, u32 if ((openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)) == BYTESTREAM_OPEN_WRITE) { // if opening with write but not create, the path must exist. - if (GetFileAttributes(fileName) == INVALID_FILE_ATTRIBUTES) + if (!FileSystem::FileExists(fileName)) return nullptr; } @@ -895,7 +895,7 @@ std::unique_ptr ByteStream_OpenFileStream(const char* fileName, u32 { // if the file exists, use r+, otherwise w+ // HACK: if we're not truncating, and the file exists (we want to only update it), we still have to use r+ - if ((openMode & BYTESTREAM_OPEN_TRUNCATE) || GetFileAttributes(fileName) == INVALID_FILE_ATTRIBUTES) + if (!FileSystem::FileExists(fileName)) { modeString[modeStringLength++] = 'w'; if (openMode & BYTESTREAM_OPEN_READ) @@ -1013,8 +1013,15 @@ std::unique_ptr ByteStream_OpenFileStream(const char* fileName, u32 DWORD desiredAccess = GENERIC_WRITE; if (openMode & BYTESTREAM_OPEN_READ) desiredAccess |= GENERIC_READ; + +#ifndef _UWP HANDLE hFile = CreateFileW(wideTemporaryFileName.c_str(), desiredAccess, FILE_SHARE_DELETE, NULL, CREATE_NEW, 0, NULL); +#else + HANDLE hFile = + CreateFile2FromAppW(wideTemporaryFileName.c_str(), desiredAccess, FILE_SHARE_DELETE, CREATE_NEW, nullptr); +#endif + if (hFile == INVALID_HANDLE_VALUE) return nullptr; @@ -1175,8 +1182,8 @@ std::unique_ptr ByteStream_OpenFileStream(const char* fileName, u32 } else // if (errno == ENOTDIR) { - // well.. someone's trying to open a fucking weird path that is comprised of both directories and files... - // I aint sticking around here to find out what disaster awaits... let fopen deal with it + // well.. someone's trying to open a fucking weird path that is comprised of both directories and + // files... I aint sticking around here to find out what disaster awaits... let fopen deal with it break; } } diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 797d14ed2..f14160ae9 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -33,8 +33,7 @@ - true - true + true @@ -62,7 +61,9 @@ - + + true + @@ -79,7 +80,9 @@ - + + true + @@ -116,8 +119,7 @@ - true - true + true @@ -140,7 +142,7 @@ - true + true @@ -155,7 +157,9 @@ - + + true + diff --git a/src/common/crash_handler.cpp b/src/common/crash_handler.cpp index 1db770de6..33c74b022 100644 --- a/src/common/crash_handler.cpp +++ b/src/common/crash_handler.cpp @@ -4,7 +4,7 @@ #include #include -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) #include "windows_headers.h" #include "thirdparty/StackWalker.h" diff --git a/src/common/file_system.cpp b/src/common/file_system.cpp index 2602fef97..408d0b605 100644 --- a/src/common/file_system.cpp +++ b/src/common/file_system.cpp @@ -21,6 +21,20 @@ #if defined(_WIN32) #include + +#if defined(_UWP) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#endif + #else #include #include @@ -713,7 +727,7 @@ std::vector GetRootDirectoryList() { std::vector results; -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) char buf[256]; if (GetLogicalDriveStringsA(sizeof(buf), buf) != 0) { @@ -725,6 +739,28 @@ std::vector GetRootDirectoryList() ptr += len + 1u; } } +#elif defined(_UWP) + if (const auto install_location = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation(); + install_location) + { + if (const auto path = install_location.Path(); !path.empty()) + results.push_back(StringUtil::WideStringToUTF8String(path)); + } + + if (const auto local_location = winrt::Windows::Storage::ApplicationData::Current().LocalFolder(); local_location) + { + if (const auto path = local_location.Path(); !path.empty()) + results.push_back(StringUtil::WideStringToUTF8String(path)); + } + + const auto devices = winrt::Windows::Storage::KnownFolders::RemovableDevices(); + const auto folders_task(devices.GetFoldersAsync()); + for (const auto& storage_folder : folders_task.get()) + { + const auto path = storage_folder.Path(); + if (!path.empty()) + results.push_back(StringUtil::WideStringToUTF8String(path)); + } #else const char* home_path = std::getenv("HOME"); if (home_path) @@ -772,6 +808,106 @@ FileSystem::ManagedCFilePtr OpenManagedCFile(const char* filename, const char* m return ManagedCFilePtr(OpenCFile(filename, mode), [](std::FILE* fp) { std::fclose(fp); }); } +#ifdef _UWP +std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) +{ + DWORD access = 0; + DWORD share = 0; + DWORD disposition = 0; + + int flags = 0; + const wchar_t* tmode = mode; + while (*tmode) + { + if (*tmode == L'r' && *(tmode + 1) == L'+') + { + access = GENERIC_READ | GENERIC_WRITE; + share = 0; + disposition = OPEN_EXISTING; + flags |= _O_RDWR; + tmode += 2; + } + else if (*tmode == L'w' && *(tmode + 1) == L'+') + { + access = GENERIC_READ | GENERIC_WRITE; + share = 0; + disposition = CREATE_ALWAYS; + flags |= _O_RDWR | _O_CREAT | _O_TRUNC; + tmode += 2; + } + else if (*tmode == L'a' && *(tmode + 1) == L'+') + { + access = GENERIC_READ | GENERIC_WRITE; + share = 0; + disposition = CREATE_ALWAYS; + flags |= _O_RDWR | _O_APPEND | _O_CREAT | _O_TRUNC; + tmode += 2; + } + else if (*tmode == L'r') + { + access = GENERIC_READ; + share = 0; + disposition = OPEN_EXISTING; + flags |= _O_RDONLY; + tmode++; + } + else if (*tmode == L'w') + { + access = GENERIC_WRITE; + share = 0; + disposition = CREATE_ALWAYS; + flags |= _O_WRONLY | _O_CREAT | _O_TRUNC; + tmode++; + } + else if (*tmode == L'a') + { + access = GENERIC_READ | GENERIC_WRITE; + share = 0; + disposition = CREATE_ALWAYS; + flags |= _O_WRONLY | _O_APPEND | _O_CREAT | _O_TRUNC; + tmode++; + } + else if (*tmode == L'b') + { + flags |= _O_BINARY; + tmode++; + } + else + { + Log_ErrorPrintf("Unknown mode flags: '%s'", StringUtil::WideStringToUTF8String(mode).c_str()); + return nullptr; + } + } + + HANDLE hFile = CreateFileFromAppW(wfilename, access, share, nullptr, disposition, 0, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + return nullptr; + + if (flags & _O_APPEND && !SetFilePointerEx(hFile, LARGE_INTEGER{}, nullptr, FILE_END)) + { + Log_ErrorPrintf("SetFilePointerEx() failed: %08X", GetLastError()); + CloseHandle(hFile); + return nullptr; + } + + int fd = _open_osfhandle(reinterpret_cast(hFile), flags); + if (fd < 0) + { + CloseHandle(hFile); + return nullptr; + } + + std::FILE* fp = _wfdopen(fd, mode); + if (!fp) + { + _close(fd); + return nullptr; + } + + return fp; +} +#endif // _UWP + std::FILE* OpenCFile(const char* filename, const char* mode) { #ifdef _WIN32 @@ -789,9 +925,16 @@ std::FILE* OpenCFile(const char* filename, const char* mode) { wfilename[wlen] = 0; wmode[wmodelen] = 0; + std::FILE* fp; if (_wfopen_s(&fp, wfilename, wmode) != 0) + { +#ifdef _UWP + return OpenCFileUWP(wfilename, wmode); +#else return nullptr; +#endif + } return fp; } @@ -1052,6 +1195,19 @@ static u32 TranslateWin32Attributes(u32 Win32Attributes) return r; } +static DWORD WrapGetFileAttributes(const wchar_t* path) +{ +#ifndef _UWP + return GetFileAttributesW(path); +#else + WIN32_FILE_ATTRIBUTE_DATA fad; + if (!GetFileAttributesExFromAppW(path, GetFileExInfoStandard, &fad)) + return INVALID_FILE_ATTRIBUTES; + + return fad.dwFileAttributes; +#endif +} + static const u32 READ_DIRECTORY_CHANGES_NOTIFY_FILTER = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION; @@ -1064,7 +1220,7 @@ public: m_directoryChangeQueued(false) { m_bufferSize = 16384; - m_pBuffer = new byte[m_bufferSize]; + m_pBuffer = new u8[m_bufferSize]; } virtual ~ChangeNotifierWin32() @@ -1103,7 +1259,7 @@ public: // has any bytes? if (bytesRead > 0) { - const byte* pCurrentPointer = m_pBuffer; + const u8* pCurrentPointer = m_pBuffer; PathString fileName; for (;;) { @@ -1176,15 +1332,26 @@ private: HANDLE m_hDirectory; OVERLAPPED m_overlapped; bool m_directoryChangeQueued; - byte* m_pBuffer; + u8* m_pBuffer; u32 m_bufferSize; }; std::unique_ptr CreateChangeNotifier(const char* path, bool recursiveWatch) { // open the directory up - HANDLE hDirectory = CreateFileA(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); + std::wstring path_wstr(StringUtil::UTF8StringToWideString(path)); +#ifndef _UWP + HANDLE hDirectory = + CreateFileW(path_wstr.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); +#else + CREATEFILE2_EXTENDED_PARAMETERS ep = {}; + ep.dwSize = sizeof(ep); + ep.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + ep.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED; + HANDLE hDirectory = CreateFile2FromAppW(path_wstr.c_str(), FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, OPEN_EXISTING, &ep); +#endif if (hDirectory == nullptr) return nullptr; @@ -1212,8 +1379,18 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co tempStr = StringUtil::StdStringFromFormat("%s\\*", OriginPath); } + // holder for utf-8 conversion WIN32_FIND_DATAW wfd; + std::string utf8_filename; + utf8_filename.reserve(countof(wfd.cFileName) * 2); + +#ifndef _UWP HANDLE hFind = FindFirstFileW(StringUtil::UTF8StringToWideString(tempStr).c_str(), &wfd); +#else + HANDLE hFind = FindFirstFileExFromAppW(StringUtil::UTF8StringToWideString(tempStr).c_str(), FindExInfoBasic, &wfd, + FindExSearchNameMatch, nullptr, 0); +#endif + if (hFind == INVALID_HANDLE_VALUE) return 0; @@ -1227,10 +1404,6 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co wildCardMatchAll = !(std::strcmp(Pattern, "*")); } - // holder for utf-8 conversion - std::string utf8_filename; - utf8_filename.reserve(countof(wfd.cFileName) * 2); - // iterate results do { @@ -1360,6 +1533,7 @@ bool FileSystem::StatFile(const char* path, FILESYSTEM_STAT_DATA* pStatData) wpath[wlen] = 0; +#ifndef _UWP // determine attributes for the path. if it's a directory, things have to be handled differently.. DWORD fileAttributes = GetFileAttributesW(wpath); if (fileAttributes == INVALID_FILE_ATTRIBUTES) @@ -1398,6 +1572,16 @@ bool FileSystem::StatFile(const char* path, FILESYSTEM_STAT_DATA* pStatData) pStatData->ModificationTime.SetWindowsFileTime(&bhfi.ftLastWriteTime); pStatData->Size = ((u64)bhfi.nFileSizeHigh) << 32 | (u64)bhfi.nFileSizeLow; return true; +#else + WIN32_FILE_ATTRIBUTE_DATA fad; + if (!GetFileAttributesExFromAppW(wpath, GetFileExInfoStandard, &fad)) + return false; + + pStatData->Attributes = TranslateWin32Attributes(fad.dwFileAttributes); + pStatData->ModificationTime.SetWindowsFileTime(&fad.ftLastWriteTime); + pStatData->Size = ((u64)fad.nFileSizeHigh) << 32 | (u64)fad.nFileSizeLow; + return true; +#endif } bool FileSystem::StatFile(std::FILE* fp, FILESYSTEM_STAT_DATA* pStatData) @@ -1447,7 +1631,7 @@ bool FileSystem::FileExists(const char* path) wpath[wlen] = 0; // determine attributes for the path. if it's a directory, things have to be handled differently.. - DWORD fileAttributes = GetFileAttributesW(wpath); + DWORD fileAttributes = WrapGetFileAttributes(wpath); if (fileAttributes == INVALID_FILE_ATTRIBUTES) return false; @@ -1477,7 +1661,7 @@ bool FileSystem::DirectoryExists(const char* path) wpath[wlen] = 0; // determine attributes for the path. if it's a directory, things have to be handled differently.. - DWORD fileAttributes = GetFileAttributesW(wpath); + DWORD fileAttributes = WrapGetFileAttributes(wpath); if (fileAttributes == INVALID_FILE_ATTRIBUTES) return false; @@ -1495,16 +1679,21 @@ bool FileSystem::CreateDirectory(const char* Path, bool Recursive) if (wpath[0] == L'\0') return false; - // try just flat-out, might work if there's no other segments that have to be made + // try just flat-out, might work if there's no other segments that have to be made +#ifndef _UWP if (CreateDirectoryW(wpath.c_str(), nullptr)) return true; +#else + if (CreateDirectoryFromAppW(wpath.c_str(), nullptr)) + return true; +#endif // check error DWORD lastError = GetLastError(); if (lastError == ERROR_ALREADY_EXISTS) { // check the attributes - u32 Attributes = GetFileAttributesW(wpath.c_str()); + u32 Attributes = WrapGetFileAttributes(wpath.c_str()); if (Attributes != INVALID_FILE_ATTRIBUTES && Attributes & FILE_ATTRIBUTE_DIRECTORY) return true; else @@ -1523,7 +1712,13 @@ bool FileSystem::CreateDirectory(const char* Path, bool Recursive) if (wpath[i] == L'\\' || wpath[i] == L'/') { tempStr[i] = L'\0'; - if (!CreateDirectoryW(tempStr, nullptr)) + +#ifndef _UWP + const BOOL result = CreateDirectoryW(tempStr, nullptr); +#else + const BOOL result = CreateDirectoryFromAppW(tempStr, nullptr); +#endif + if (!result) { lastError = GetLastError(); if (lastError != ERROR_ALREADY_EXISTS) // fine, continue to next path segment @@ -1537,7 +1732,12 @@ bool FileSystem::CreateDirectory(const char* Path, bool Recursive) // re-create the end if it's not a separator, check / as well because windows can interpret them if (wpath[pathLength - 1] != L'\\' && wpath[pathLength - 1] != L'/') { - if (!CreateDirectoryW(wpath.c_str(), nullptr)) +#ifndef _UWP + const BOOL result = CreateDirectoryW(wpath.c_str(), nullptr); +#else + const BOOL result = CreateDirectoryFromAppW(wpath.c_str(), nullptr); +#endif + if (!result) { lastError = GetLastError(); if (lastError != ERROR_ALREADY_EXISTS) @@ -1561,14 +1761,15 @@ bool FileSystem::DeleteFile(const char* Path) return false; const std::wstring wpath(StringUtil::UTF8StringToWideString(Path)); - DWORD fileAttributes = GetFileAttributesW(wpath.c_str()); - if (fileAttributes == INVALID_FILE_ATTRIBUTES) + const DWORD fileAttributes = WrapGetFileAttributes(wpath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES || fileAttributes & FILE_ATTRIBUTE_DIRECTORY) return false; - if (!(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return (DeleteFileW(wpath.c_str()) == TRUE); - else - return false; +#ifndef _UWP + return (DeleteFileW(wpath.c_str()) == TRUE); +#else + return (DeleteFileFromAppW(wpath.c_str()) == TRUE); +#endif } bool FileSystem::RenamePath(const char* OldPath, const char* NewPath) @@ -1576,11 +1777,19 @@ bool FileSystem::RenamePath(const char* OldPath, const char* NewPath) const std::wstring old_wpath(StringUtil::UTF8StringToWideString(OldPath)); const std::wstring new_wpath(StringUtil::UTF8StringToWideString(NewPath)); +#ifndef _UWP if (!MoveFileExW(old_wpath.c_str(), new_wpath.c_str(), MOVEFILE_REPLACE_EXISTING)) { Log_ErrorPrintf("MoveFileEx('%s', '%s') failed: %08X", OldPath, NewPath, GetLastError()); return false; } +#else + if (!ReplaceFileFromAppW(old_wpath.c_str(), new_wpath.c_str(), nullptr, 0, nullptr, nullptr)) + { + Log_ErrorPrintf("MoveFileFromAppW('%s', '%s') failed: %08X", OldPath, NewPath, GetLastError()); + return false; + } +#endif return true; } @@ -1588,13 +1797,19 @@ bool FileSystem::RenamePath(const char* OldPath, const char* NewPath) static bool RecursiveDeleteDirectory(const std::wstring& wpath, bool Recursive) { // ensure it exists - DWORD fileAttributes = GetFileAttributesW(wpath.c_str()); - if (fileAttributes == INVALID_FILE_ATTRIBUTES || !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + const DWORD fileAttributes = WrapGetFileAttributes(wpath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES || fileAttributes & FILE_ATTRIBUTE_DIRECTORY) return false; // non-recursive case just try removing the directory if (!Recursive) + { +#ifndef _UWP return (RemoveDirectoryW(wpath.c_str()) == TRUE); +#else + return (RemoveDirectoryFromAppW(wpath.c_str()) == TRUE); +#endif + } // doing a recursive delete std::wstring fileName = wpath; @@ -1602,7 +1817,12 @@ static bool RecursiveDeleteDirectory(const std::wstring& wpath, bool Recursive) // is there any files? WIN32_FIND_DATAW findData; +#ifndef _UWP HANDLE hFind = FindFirstFileW(fileName.c_str(), &findData); +#else + HANDLE hFind = + FindFirstFileExFromAppW(fileName.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0); +#endif if (hFind == INVALID_HANDLE_VALUE) return false; @@ -1634,7 +1854,12 @@ static bool RecursiveDeleteDirectory(const std::wstring& wpath, bool Recursive) else { // found a file, so delete it - if (!DeleteFileW(fileName.c_str())) +#ifndef _UWP + const BOOL result = DeleteFileW(fileName.c_str()); +#else + const BOOL result = DeleteFileFromAppW(fileName.c_str()); +#endif + if (!result) { FindClose(hFind); return false; @@ -1644,7 +1869,12 @@ static bool RecursiveDeleteDirectory(const std::wstring& wpath, bool Recursive) FindClose(hFind); // nuke the directory itself - if (!RemoveDirectoryW(wpath.c_str())) +#ifndef _UWP + const BOOL result = RemoveDirectoryW(wpath.c_str()); +#else + const BOOL result = RemoveDirectoryFromAppW(wpath.c_str()); +#endif + if (!result) return false; // done @@ -1664,8 +1894,10 @@ std::string GetProgramPath() // Fall back to the main module if this fails. HMODULE module = nullptr; +#ifndef _UWP GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(&GetProgramPath), &module); +#endif for (;;) { diff --git a/src/common/jit_code_buffer.cpp b/src/common/jit_code_buffer.cpp index ee595833c..15a6dc835 100644 --- a/src/common/jit_code_buffer.cpp +++ b/src/common/jit_code_buffer.cpp @@ -44,7 +44,21 @@ bool JitCodeBuffer::Allocate(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz m_total_size = size + far_code_size; #if defined(_WIN32) +#if !defined(_UWP) m_code_ptr = static_cast(VirtualAlloc(nullptr, m_total_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE)); +#else + m_code_ptr = static_cast( + VirtualAlloc2FromApp(GetCurrentProcess(), nullptr, m_total_size, MEM_COMMIT, PAGE_READWRITE, nullptr, 0)); + if (m_code_ptr) + { + ULONG old_protection; + if (!VirtualProtectFromApp(m_code_ptr, m_total_size, PAGE_EXECUTE_READWRITE, &old_protection)) + { + VirtualFree(m_code_ptr, m_total_size, MEM_RELEASE); + return false; + } + } +#endif if (!m_code_ptr) { Log_ErrorPrintf("VirtualAlloc(RWX, %u) for internal buffer failed: %u", m_total_size, GetLastError()); @@ -157,20 +171,48 @@ void JitCodeBuffer::Destroy() if (m_owns_buffer) { #if defined(_WIN32) - VirtualFree(m_code_ptr, 0, MEM_RELEASE); + if (!VirtualFree(m_code_ptr, 0, MEM_RELEASE)) + Log_ErrorPrintf("Failed to free code pointer %p", m_code_ptr); #elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__) || defined(__HAIKU__) || defined(__FreeBSD__) - munmap(m_code_ptr, m_total_size); + if (munmap(m_code_ptr, m_total_size) != 0) + Log_ErrorPrintf("Failed to free code pointer %p", m_code_ptr); #endif } else if (m_code_ptr) { #if defined(_WIN32) DWORD old_protect = 0; - VirtualProtect(m_code_ptr, m_total_size, m_old_protection, &old_protect); + if (!VirtualProtect(m_code_ptr, m_total_size, m_old_protection, &old_protect)) + Log_ErrorPrintf("Failed to restore protection on %p", m_code_ptr); #else - mprotect(m_code_ptr, m_total_size, m_old_protection); + if (mprotect(m_code_ptr, m_total_size, m_old_protection) != 0) + Log_ErrorPrintf("Failed to restore protection on %p", m_code_ptr); #endif } + + m_code_ptr = nullptr; + m_free_code_ptr = nullptr; + m_code_size = 0; + m_code_reserve_size = 0; + m_code_used = 0; + m_far_code_ptr = nullptr; + m_free_far_code_ptr = nullptr; + m_far_code_size = 0; + m_far_code_used = 0; + m_total_size = 0; + m_guard_size = 0; + m_old_protection = 0; + m_owns_buffer = false; +} + +void JitCodeBuffer::ReserveCode(u32 size) +{ + Assert(m_code_used == 0); + Assert(size < m_code_size); + + m_code_reserve_size += size; + m_free_code_ptr += size; + m_code_size -= size; } void JitCodeBuffer::CommitCode(u32 length) @@ -207,7 +249,7 @@ void JitCodeBuffer::Reset() { WriteProtect(false); - m_free_code_ptr = m_code_ptr + m_guard_size; + m_free_code_ptr = m_code_ptr + m_guard_size + m_code_reserve_size; m_code_used = 0; std::memset(m_free_code_ptr, 0, m_code_size); FlushInstructionCache(m_free_code_ptr, m_code_size); diff --git a/src/common/jit_code_buffer.h b/src/common/jit_code_buffer.h index 3c6b210ef..72d2b88b4 100644 --- a/src/common/jit_code_buffer.h +++ b/src/common/jit_code_buffer.h @@ -9,20 +9,23 @@ public: JitCodeBuffer(void* buffer, u32 size, u32 far_code_size, u32 guard_size); ~JitCodeBuffer(); + bool IsValid() const { return (m_code_ptr != nullptr); } + bool Allocate(u32 size = 64 * 1024 * 1024, u32 far_code_size = 0); bool Initialize(void* buffer, u32 size, u32 far_code_size = 0, u32 guard_size = 0); void Destroy(); void Reset(); - u8* GetCodePointer() const { return m_code_ptr; } - u32 GetTotalSize() const { return m_total_size; } + ALWAYS_INLINE u8* GetCodePointer() const { return m_code_ptr; } + ALWAYS_INLINE u32 GetTotalSize() const { return m_total_size; } - u8* GetFreeCodePointer() const { return m_free_code_ptr; } - u32 GetFreeCodeSpace() const { return static_cast(m_code_size - m_code_used); } + ALWAYS_INLINE u8* GetFreeCodePointer() const { return m_free_code_ptr; } + ALWAYS_INLINE u32 GetFreeCodeSpace() const { return static_cast(m_code_size - m_code_used); } + void ReserveCode(u32 size); void CommitCode(u32 length); - u8* GetFreeFarCodePointer() const { return m_free_far_code_ptr; } - u32 GetFreeFarCodeSpace() const { return static_cast(m_far_code_size - m_far_code_used); } + ALWAYS_INLINE u8* GetFreeFarCodePointer() const { return m_free_far_code_ptr; } + ALWAYS_INLINE u32 GetFreeFarCodeSpace() const { return static_cast(m_far_code_size - m_far_code_used); } void CommitFarCode(u32 length); /// Adjusts the free code pointer to the specified alignment, padding with bytes. @@ -43,6 +46,7 @@ private: u8* m_code_ptr = nullptr; u8* m_free_code_ptr = nullptr; u32 m_code_size = 0; + u32 m_code_reserve_size = 0; u32 m_code_used = 0; u8* m_far_code_ptr = nullptr; diff --git a/src/common/memory_arena.cpp b/src/common/memory_arena.cpp index d89f388dc..02d7b68f6 100644 --- a/src/common/memory_arena.cpp +++ b/src/common/memory_arena.cpp @@ -135,8 +135,13 @@ bool MemoryArena::Create(size_t size, bool writable, bool executable) #if defined(_WIN32) const DWORD protect = (writable ? (executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE) : PAGE_READONLY); +#ifndef _UWP m_file_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, protect, Truncate32(size >> 32), Truncate32(size), file_mapping_name.c_str()); +#else + m_file_handle = CreateFileMappingFromApp(INVALID_HANDLE_VALUE, nullptr, protect, size, + StringUtil::UTF8StringToWideString(file_mapping_name).c_str()); +#endif if (!m_file_handle) { Log_ErrorPrintf("CreateFileMapping failed: %u", GetLastError()); @@ -257,8 +262,16 @@ void* MemoryArena::CreateViewPtr(size_t offset, size_t size, bool writable, bool void* base_pointer; #if defined(_WIN32) const DWORD desired_access = FILE_MAP_READ | (writable ? FILE_MAP_WRITE : 0) | (executable ? FILE_MAP_EXECUTE : 0); +#ifndef _UWP base_pointer = MapViewOfFileEx(m_file_handle, desired_access, Truncate32(offset >> 32), Truncate32(offset), size, fixed_address); +#else + // UWP does not support fixed mappings. + if (!fixed_address) + base_pointer = MapViewOfFileFromApp(m_file_handle, desired_access, offset, size); + else + base_pointer = nullptr; +#endif if (!base_pointer) return nullptr; #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) diff --git a/src/common/page_fault_handler.cpp b/src/common/page_fault_handler.cpp index 9be882561..e3a1fda3c 100644 --- a/src/common/page_fault_handler.cpp +++ b/src/common/page_fault_handler.cpp @@ -24,8 +24,10 @@ namespace Common::PageFaultHandler { struct RegisteredHandler { - void* owner; Callback callback; + const void* owner; + void* start_pc; + u32 code_size; }; static std::vector m_handlers; static std::mutex m_handler_lock; @@ -78,7 +80,7 @@ static bool IsStoreInstruction(const void* ptr) } #endif -#if defined(_WIN32) && (defined(CPU_X64) || defined(CPU_AARCH64)) +#if defined(_WIN32) && !defined(_UWP) && (defined(CPU_X64) || defined(CPU_AARCH64)) static PVOID s_veh_handle; static LONG ExceptionHandler(PEXCEPTION_POINTERS exi) @@ -110,9 +112,149 @@ static LONG ExceptionHandler(PEXCEPTION_POINTERS exi) } s_in_handler = false; + return EXCEPTION_CONTINUE_SEARCH; } +u32 GetHandlerCodeSize() +{ + return 0; +} + +#elif defined(_UWP) + +// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=msvc-160 +struct UNWIND_INFO +{ + BYTE version : 3; + BYTE flags : 5; + BYTE size_of_prologue; + BYTE count_of_unwind_codes; + BYTE frame_register : 4; + BYTE frame_offset_scaled : 4; + ULONG exception_handler_address; +}; + +struct UnwindHandler +{ + RUNTIME_FUNCTION runtime_function; + UNWIND_INFO unwind_info; + uint8_t exception_handler_code[32]; +}; + +static constexpr size_t UNWIND_HANDLER_ALLOC_SIZE = 4096; +static_assert(sizeof(UnwindHandler) <= UNWIND_HANDLER_ALLOC_SIZE); + +static EXCEPTION_DISPOSITION UnwindExceptionHandler(PEXCEPTION_RECORD ExceptionRecord, ULONG64 EstablisherFrame, + PCONTEXT ContextRecord, PDISPATCHER_CONTEXT DispatcherContext) +{ + if (s_in_handler) + return ExceptionContinueSearch; + + s_in_handler = true; + + void* const exception_pc = reinterpret_cast(DispatcherContext->ControlPc); + void* const exception_address = reinterpret_cast(ExceptionRecord->ExceptionInformation[1]); + bool const is_write = ExceptionRecord->ExceptionInformation[0] == 1; + + std::lock_guard guard(m_handler_lock); + for (const RegisteredHandler& rh : m_handlers) + { + if (static_cast(exception_pc) >= static_cast(rh.start_pc) && + static_cast(exception_pc) <= (static_cast(rh.start_pc) + rh.code_size)) + { + if (rh.callback(exception_pc, exception_address, is_write) == HandlerResult::ContinueExecution) + { + s_in_handler = false; + return ExceptionContinueExecution; + } + } + } + + s_in_handler = false; + return ExceptionContinueSearch; +} + +static PRUNTIME_FUNCTION GetRuntimeFunctionCallback(DWORD64 ControlPc, PVOID Context) +{ + std::lock_guard guard(m_handler_lock); + for (const RegisteredHandler& rh : m_handlers) + { + if (ControlPc >= reinterpret_cast(rh.start_pc) && + ControlPc <= (reinterpret_cast(rh.start_pc) + rh.code_size)) + { + return reinterpret_cast(rh.start_pc); + } + } + + return nullptr; +} + +static bool InstallFunctionTableCallback(const void* owner, void* start_pc, u32 code_size) +{ + if (code_size < UNWIND_HANDLER_ALLOC_SIZE) + { + Log_ErrorPrintf("Invalid code size: %u @ %p", code_size, UNWIND_HANDLER_ALLOC_SIZE); + return false; + } + + if (!RtlInstallFunctionTableCallback(reinterpret_cast(owner) | 0x3, reinterpret_cast(start_pc), + static_cast(code_size), &GetRuntimeFunctionCallback, nullptr, nullptr)) + { + Log_ErrorPrintf("RtlInstallFunctionTableCallback() failed: %08X", GetLastError()); + return false; + } + + // This is only valid on x86 for now. +#ifndef CPU_X64 + Log_ErrorPrint("Exception unwind codegen not implemented"); + return false; +#else + UnwindHandler* uh = static_cast(start_pc); + ULONG old_protection; + if (!VirtualProtectFromApp(uh, UNWIND_HANDLER_ALLOC_SIZE, PAGE_READWRITE, &old_protection)) + { + Log_ErrorPrintf("VirtualProtectFromApp(RW) for exception handler failed: %08X", GetLastError()); + return false; + } + + uh->runtime_function.BeginAddress = UNWIND_HANDLER_ALLOC_SIZE; + uh->runtime_function.EndAddress = code_size; + uh->runtime_function.UnwindInfoAddress = offsetof(UnwindHandler, unwind_info); + + uh->unwind_info.version = 1; + uh->unwind_info.flags = UNW_FLAG_EHANDLER; + uh->unwind_info.size_of_prologue = 0; + uh->unwind_info.count_of_unwind_codes = 0; + uh->unwind_info.frame_register = 0; + uh->unwind_info.frame_offset_scaled = 0; + uh->unwind_info.exception_handler_address = offsetof(UnwindHandler, exception_handler_code); + + // mov rax, handler + const void* handler = UnwindExceptionHandler; + uh->exception_handler_code[0] = 0x48; + uh->exception_handler_code[1] = 0xb8; + std::memcpy(&uh->exception_handler_code[2], &handler, sizeof(handler)); + + // jmp rax + uh->exception_handler_code[10] = 0xff; + uh->exception_handler_code[11] = 0xe0; + + if (!VirtualProtectFromApp(uh, UNWIND_HANDLER_ALLOC_SIZE, PAGE_EXECUTE_READ, &old_protection)) + { + Log_ErrorPrintf("VirtualProtectFromApp(RX) for exception handler failed: %08X", GetLastError()); + return false; + } + + return true; +#endif +} + +u32 GetHandlerCodeSize() +{ + return UNWIND_HANDLER_ALLOC_SIZE; +} + #elif defined(USE_SIGSEGV) static struct sigaction s_old_sigsegv_action; @@ -203,9 +345,21 @@ static void SIGSEGVHandler(int sig, siginfo_t* info, void* ctx) sa.sa_handler(sig); } +u32 GetHandlerCodeSize() +{ + return 0; +} + +#else + +u32 GetHandlerCodeSize() +{ + return 0; +} + #endif -bool InstallHandler(void* owner, Callback callback) +bool InstallHandler(const void* owner, void* start_pc, u32 code_size, Callback callback) { bool was_empty; { @@ -217,31 +371,24 @@ bool InstallHandler(void* owner, Callback callback) } was_empty = m_handlers.empty(); - m_handlers.push_back(RegisteredHandler{owner, std::move(callback)}); } if (was_empty) { -#if defined(_WIN32) && (defined(CPU_X64) || defined(CPU_AARCH64)) +#if defined(_WIN32) && !defined(_UWP) && (defined(CPU_X64) || defined(CPU_AARCH64)) s_veh_handle = AddVectoredExceptionHandler(1, ExceptionHandler); if (!s_veh_handle) { Log_ErrorPrint("Failed to add vectored exception handler"); return false; } -#elif defined(USE_SIGSEGV) -#if 0 - // Alternative stack - we'll need this is we ever use the host stack for branches. - stack_t signal_stack = {}; - signal_stack.ss_sp = malloc(SIGSTKSZ); - signal_stack.ss_size = SIGSTKSZ; - if (sigaltstack(&signal_stack, nullptr)) +#elif defined(_UWP) + if (!InstallFunctionTableCallback(owner, start_pc, code_size)) { - Log_ErrorPrintf("signaltstack() failed: %d", errno); + Log_ErrorPrint("Failed to install function table callback"); return false; } -#endif - +#elif defined(USE_SIGSEGV) struct sigaction sa = {}; sa.sa_sigaction = SIGSEGVHandler; sa.sa_flags = SA_SIGINFO; @@ -264,10 +411,11 @@ bool InstallHandler(void* owner, Callback callback) #endif } + m_handlers.push_back(RegisteredHandler{callback, owner, start_pc, code_size}); return true; } -bool RemoveHandler(void* owner) +bool RemoveHandler(const void* owner) { std::lock_guard guard(m_handler_lock); auto it = std::find_if(m_handlers.begin(), m_handlers.end(), @@ -279,9 +427,11 @@ bool RemoveHandler(void* owner) if (m_handlers.empty()) { -#if defined(_WIN32) && (defined(CPU_X64) || defined(CPU_AARCH64)) +#if defined(_WIN32) && !defined(_UWP) && (defined(CPU_X64) || defined(CPU_AARCH64)) RemoveVectoredExceptionHandler(s_veh_handle); s_veh_handle = nullptr; +#elif defined(_UWP) + // nothing to do here, any unregistered regions will be ignored #elif defined(USE_SIGSEGV) // restore old signal handler #if defined(__APPLE__) || defined(__aarch64__) diff --git a/src/common/page_fault_handler.h b/src/common/page_fault_handler.h index 67ef38cbd..f4b57b231 100644 --- a/src/common/page_fault_handler.h +++ b/src/common/page_fault_handler.h @@ -8,10 +8,12 @@ enum class HandlerResult ExecuteNextHandler, }; -using Callback = HandlerResult(*)(void* exception_pc, void* fault_address, bool is_write); +using Callback = HandlerResult (*)(void* exception_pc, void* fault_address, bool is_write); using Handle = void*; -bool InstallHandler(void* owner, Callback callback); -bool RemoveHandler(void* owner); +u32 GetHandlerCodeSize(); + +bool InstallHandler(const void* owner, void* start_pc, u32 code_size, Callback callback); +bool RemoveHandler(const void* owner); } // namespace Common::PageFaultHandler diff --git a/src/common/timer.cpp b/src/common/timer.cpp index 22a3a10d0..79b49684d 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp @@ -93,6 +93,7 @@ void Timer::SleepUntil(Value value, bool exact) if (diff <= 0) return; +#ifndef _UWP HANDLE timer = GetSleepTimer(); if (timer) { @@ -110,6 +111,7 @@ void Timer::SleepUntil(Value value, bool exact) return; } } +#endif // falling back to sleep... bad. Sleep(static_cast(static_cast(diff) / 1000000)); diff --git a/src/common/window_info.cpp b/src/common/window_info.cpp index 65c8a51b7..ca2b2f901 100644 --- a/src/common/window_info.cpp +++ b/src/common/window_info.cpp @@ -2,7 +2,7 @@ #include "common/log.h" Log_SetChannel(WindowInfo); -#if defined(_WIN32) +#if defined(_WIN32) && !defined(_UWP) #include "common/windows_headers.h" #include diff --git a/src/common/window_info.h b/src/common/window_info.h index 626d54753..be1cc15b4 100644 --- a/src/common/window_info.h +++ b/src/common/window_info.h @@ -8,6 +8,7 @@ struct WindowInfo { Surfaceless, Win32, + WinRT, X11, Wayland, MacOS, diff --git a/src/common/windows_headers.h b/src/common/windows_headers.h index 9d882c640..0e242a789 100644 --- a/src/common/windows_headers.h +++ b/src/common/windows_headers.h @@ -7,11 +7,13 @@ #define NOMINMAX 1 #endif -// require vista+ +// require vista+, but don't set it when compiling for UWP +#ifndef WINAPI_FAMILY #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif #define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif #include diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index 619d11866..839a056c0 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -25,7 +25,7 @@ static constexpr u32 RECOMPILE_COUNT_TO_FALL_BACK_TO_INTERPRETER = 20; #ifdef WITH_RECOMPILER // Currently remapping the code buffer doesn't work in macOS or Haiku. -#if !defined(__HAIKU__) && !defined(__APPLE__) +#if !defined(__HAIKU__) && !defined(__APPLE__) && !defined(_UWP) #define USE_STATIC_CODE_BUFFER 1 #endif @@ -822,7 +822,10 @@ bool InitializeFastmem() Assert(mode != CPUFastmemMode::MMap); #endif - if (!Common::PageFaultHandler::InstallHandler(&s_host_code_map, handler)) + s_code_buffer.ReserveCode(Common::PageFaultHandler::GetHandlerCodeSize()); + + if (!Common::PageFaultHandler::InstallHandler(&s_host_code_map, s_code_buffer.GetCodePointer(), + s_code_buffer.GetTotalSize(), handler)) { Log_ErrorPrintf("Failed to install page fault handler"); return false; diff --git a/src/core/settings.h b/src/core/settings.h index 26c8d9d75..170cef3c0 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -369,10 +369,12 @@ struct Settings static constexpr CPUFastmemMode DEFAULT_CPU_FASTMEM_MODE = CPUFastmemMode::Disabled; #endif -#ifndef __ANDROID__ - static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Cubeb; -#else +#if defined(__ANDROID__) static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::OpenSLES; +#elif defined(_UWP) + static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::XAudio2; +#else + static constexpr AudioBackend DEFAULT_AUDIO_BACKEND = AudioBackend::Cubeb; #endif static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan; diff --git a/src/duckstation-uwp/.gitignore b/src/duckstation-uwp/.gitignore new file mode 100644 index 000000000..2297123c5 --- /dev/null +++ b/src/duckstation-uwp/.gitignore @@ -0,0 +1,6 @@ +database/ +inputprofiles/ +resources/ +shaders/ +BundleArtifacts/ +duckstation-uwp.pfx \ No newline at end of file diff --git a/src/duckstation-uwp/Assets/LargeTile.scale-100.png b/src/duckstation-uwp/Assets/LargeTile.scale-100.png new file mode 100644 index 000000000..4b0ef4b94 Binary files /dev/null and b/src/duckstation-uwp/Assets/LargeTile.scale-100.png differ diff --git a/src/duckstation-uwp/Assets/LargeTile.scale-125.png b/src/duckstation-uwp/Assets/LargeTile.scale-125.png new file mode 100644 index 000000000..45cf55b1e Binary files /dev/null and b/src/duckstation-uwp/Assets/LargeTile.scale-125.png differ diff --git a/src/duckstation-uwp/Assets/LargeTile.scale-150.png b/src/duckstation-uwp/Assets/LargeTile.scale-150.png new file mode 100644 index 000000000..014868e37 Binary files /dev/null and b/src/duckstation-uwp/Assets/LargeTile.scale-150.png differ diff --git a/src/duckstation-uwp/Assets/LargeTile.scale-200.png b/src/duckstation-uwp/Assets/LargeTile.scale-200.png new file mode 100644 index 000000000..2d481044d Binary files /dev/null and b/src/duckstation-uwp/Assets/LargeTile.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/LargeTile.scale-400.png b/src/duckstation-uwp/Assets/LargeTile.scale-400.png new file mode 100644 index 000000000..1e095e886 Binary files /dev/null and b/src/duckstation-uwp/Assets/LargeTile.scale-400.png differ diff --git a/src/duckstation-uwp/Assets/LockScreenLogo.scale-200.png b/src/duckstation-uwp/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 000000000..735f57adb Binary files /dev/null and b/src/duckstation-uwp/Assets/LockScreenLogo.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/SmallTile.scale-100.png b/src/duckstation-uwp/Assets/SmallTile.scale-100.png new file mode 100644 index 000000000..22154a005 Binary files /dev/null and b/src/duckstation-uwp/Assets/SmallTile.scale-100.png differ diff --git a/src/duckstation-uwp/Assets/SmallTile.scale-125.png b/src/duckstation-uwp/Assets/SmallTile.scale-125.png new file mode 100644 index 000000000..a9692405e Binary files /dev/null and b/src/duckstation-uwp/Assets/SmallTile.scale-125.png differ diff --git a/src/duckstation-uwp/Assets/SmallTile.scale-150.png b/src/duckstation-uwp/Assets/SmallTile.scale-150.png new file mode 100644 index 000000000..eb5b166e5 Binary files /dev/null and b/src/duckstation-uwp/Assets/SmallTile.scale-150.png differ diff --git a/src/duckstation-uwp/Assets/SmallTile.scale-200.png b/src/duckstation-uwp/Assets/SmallTile.scale-200.png new file mode 100644 index 000000000..2712512e8 Binary files /dev/null and b/src/duckstation-uwp/Assets/SmallTile.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/SmallTile.scale-400.png b/src/duckstation-uwp/Assets/SmallTile.scale-400.png new file mode 100644 index 000000000..3570a150c Binary files /dev/null and b/src/duckstation-uwp/Assets/SmallTile.scale-400.png differ diff --git a/src/duckstation-uwp/Assets/SplashScreen.scale-100.png b/src/duckstation-uwp/Assets/SplashScreen.scale-100.png new file mode 100644 index 000000000..6231d0d2d Binary files /dev/null and b/src/duckstation-uwp/Assets/SplashScreen.scale-100.png differ diff --git a/src/duckstation-uwp/Assets/SplashScreen.scale-125.png b/src/duckstation-uwp/Assets/SplashScreen.scale-125.png new file mode 100644 index 000000000..d75cd027f Binary files /dev/null and b/src/duckstation-uwp/Assets/SplashScreen.scale-125.png differ diff --git a/src/duckstation-uwp/Assets/SplashScreen.scale-150.png b/src/duckstation-uwp/Assets/SplashScreen.scale-150.png new file mode 100644 index 000000000..76cb3e102 Binary files /dev/null and b/src/duckstation-uwp/Assets/SplashScreen.scale-150.png differ diff --git a/src/duckstation-uwp/Assets/SplashScreen.scale-200.png b/src/duckstation-uwp/Assets/SplashScreen.scale-200.png new file mode 100644 index 000000000..1008aca35 Binary files /dev/null and b/src/duckstation-uwp/Assets/SplashScreen.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/SplashScreen.scale-400.png b/src/duckstation-uwp/Assets/SplashScreen.scale-400.png new file mode 100644 index 000000000..645c6bb6a Binary files /dev/null and b/src/duckstation-uwp/Assets/SplashScreen.scale-400.png differ diff --git a/src/duckstation-uwp/Assets/Square150x150Logo.scale-100.png b/src/duckstation-uwp/Assets/Square150x150Logo.scale-100.png new file mode 100644 index 000000000..6d132a875 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square150x150Logo.scale-100.png differ diff --git a/src/duckstation-uwp/Assets/Square150x150Logo.scale-125.png b/src/duckstation-uwp/Assets/Square150x150Logo.scale-125.png new file mode 100644 index 000000000..f05e4604c Binary files /dev/null and b/src/duckstation-uwp/Assets/Square150x150Logo.scale-125.png differ diff --git a/src/duckstation-uwp/Assets/Square150x150Logo.scale-150.png b/src/duckstation-uwp/Assets/Square150x150Logo.scale-150.png new file mode 100644 index 000000000..cd2cac9a0 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square150x150Logo.scale-150.png differ diff --git a/src/duckstation-uwp/Assets/Square150x150Logo.scale-200.png b/src/duckstation-uwp/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 000000000..863e4ef91 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square150x150Logo.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/Square150x150Logo.scale-400.png b/src/duckstation-uwp/Assets/Square150x150Logo.scale-400.png new file mode 100644 index 000000000..045913a3c Binary files /dev/null and b/src/duckstation-uwp/Assets/Square150x150Logo.scale-400.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png new file mode 100644 index 000000000..db57efd60 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png new file mode 100644 index 000000000..5cf25729b Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png new file mode 100644 index 000000000..4d1f4620f Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png new file mode 100644 index 000000000..137ac4497 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png new file mode 100644 index 000000000..6374d0219 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png new file mode 100644 index 000000000..db57efd60 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-16.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png new file mode 100644 index 000000000..4d1f4620f Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-256.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png new file mode 100644 index 000000000..137ac4497 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-32.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png new file mode 100644 index 000000000..6374d0219 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.altform-unplated_targetsize-48.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.scale-100.png b/src/duckstation-uwp/Assets/Square44x44Logo.scale-100.png new file mode 100644 index 000000000..44b92a911 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.scale-100.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.scale-125.png b/src/duckstation-uwp/Assets/Square44x44Logo.scale-125.png new file mode 100644 index 000000000..3890bbfd5 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.scale-125.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.scale-150.png b/src/duckstation-uwp/Assets/Square44x44Logo.scale-150.png new file mode 100644 index 000000000..9a99b0af1 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.scale-150.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.scale-200.png b/src/duckstation-uwp/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 000000000..95ae49964 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.scale-400.png b/src/duckstation-uwp/Assets/Square44x44Logo.scale-400.png new file mode 100644 index 000000000..b84abd2fe Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.scale-400.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-16.png b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-16.png new file mode 100644 index 000000000..78927eb6d Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-16.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-24.png b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-24.png new file mode 100644 index 000000000..82ed7fa0c Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-24.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 000000000..26eb22fb4 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-256.png b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-256.png new file mode 100644 index 000000000..db333e184 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-256.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-32.png b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-32.png new file mode 100644 index 000000000..89a192373 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-32.png differ diff --git a/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-48.png b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-48.png new file mode 100644 index 000000000..75d0a3ab5 Binary files /dev/null and b/src/duckstation-uwp/Assets/Square44x44Logo.targetsize-48.png differ diff --git a/src/duckstation-uwp/Assets/StoreLogo.backup.png b/src/duckstation-uwp/Assets/StoreLogo.backup.png new file mode 100644 index 000000000..7385b56c0 Binary files /dev/null and b/src/duckstation-uwp/Assets/StoreLogo.backup.png differ diff --git a/src/duckstation-uwp/Assets/StoreLogo.scale-100.png b/src/duckstation-uwp/Assets/StoreLogo.scale-100.png new file mode 100644 index 000000000..e2a5aba4c Binary files /dev/null and b/src/duckstation-uwp/Assets/StoreLogo.scale-100.png differ diff --git a/src/duckstation-uwp/Assets/StoreLogo.scale-125.png b/src/duckstation-uwp/Assets/StoreLogo.scale-125.png new file mode 100644 index 000000000..d8120d319 Binary files /dev/null and b/src/duckstation-uwp/Assets/StoreLogo.scale-125.png differ diff --git a/src/duckstation-uwp/Assets/StoreLogo.scale-150.png b/src/duckstation-uwp/Assets/StoreLogo.scale-150.png new file mode 100644 index 000000000..252be5560 Binary files /dev/null and b/src/duckstation-uwp/Assets/StoreLogo.scale-150.png differ diff --git a/src/duckstation-uwp/Assets/StoreLogo.scale-200.png b/src/duckstation-uwp/Assets/StoreLogo.scale-200.png new file mode 100644 index 000000000..30ad00de0 Binary files /dev/null and b/src/duckstation-uwp/Assets/StoreLogo.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/StoreLogo.scale-400.png b/src/duckstation-uwp/Assets/StoreLogo.scale-400.png new file mode 100644 index 000000000..2f9adfad7 Binary files /dev/null and b/src/duckstation-uwp/Assets/StoreLogo.scale-400.png differ diff --git a/src/duckstation-uwp/Assets/Wide310x150Logo.scale-200.png b/src/duckstation-uwp/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 000000000..288995b39 Binary files /dev/null and b/src/duckstation-uwp/Assets/Wide310x150Logo.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/WideTile.scale-100.png b/src/duckstation-uwp/Assets/WideTile.scale-100.png new file mode 100644 index 000000000..11fbe3620 Binary files /dev/null and b/src/duckstation-uwp/Assets/WideTile.scale-100.png differ diff --git a/src/duckstation-uwp/Assets/WideTile.scale-125.png b/src/duckstation-uwp/Assets/WideTile.scale-125.png new file mode 100644 index 000000000..53fb67842 Binary files /dev/null and b/src/duckstation-uwp/Assets/WideTile.scale-125.png differ diff --git a/src/duckstation-uwp/Assets/WideTile.scale-150.png b/src/duckstation-uwp/Assets/WideTile.scale-150.png new file mode 100644 index 000000000..d87729e47 Binary files /dev/null and b/src/duckstation-uwp/Assets/WideTile.scale-150.png differ diff --git a/src/duckstation-uwp/Assets/WideTile.scale-200.png b/src/duckstation-uwp/Assets/WideTile.scale-200.png new file mode 100644 index 000000000..6231d0d2d Binary files /dev/null and b/src/duckstation-uwp/Assets/WideTile.scale-200.png differ diff --git a/src/duckstation-uwp/Assets/WideTile.scale-400.png b/src/duckstation-uwp/Assets/WideTile.scale-400.png new file mode 100644 index 000000000..1008aca35 Binary files /dev/null and b/src/duckstation-uwp/Assets/WideTile.scale-400.png differ diff --git a/src/duckstation-uwp/Assets/duck_128.png b/src/duckstation-uwp/Assets/duck_128.png new file mode 100644 index 000000000..bc6655bbd Binary files /dev/null and b/src/duckstation-uwp/Assets/duck_128.png differ diff --git a/src/duckstation-uwp/Package.appxmanifest b/src/duckstation-uwp/Package.appxmanifest new file mode 100644 index 000000000..7982fce24 --- /dev/null +++ b/src/duckstation-uwp/Package.appxmanifest @@ -0,0 +1,52 @@ + + + + + + DuckStation + Stenzek + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + .cue + .chd + .ecm + .iso + .mds + .psexe + .psf + .minipsf + .m3u + + DuckStation Disc Image + Assets\duck_128.png + + + + + + + + + + + \ No newline at end of file diff --git a/src/duckstation-uwp/PropertySheet.props b/src/duckstation-uwp/PropertySheet.props new file mode 100644 index 000000000..e34141b01 --- /dev/null +++ b/src/duckstation-uwp/PropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/duckstation-uwp/duckstation-uwp.vcxproj b/src/duckstation-uwp/duckstation-uwp.vcxproj new file mode 100644 index 000000000..6af60b175 --- /dev/null +++ b/src/duckstation-uwp/duckstation-uwp.vcxproj @@ -0,0 +1,211 @@ + + + + + true + false + true + {e2a6cea9-9537-4c61-b637-81f1f17ef638} + duckstation-uwp + DuckStationUWP + en-US + 15.0 + true + Windows Store + 10.0 + 10.0.19041.0 + 10.0.17134.0 + false + true + false + + + $(SolutionDir)build\$(Configuration)-$(Platform)\$(ProjectName)\ + $(SolutionDir)build\$(Configuration)-$(Platform)\$(ProjectName)\ + false + $(ProjectName) + False + True + duckstation-uwp.pfx + SHA256 + False + False + True + Always + x64 + 0 + + + + + + + + + + Level4 + %(AdditionalOptions) /bigobj + + + WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + + + false + + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + + true + + + true + + + true + + + true + + + true + + + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + $(RootBuildDir)frontend-common\frontend-common.lib;%(AdditionalDependencies) + + + + diff --git a/src/duckstation-uwp/duckstation-uwp.vcxproj.filters b/src/duckstation-uwp/duckstation-uwp.vcxproj.filters new file mode 100644 index 000000000..149abe69f --- /dev/null +++ b/src/duckstation-uwp/duckstation-uwp.vcxproj.filters @@ -0,0 +1,304 @@ + + + + + + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + Data\resources + + + + + + + + {db9086fe-bacc-4437-8272-1aa6642082db} + + + {fa7724ab-a364-49f2-9cdb-11307aa3f63e} + + + {8eda719e-58c6-484f-b00f-90ee69cd6cb5} + + + {1d471d6d-942e-4e06-9b74-36c5620c9d43} + + + {bf7c6843-57df-4e82-bf4e-7ffea724e92d} + + + {26b9c7e0-8acd-44be-848e-7da8e32238fa} + + + {1c1f164b-4184-422c-8d95-9c4cbb6a21eb} + + + + + + + Data\database + + + Data\database + + + Data\shaders + + + Data\shaders + + + Data\shaders + + + Data\shaders + + + Data\shaders\dolphinfx + + + Data\shaders\dolphinfx + + + Data\shaders\dolphinfx + + + Data\inputprofiles + + + Data\inputprofiles + + + Data\inputprofiles + + + Data\inputprofiles + + + Data\inputprofiles + + + + + + + + + + Data\resources + + + + + Data\database + + + Data\database + + + + + Data\database + + + \ No newline at end of file diff --git a/src/duckstation-uwp/packages.config b/src/duckstation-uwp/packages.config new file mode 100644 index 000000000..6d1e442a5 --- /dev/null +++ b/src/duckstation-uwp/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/duckstation-uwp/uwp_host_interface.cpp b/src/duckstation-uwp/uwp_host_interface.cpp new file mode 100644 index 000000000..1de20d6c5 --- /dev/null +++ b/src/duckstation-uwp/uwp_host_interface.cpp @@ -0,0 +1,753 @@ +#include "uwp_host_interface.h" +#include "common/assert.h" +#include "common/byte_stream.h" +#include "common/file_system.h" +#include "common/log.h" +#include "common/string_util.h" +#include "core/controller.h" +#include "core/gpu.h" +#include "core/host_display.h" +#include "core/system.h" +#include "frontend-common/controller_interface.h" +#include "frontend-common/d3d11_host_display.h" +#include "frontend-common/d3d12_host_display.h" +#include "frontend-common/fullscreen_ui.h" +#include "frontend-common/icon.h" +#include "frontend-common/imgui_styles.h" +#include "frontend-common/ini_settings_interface.h" +#include "imgui.h" +#include "imgui_internal.h" +#include "imgui_stdlib.h" +#include "uwp_key_map.h" +#include +#include +Log_SetChannel(UWPHostInterface); + +#include +#include +#include +#include +#include + +static bool IsRunningOnXbox() +{ + const auto version_info = winrt::Windows::System::Profile::AnalyticsInfo::VersionInfo(); + const auto device_family = version_info.DeviceFamily(); + return (device_family == L"Windows.Xbox"); +} + +UWPHostInterface::UWPHostInterface() = default; + +UWPHostInterface::~UWPHostInterface() = default; + +winrt::Windows::ApplicationModel::Core::IFrameworkView UWPHostInterface::CreateView() +{ + return *this; +} + +void UWPHostInterface::Initialize(const winrt::Windows::ApplicationModel::Core::CoreApplicationView& a) +{ + winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({this, &UWPHostInterface::OnSuspending}); + winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({this, &UWPHostInterface::OnResuming}); +} + +void UWPHostInterface::Load(const winrt::hstring&) {} + +void UWPHostInterface::Uninitialize() {} + +const char* UWPHostInterface::GetFrontendName() const +{ + return "DuckStation UWP Frontend"; +} + +bool UWPHostInterface::Initialize() +{ + Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEBUG); + if (!SetDirectories()) + return false; + + m_settings_interface = std::make_unique(GetSettingsFileName()); + m_flags.force_fullscreen_ui = true; + m_fullscreen_ui_enabled = true; + + if (!CommonHostInterface::Initialize()) + return false; + + SetImGuiKeyMap(); + + const bool start_fullscreen = m_flags.start_fullscreen || g_settings.start_fullscreen; + if (!CreateDisplay(start_fullscreen)) + { + Log_ErrorPrintf("Failed to create host display"); + return false; + } + + return true; +} + +void UWPHostInterface::Shutdown() +{ + DestroyDisplay(); + + CommonHostInterface::Shutdown(); +} + +void UWPHostInterface::SetDefaultSettings(SettingsInterface& si) +{ + CommonHostInterface::SetDefaultSettings(si); + + si.SetStringValue("CPU", "FastmemMode", "LUT"); + si.SetStringValue("Main", "ControllerBackend", "XInput"); + si.AddToStringList("GameList", "RecursivePaths", GetUserDirectoryRelativePath("games").c_str()); + + if (IsRunningOnXbox()) + SetDefaultSettingsForXbox(si); +} + +bool UWPHostInterface::CreateDisplay(bool fullscreen) +{ + Assert(!m_display); + + m_appview = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); + m_appview.PreferredLaunchWindowingMode( + fullscreen ? winrt::Windows::UI::ViewManagement::ApplicationViewWindowingMode::FullScreen : + winrt::Windows::UI::ViewManagement::ApplicationViewWindowingMode::Auto); + + m_window.Activate(); + + const auto di = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); + const auto hdi = winrt::Windows::Graphics::Display::Core::HdmiDisplayInformation::GetForCurrentView(); + const s32 resolution_scale = static_cast(di.ResolutionScale()); + + WindowInfo wi; + wi.type = WindowInfo::Type::WinRT; + wi.window_handle = winrt::get_unknown(m_window); + wi.surface_scale = static_cast(resolution_scale) / 100.0f; + wi.surface_width = static_cast(m_window.Bounds().Width * wi.surface_scale); + wi.surface_height = static_cast(m_window.Bounds().Height * wi.surface_scale); + if (hdi) + { + try + { + const auto dm = hdi.GetCurrentDisplayMode(); + const u32 hdmi_width = dm.ResolutionWidthInRawPixels(); + const u32 hdmi_height = dm.ResolutionHeightInRawPixels(); + wi.surface_refresh_rate = static_cast(dm.RefreshRate()); + Log_InfoPrintf("HDMI mode: %ux%u @ %.2f hz", hdmi_width, hdmi_height, wi.surface_refresh_rate); + + // If we're running on Xbox, use the HDMI mode instead of the CoreWindow size. + // In UWP, the CoreWindow is always 1920x1080, even when running at 4K. + if (IsRunningOnXbox()) + { + GAMING_DEVICE_MODEL_INFORMATION gdinfo = {}; + if (SUCCEEDED(GetGamingDeviceModelInformation(&gdinfo)) && gdinfo.vendorId == GAMING_DEVICE_VENDOR_ID_MICROSOFT) + { + if (gdinfo.deviceId != GAMING_DEVICE_DEVICE_ID_XBOX_ONE) + { + Log_InfoPrintf("Overriding core window size %ux%u with HDMI size %ux%u", wi.surface_width, + wi.surface_height, hdmi_width, hdmi_height); + wi.surface_scale *= static_cast(hdmi_width) / static_cast(wi.surface_width); + wi.surface_width = hdmi_width; + wi.surface_height = hdmi_height; + } + } + } + } + catch (const winrt::hresult_error&) + { + } + } + + if (g_settings.gpu_renderer == GPURenderer::HardwareD3D12) + m_display = std::make_unique(); + else + m_display = std::make_unique(); + + if (!m_display->CreateRenderDevice(wi, g_settings.gpu_adapter, g_settings.gpu_use_debug_device, + g_settings.gpu_threaded_presentation) || + !m_display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device, + g_settings.gpu_threaded_presentation) || + !CreateHostDisplayResources()) + { + m_display->DestroyRenderDevice(); + m_display.reset(); + ReportError("Failed to create/initialize display render device"); + return false; + } + + if (!CreateHostDisplayResources()) + Log_WarningPrint("Failed to create host display resources"); + + Log_InfoPrintf("Host display initialized at %ux%u resolution", m_display->GetWindowWidth(), + m_display->GetWindowHeight()); + return true; +} + +void UWPHostInterface::DestroyDisplay() +{ + ReleaseHostDisplayResources(); + + if (m_display) + m_display->DestroyRenderDevice(); + + m_display.reset(); +} + +bool UWPHostInterface::AcquireHostDisplay() +{ + return true; +} + +void UWPHostInterface::ReleaseHostDisplay() +{ + // restore vsync, since we don't want to burn cycles at the menu + m_display->SetVSync(true); +} + +void UWPHostInterface::PollAndUpdate() +{ + CommonHostInterface::PollAndUpdate(); + + ImGuiIO& io = ImGui::GetIO(); + if (m_text_input_requested != io.WantTextInput) + { + const bool activate = io.WantTextInput; + Log_InfoPrintf("%s input pane...", activate ? "showing" : "hiding"); + + m_text_input_requested = activate; + m_dispatcher.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [this, activate]() { + const auto input_pane = winrt::Windows::UI::ViewManagement::InputPane::GetForCurrentView(); + if (input_pane) + { + if (activate) + input_pane.TryShow(); + else + input_pane.TryHide(); + } + }); + } +} + +void UWPHostInterface::RequestExit() +{ + m_shutdown_flag.store(true); + m_dispatcher.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, + [this]() { winrt::Windows::ApplicationModel::Core::CoreApplication::Exit(); }); +} + +void UWPHostInterface::Run() +{ + if (!Initialize()) + { + Shutdown(); + return; + } + + m_emulation_thread = std::thread(&UWPHostInterface::EmulationThreadEntryPoint, this); + + m_dispatcher.ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessUntilQuit); + m_shutdown_flag.store(true); + m_emulation_thread.join(); +} + +void UWPHostInterface::EmulationThreadEntryPoint() +{ + if (m_fullscreen_ui_enabled) + { + FullscreenUI::SetDebugMenuAllowed(true); + FullscreenUI::QueueGameListRefresh(); + } + + // process events to pick up controllers before updating input map + PollAndUpdate(); + UpdateInputMap(); + + if (m_was_running_on_suspend && ShouldSaveResumeState()) + ResumeSystemFromMostRecentState(); + + while (!m_shutdown_flag.load()) + { + RunCallbacks(); + PollAndUpdate(); + + ImGui::NewFrame(); + + if (System::IsRunning()) + { + if (m_display_all_frames) + System::RunFrame(); + else + System::RunFrames(); + + UpdateControllerMetaState(); + if (m_frame_step_request) + { + m_frame_step_request = false; + PauseSystem(true); + } + } + + // rendering + { + DrawImGuiWindows(); + ImGui::Render(); + ImGui::EndFrame(); + + m_display->Render(); + + if (System::IsRunning()) + { + System::UpdatePerformanceCounters(); + + if (m_throttler_enabled) + System::Throttle(); + } + } + } + + // Save state on exit so it can be resumed + if (!System::IsShutdown()) + PowerOffSystem(ShouldSaveResumeState()); +} + +void UWPHostInterface::ReportMessage(const char* message) +{ + Log_InfoPrint(message); + AddOSDMessage(message, 10.0f); +} + +void UWPHostInterface::ReportError(const char* message) +{ + Log_ErrorPrint(message); + + if (!m_display) + return; + + const bool was_in_frame = GImGui->FrameCount != GImGui->FrameCountEnded; + if (was_in_frame) + ImGui::EndFrame(); + + bool done = false; + while (!done) + { + RunCallbacks(); + PollAndUpdate(); + if (m_fullscreen_ui_enabled) + FullscreenUI::SetImGuiNavInputs(); + + ImGui::NewFrame(); + done = FullscreenUI::DrawErrorWindow(message); + ImGui::EndFrame(); + m_display->Render(); + } + + if (was_in_frame) + ImGui::NewFrame(); +} + +bool UWPHostInterface::ConfirmMessage(const char* message) +{ + Log_InfoPrintf("Confirm: %s", message); + + if (!m_display) + return true; + + const bool was_in_frame = GImGui->FrameCount != GImGui->FrameCountEnded; + if (was_in_frame) + ImGui::EndFrame(); + + bool done = false; + bool result = true; + while (!done) + { + RunCallbacks(); + PollAndUpdate(); + if (m_fullscreen_ui_enabled) + FullscreenUI::SetImGuiNavInputs(); + + ImGui::NewFrame(); + done = FullscreenUI::DrawConfirmWindow(message, &result); + ImGui::EndFrame(); + m_display->Render(); + } + + if (was_in_frame) + ImGui::NewFrame(); + + return result; +} + +void UWPHostInterface::RunLater(std::function callback) +{ + std::unique_lock lock(m_queued_callbacks_lock); + m_queued_callbacks.push_back(std::move(callback)); +} + +bool UWPHostInterface::IsFullscreen() const +{ + return m_appview.IsFullScreenMode(); +} + +bool UWPHostInterface::SetFullscreen(bool enabled) +{ + m_dispatcher.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [this, enabled]() { + if (enabled) + m_appview.TryEnterFullScreenMode(); + else + m_appview.ExitFullScreenMode(); + }); + + return true; +} + +void UWPHostInterface::RunCallbacks() +{ + std::unique_lock lock(m_queued_callbacks_lock); + + while (!m_queued_callbacks.empty()) + { + auto callback = std::move(m_queued_callbacks.front()); + m_queued_callbacks.pop_front(); + lock.unlock(); + callback(); + lock.lock(); + } +} + +void UWPHostInterface::SetWindow(const winrt::Windows::UI::Core::CoreWindow& window) +{ + m_window = window; + m_dispatcher = m_window.Dispatcher(); + + window.Closed({this, &UWPHostInterface::OnClosed}); + window.SizeChanged({this, &UWPHostInterface::OnSizeChanged}); + window.KeyDown({this, &UWPHostInterface::OnKeyDown}); + window.KeyUp({this, &UWPHostInterface::OnKeyUp}); + window.CharacterReceived({this, &UWPHostInterface::OnCharacterReceived}); + window.PointerPressed({this, &UWPHostInterface::OnPointerPressed}); + window.PointerReleased({this, &UWPHostInterface::OnPointerPressed}); + window.PointerMoved({this, &UWPHostInterface::OnPointerMoved}); + window.PointerWheelChanged({this, &UWPHostInterface::OnPointerWheelChanged}); +} + +bool UWPHostInterface::SetDirectories() +{ + const auto install_location = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation(); + m_program_directory = StringUtil::WideStringToUTF8String(install_location.Path()); + if (m_program_directory.empty()) + { + Log_ErrorPrintf("Failed to get install location"); + return false; + } + + Log_InfoPrintf("Program directory: %s", m_program_directory.c_str()); + + const auto local_location = winrt::Windows::Storage::ApplicationData::Current().LocalFolder(); + m_user_directory = StringUtil::WideStringToUTF8String(local_location.Path()); + if (m_user_directory.empty()) + { + Log_ErrorPrintf("Failed to get user directory"); + return false; + } + + Log_InfoPrintf("User directory: %s", m_user_directory.c_str()); + return true; +} + +void UWPHostInterface::OnSuspending(const IInspectable&, + const winrt::Windows::ApplicationModel::SuspendingEventArgs& args) +{ + if (IsEmulationThreadRunning()) + { + RunLater([this]() { + if (ShouldSaveResumeState()) + SaveResumeSaveState(); + + m_was_running_on_suspend.store(System::IsRunning()); + PauseSystem(true); + m_suspend_sync_event.Signal(); + }); + + m_suspend_sync_event.Wait(); + } +} + +void UWPHostInterface::OnResuming(const IInspectable&, const IInspectable&) +{ + if (IsEmulationThreadRunning()) + { + if (m_was_running_on_suspend.load()) + RunLater([this]() { PauseSystem(false); }); + } + else + { + RunLater([this]() { + if (ShouldSaveResumeState()) + ResumeSystemFromMostRecentState(); + }); + } +} + +void UWPHostInterface::OnClosed(const IInspectable&, const winrt::Windows::UI::Core::CoreWindowEventArgs& args) +{ + if (IsEmulationThreadRunning()) + { + m_shutdown_flag.store(true); + m_emulation_thread.join(); + } + args.Handled(true); +} + +void UWPHostInterface::OnSizeChanged(const IInspectable&, + const winrt::Windows::UI::Core::WindowSizeChangedEventArgs& args) +{ + const auto size = args.Size(); + const s32 width = static_cast(size.Width * m_display->GetWindowScale()); + const s32 height = static_cast(size.Height * m_display->GetWindowScale()); + if (IsEmulationThreadRunning()) + { + RunLater([this, width, height]() { + m_display->ResizeRenderWindow(width, height); + OnHostDisplayResized(); + }); + } + + args.Handled(true); +} + +void UWPHostInterface::OnKeyDown(const IInspectable&, const winrt::Windows::UI::Core::KeyEventArgs& args) +{ + const auto status = args.KeyStatus(); + if (!status.WasKeyDown && !status.IsKeyReleased && IsEmulationThreadRunning()) + { + const HostKeyCode code = static_cast(args.VirtualKey()); + RunLater([this, code]() { + ImGuiIO& io = ImGui::GetIO(); + if (code < countof(io.KeysDown)) + io.KeysDown[code] = true; + + if (!io.WantCaptureKeyboard) + HandleHostKeyEvent(code, 0, true); + }); + } + + args.Handled(true); +} + +void UWPHostInterface::OnKeyUp(const IInspectable&, const winrt::Windows::UI::Core::KeyEventArgs& args) +{ + const auto status = args.KeyStatus(); + if (status.WasKeyDown && status.IsKeyReleased && IsEmulationThreadRunning()) + { + const HostKeyCode code = static_cast(args.VirtualKey()); + RunLater([this, code]() { + ImGuiIO& io = ImGui::GetIO(); + if (code < countof(io.KeysDown)) + io.KeysDown[code] = false; + + if (!io.WantCaptureKeyboard) + HandleHostKeyEvent(code, 0, false); + }); + } + + args.Handled(true); +} + +void UWPHostInterface::OnCharacterReceived(const IInspectable&, + const winrt::Windows::UI::Core::CharacterReceivedEventArgs& args) +{ + if (IsEmulationThreadRunning()) + { + const u32 code = args.KeyCode(); + RunLater([this, code]() { ImGui::GetIO().AddInputCharacter(code); }); + } + + args.Handled(true); +} + +void UWPHostInterface::OnPointerPressed(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args) +{ + const auto pointer = args.CurrentPoint(); + if (pointer.PointerDevice().PointerDeviceType() == winrt::Windows::Devices::Input::PointerDeviceType::Mouse) + UpdateMouseButtonState(pointer); + + args.Handled(true); +} + +void UWPHostInterface::OnPointerReleased(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args) +{ + const auto pointer = args.CurrentPoint(); + if (pointer.PointerDevice().PointerDeviceType() == winrt::Windows::Devices::Input::PointerDeviceType::Mouse) + UpdateMouseButtonState(pointer); + + args.Handled(true); +} + +void UWPHostInterface::OnPointerMoved(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args) +{ + const auto pointer = args.CurrentPoint(); + if (pointer.PointerDevice().PointerDeviceType() == winrt::Windows::Devices::Input::PointerDeviceType::Mouse) + { + const auto pos = pointer.Position(); + const float x = pos.X * m_display->GetWindowScale(); + const float y = pos.Y * m_display->GetWindowScale(); + + if (IsEmulationThreadRunning()) + { + RunLater([this, x, y]() { + m_display->SetMousePosition(static_cast(x), static_cast(y)); + + if (ImGui::GetCurrentContext()) + { + ImGuiIO& io = ImGui::GetIO(); + io.MousePos.x = x; + io.MousePos.y = y; + } + }); + } + + UpdateMouseButtonState(pointer); + } + + args.Handled(true); +} + +void UWPHostInterface::OnPointerWheelChanged(const IInspectable&, + const winrt::Windows::UI::Core::PointerEventArgs& args) +{ + const auto pointer = args.CurrentPoint(); + const auto properties = pointer.Properties(); + const s32 delta = properties.MouseWheelDelta(); + const bool horizontal = properties.IsHorizontalMouseWheel(); + + if (IsEmulationThreadRunning()) + { + RunLater([this, delta, horizontal]() { + if (ImGui::GetCurrentContext()) + { + ImGuiIO& io = ImGui::GetIO(); + const float dw = static_cast(std::clamp(delta, -1, 1)); + if (horizontal) + io.MouseWheelH = dw; + else + io.MouseWheel = dw; + } + }); + } + + args.Handled(true); +} + +void UWPHostInterface::UpdateMouseButtonState(const winrt::Windows::UI::Input::PointerPoint& point) +{ + const auto properties = point.Properties(); + const bool states[3] = {properties.IsLeftButtonPressed(), properties.IsRightButtonPressed(), + properties.IsMiddleButtonPressed()}; + + if (IsEmulationThreadRunning()) + { + RunLater([this, states]() { + if (!ImGui::GetCurrentContext()) + return; + + ImGuiIO& io = ImGui::GetIO(); + for (u32 i = 0; i < countof(states); i++) + { + if (io.MouseDown[i] == states[i]) + continue; + + io.MouseDown[i] = states[i]; + HandleHostMouseEvent(static_cast(i), states[i]); + } + }); + } +} + +std::optional UWPHostInterface::GetHostKeyCode(const std::string_view key_code) const +{ + for (const auto& it : s_key_map) + { + if (key_code.compare(it.second) == 0) + return static_cast(it.first); + } + + return std::nullopt; +} + +const char* UWPHostInterface::GetKeyCodeName(int key_code) +{ + const auto it = s_key_map.find(key_code); + return (it != s_key_map.end()) ? it->second : nullptr; +} + +void UWPHostInterface::SetImGuiKeyMap() +{ + using namespace winrt::Windows::System; + + ImGuiIO& io = ImGui::GetIO(); + io.KeyMap[ImGuiKey_Tab] = static_cast(VirtualKey::Tab); + io.KeyMap[ImGuiKey_LeftArrow] = static_cast(VirtualKey::Left); + io.KeyMap[ImGuiKey_RightArrow] = static_cast(VirtualKey::Right); + io.KeyMap[ImGuiKey_UpArrow] = static_cast(VirtualKey::Up); + io.KeyMap[ImGuiKey_DownArrow] = static_cast(VirtualKey::Down); + io.KeyMap[ImGuiKey_PageUp] = static_cast(VirtualKey::PageUp); + io.KeyMap[ImGuiKey_PageDown] = static_cast(VirtualKey::PageDown); + io.KeyMap[ImGuiKey_Home] = static_cast(VirtualKey::Home); + io.KeyMap[ImGuiKey_End] = static_cast(VirtualKey::End); + io.KeyMap[ImGuiKey_Insert] = static_cast(VirtualKey::Insert); + io.KeyMap[ImGuiKey_Delete] = static_cast(VirtualKey::Delete); + io.KeyMap[ImGuiKey_Backspace] = static_cast(VirtualKey::Back); + io.KeyMap[ImGuiKey_Space] = static_cast(VirtualKey::Space); + io.KeyMap[ImGuiKey_Enter] = static_cast(VirtualKey::Enter); + io.KeyMap[ImGuiKey_Escape] = static_cast(VirtualKey::Escape); + io.KeyMap[ImGuiKey_A] = static_cast(VirtualKey::A); + io.KeyMap[ImGuiKey_C] = static_cast(VirtualKey::C); + io.KeyMap[ImGuiKey_V] = static_cast(VirtualKey::V); + io.KeyMap[ImGuiKey_X] = static_cast(VirtualKey::X); + io.KeyMap[ImGuiKey_Y] = static_cast(VirtualKey::Y); + io.KeyMap[ImGuiKey_Z] = static_cast(VirtualKey::Z); +} + +void UWPHostInterface::SetDefaultSettingsForXbox(SettingsInterface& si) +{ + si.SetStringValue("GPU", "Renderer", "D3D12"); + + si.SetBoolValue("Main", "SyncToHostRefreshRate", true); + si.SetBoolValue("Display", "VSync", true); + si.SetBoolValue("Display", "DisplayAllFrames", true); + si.SetFloatValue("Display", "MaxFPS", 60.0f); + + // Set up an analog controller in port 1. + si.SetStringValue("Controller1", "Type", "AnalogController"); + si.SetStringValue("Controller1", "ButtonUp", "Controller0/Button11"); + si.SetStringValue("Controller1", "ButtonDown", "Controller0/Button12"); + si.SetStringValue("Controller1", "ButtonLeft", "Controller0/Button13"); + si.SetStringValue("Controller1", "ButtonRight", "Controller0/Button14"); + si.SetStringValue("Controller1", "ButtonStart", "Controller0/Button6"); + si.SetStringValue("Controller1", "ButtonTriangle", "Controller0/Button3"); + si.SetStringValue("Controller1", "ButtonCross", "Controller0/Button0"); + si.SetStringValue("Controller1", "ButtonCircle", "Controller0/Button1"); + si.SetStringValue("Controller1", "ButtonSquare", "Controller0/Button2"); + si.SetStringValue("Controller1", "ButtonL1", "Controller0/Button9"); + si.SetStringValue("Controller1", "ButtonL2", "Controller0/+Axis4"); + si.SetStringValue("Controller1", "ButtonR1", "Controller0/Button10"); + si.SetStringValue("Controller1", "ButtonR2", "Controller0/+Axis5"); + si.SetStringValue("Controller1", "ButtonL3", "Controller0/Button7"); + si.SetStringValue("Controller1", "ButtonR3", "Controller0/Button8"); + si.SetStringValue("Controller1", "AxisLeftX", "Controller0/Axis0"); + si.SetStringValue("Controller1", "AxisLeftY", "Controller0/Axis1"); + si.SetStringValue("Controller1", "AxisRightX", "Controller0/Axis2"); + si.SetStringValue("Controller1", "AxisRightY", "Controller0/Axis3"); + si.SetStringValue("Controller1", "Rumble", "Controller0"); + si.SetStringValue("Controller1", "ForceAnalogOnReset", "true"); + si.SetStringValue("Controller1", "AnalogDPadInDigitalMode", "true"); + + // Repurpose the select button to open the menu. + // Not ideal, but all we can do until we have chords. + si.SetStringValue("Hotkeys", "OpenQuickMenu", "Controller0/Button4"); +} + +int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) +{ + winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make()); +} diff --git a/src/duckstation-uwp/uwp_host_interface.h b/src/duckstation-uwp/uwp_host_interface.h new file mode 100644 index 000000000..a0c2fc04b --- /dev/null +++ b/src/duckstation-uwp/uwp_host_interface.h @@ -0,0 +1,117 @@ +#pragma once +#include "common/event.h" +#include "common/window_info.h" +#include "common/windows_headers.h" +#include "core/host_display.h" +#include "core/host_interface.h" +#include "frontend-common/common_host_interface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class INISettingsInterface; + +class UWPHostInterface + : public CommonHostInterface, + public winrt::implements +{ +public: + UWPHostInterface(); + ~UWPHostInterface(); + + const char* GetFrontendName() const override; + bool Initialize() override; + void Shutdown() override; + + void ReportMessage(const char* message) override; + void ReportError(const char* message) override; + bool ConfirmMessage(const char* message) override; + + void RunLater(std::function callback) override; + + bool IsFullscreen() const override; + bool SetFullscreen(bool enabled) override; + + winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView(); + void Initialize(const winrt::Windows::ApplicationModel::Core::CoreApplicationView&); + void Load(const winrt::hstring&); + void Uninitialize(); + void Run(); + void SetWindow(const winrt::Windows::UI::Core::CoreWindow& window); + +protected: + enum : u32 + { + DEFAULT_WINDOW_WIDTH = 1280, + DEFAULT_WINDOW_HEIGHT = 720 + }; + + ALWAYS_INLINE bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); } + + void SetDefaultSettings(SettingsInterface& si) override; + + bool AcquireHostDisplay() override; + void ReleaseHostDisplay() override; + + void PollAndUpdate() override; + void RequestExit() override; + + bool CreateDisplay(bool fullscreen); + void DestroyDisplay(); + void RunCallbacks(); + void EmulationThreadEntryPoint(); + + bool SetDirectories(); + void OnSuspending(const IInspectable&, const winrt::Windows::ApplicationModel::SuspendingEventArgs& args); + void OnResuming(const IInspectable&, const IInspectable&); + void OnClosed(const IInspectable&, const winrt::Windows::UI::Core::CoreWindowEventArgs& args); + void OnSizeChanged(const IInspectable&, const winrt::Windows::UI::Core::WindowSizeChangedEventArgs& args); + void OnKeyDown(const IInspectable&, const winrt::Windows::UI::Core::KeyEventArgs& args); + void OnKeyUp(const IInspectable&, const winrt::Windows::UI::Core::KeyEventArgs& args); + void OnCharacterReceived(const IInspectable&, const winrt::Windows::UI::Core::CharacterReceivedEventArgs& args); + void OnPointerPressed(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args); + void OnPointerReleased(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args); + void OnPointerMoved(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args); + void OnPointerWheelChanged(const IInspectable&, const winrt::Windows::UI::Core::PointerEventArgs& args); + void UpdateMouseButtonState(const winrt::Windows::UI::Input::PointerPoint& point); + + std::optional GetHostKeyCode(const std::string_view key_code) const override; + const char* GetKeyCodeName(int key_code); + void SetImGuiKeyMap(); + + void SetDefaultSettingsForXbox(SettingsInterface& si); + + std::deque> m_queued_callbacks; + std::mutex m_queued_callbacks_lock; + + winrt::Windows::UI::Core::CoreWindow m_window{nullptr}; + winrt::Windows::UI::Core::CoreDispatcher m_dispatcher{nullptr}; + winrt::Windows::UI::ViewManagement::ApplicationView m_appview{nullptr}; + + std::thread m_emulation_thread; + std::atomic_bool m_shutdown_flag{false}; + + bool m_text_input_requested = false; + + Common::Event m_suspend_sync_event; + std::atomic_bool m_was_running_on_suspend{false}; + std::atomic_bool m_was_running_on_background{false}; +}; diff --git a/src/duckstation-uwp/uwp_key_map.h b/src/duckstation-uwp/uwp_key_map.h new file mode 100644 index 000000000..8c5379455 --- /dev/null +++ b/src/duckstation-uwp/uwp_key_map.h @@ -0,0 +1,149 @@ +#include +#include + +static const std::map s_key_map = { + {static_cast(winrt::Windows::System::VirtualKey::LeftButton), "LeftButton"}, + {static_cast(winrt::Windows::System::VirtualKey::RightButton), "RightButton"}, + {static_cast(winrt::Windows::System::VirtualKey::Cancel), "Cancel"}, + {static_cast(winrt::Windows::System::VirtualKey::MiddleButton), "MiddleButton"}, + {static_cast(winrt::Windows::System::VirtualKey::XButton1), "XButton1"}, + {static_cast(winrt::Windows::System::VirtualKey::XButton2), "XButton2"}, + {static_cast(winrt::Windows::System::VirtualKey::Back), "Back"}, + {static_cast(winrt::Windows::System::VirtualKey::Tab), "Tab"}, + {static_cast(winrt::Windows::System::VirtualKey::Clear), "Clear"}, + {static_cast(winrt::Windows::System::VirtualKey::Enter), "Return"}, + {static_cast(winrt::Windows::System::VirtualKey::Shift), "Shift"}, + {static_cast(winrt::Windows::System::VirtualKey::Control), "Control"}, + {static_cast(winrt::Windows::System::VirtualKey::Menu), "Menu"}, + {static_cast(winrt::Windows::System::VirtualKey::Pause), "Pause"}, + {static_cast(winrt::Windows::System::VirtualKey::CapitalLock), "CapitalLock"}, + {static_cast(winrt::Windows::System::VirtualKey::Kana), "Kana"}, + {static_cast(winrt::Windows::System::VirtualKey::Hangul), "Hangul"}, + {static_cast(winrt::Windows::System::VirtualKey::Junja), "Junja"}, + {static_cast(winrt::Windows::System::VirtualKey::Final), "Final"}, + {static_cast(winrt::Windows::System::VirtualKey::Hanja), "Hanja"}, + {static_cast(winrt::Windows::System::VirtualKey::Kanji), "Kanji"}, + {static_cast(winrt::Windows::System::VirtualKey::Escape), "Escape"}, + {static_cast(winrt::Windows::System::VirtualKey::Convert), "Convert"}, + {static_cast(winrt::Windows::System::VirtualKey::NonConvert), "NonConvert"}, + {static_cast(winrt::Windows::System::VirtualKey::Accept), "Accept"}, + {static_cast(winrt::Windows::System::VirtualKey::ModeChange), "ModeChange"}, + {static_cast(winrt::Windows::System::VirtualKey::Space), "Space"}, + {static_cast(winrt::Windows::System::VirtualKey::PageUp), "PageUp"}, + {static_cast(winrt::Windows::System::VirtualKey::PageDown), "PageDown"}, + {static_cast(winrt::Windows::System::VirtualKey::End), "End"}, + {static_cast(winrt::Windows::System::VirtualKey::Home), "Home"}, + {static_cast(winrt::Windows::System::VirtualKey::Left), "Left"}, + {static_cast(winrt::Windows::System::VirtualKey::Up), "Up"}, + {static_cast(winrt::Windows::System::VirtualKey::Right), "Right"}, + {static_cast(winrt::Windows::System::VirtualKey::Down), "Down"}, + {static_cast(winrt::Windows::System::VirtualKey::Select), "Select"}, + {static_cast(winrt::Windows::System::VirtualKey::Print), "Print"}, + {static_cast(winrt::Windows::System::VirtualKey::Execute), "Execute"}, + {static_cast(winrt::Windows::System::VirtualKey::Snapshot), "Snapshot"}, + {static_cast(winrt::Windows::System::VirtualKey::Insert), "Insert"}, + {static_cast(winrt::Windows::System::VirtualKey::Delete), "Delete"}, + {static_cast(winrt::Windows::System::VirtualKey::Help), "Help"}, + {static_cast(winrt::Windows::System::VirtualKey::Number0), "Number0"}, + {static_cast(winrt::Windows::System::VirtualKey::Number1), "Number1"}, + {static_cast(winrt::Windows::System::VirtualKey::Number2), "Number2"}, + {static_cast(winrt::Windows::System::VirtualKey::Number3), "Number3"}, + {static_cast(winrt::Windows::System::VirtualKey::Number4), "Number4"}, + {static_cast(winrt::Windows::System::VirtualKey::Number5), "Number5"}, + {static_cast(winrt::Windows::System::VirtualKey::Number6), "Number6"}, + {static_cast(winrt::Windows::System::VirtualKey::Number7), "Number7"}, + {static_cast(winrt::Windows::System::VirtualKey::Number8), "Number8"}, + {static_cast(winrt::Windows::System::VirtualKey::Number9), "Number9"}, + {static_cast(winrt::Windows::System::VirtualKey::A), "A"}, + {static_cast(winrt::Windows::System::VirtualKey::B), "B"}, + {static_cast(winrt::Windows::System::VirtualKey::C), "C"}, + {static_cast(winrt::Windows::System::VirtualKey::D), "D"}, + {static_cast(winrt::Windows::System::VirtualKey::E), "E"}, + {static_cast(winrt::Windows::System::VirtualKey::F), "F"}, + {static_cast(winrt::Windows::System::VirtualKey::G), "G"}, + {static_cast(winrt::Windows::System::VirtualKey::H), "H"}, + {static_cast(winrt::Windows::System::VirtualKey::I), "I"}, + {static_cast(winrt::Windows::System::VirtualKey::J), "J"}, + {static_cast(winrt::Windows::System::VirtualKey::K), "K"}, + {static_cast(winrt::Windows::System::VirtualKey::L), "L"}, + {static_cast(winrt::Windows::System::VirtualKey::M), "M"}, + {static_cast(winrt::Windows::System::VirtualKey::N), "N"}, + {static_cast(winrt::Windows::System::VirtualKey::O), "O"}, + {static_cast(winrt::Windows::System::VirtualKey::P), "P"}, + {static_cast(winrt::Windows::System::VirtualKey::Q), "Q"}, + {static_cast(winrt::Windows::System::VirtualKey::R), "R"}, + {static_cast(winrt::Windows::System::VirtualKey::S), "S"}, + {static_cast(winrt::Windows::System::VirtualKey::T), "T"}, + {static_cast(winrt::Windows::System::VirtualKey::U), "U"}, + {static_cast(winrt::Windows::System::VirtualKey::V), "V"}, + {static_cast(winrt::Windows::System::VirtualKey::W), "W"}, + {static_cast(winrt::Windows::System::VirtualKey::X), "X"}, + {static_cast(winrt::Windows::System::VirtualKey::Y), "Y"}, + {static_cast(winrt::Windows::System::VirtualKey::Z), "Z"}, + {static_cast(winrt::Windows::System::VirtualKey::LeftWindows), "LeftWindows"}, + {static_cast(winrt::Windows::System::VirtualKey::RightWindows), "RightWindows"}, + {static_cast(winrt::Windows::System::VirtualKey::Application), "Application"}, + {static_cast(winrt::Windows::System::VirtualKey::Sleep), "Sleep"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad0), "Keypad+0"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad1), "Keypad+1"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad2), "Keypad+2"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad3), "Keypad+3"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad4), "Keypad+4"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad5), "Keypad+5"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad6), "Keypad+6"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad7), "Keypad+7"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad8), "Keypad+8"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberPad9), "Keypad+9"}, + {static_cast(winrt::Windows::System::VirtualKey::Multiply), "Multiply"}, + {static_cast(winrt::Windows::System::VirtualKey::Add), "Add"}, + {static_cast(winrt::Windows::System::VirtualKey::Separator), "Separator"}, + {static_cast(winrt::Windows::System::VirtualKey::Subtract), "Subtract"}, + {static_cast(winrt::Windows::System::VirtualKey::Decimal), "Decimal"}, + {static_cast(winrt::Windows::System::VirtualKey::Divide), "Divide"}, + {static_cast(winrt::Windows::System::VirtualKey::F1), "F1"}, + {static_cast(winrt::Windows::System::VirtualKey::F2), "F2"}, + {static_cast(winrt::Windows::System::VirtualKey::F3), "F3"}, + {static_cast(winrt::Windows::System::VirtualKey::F4), "F4"}, + {static_cast(winrt::Windows::System::VirtualKey::F5), "F5"}, + {static_cast(winrt::Windows::System::VirtualKey::F6), "F6"}, + {static_cast(winrt::Windows::System::VirtualKey::F7), "F7"}, + {static_cast(winrt::Windows::System::VirtualKey::F8), "F8"}, + {static_cast(winrt::Windows::System::VirtualKey::F9), "F9"}, + {static_cast(winrt::Windows::System::VirtualKey::F10), "F10"}, + {static_cast(winrt::Windows::System::VirtualKey::F11), "F11"}, + {static_cast(winrt::Windows::System::VirtualKey::F12), "F12"}, + {static_cast(winrt::Windows::System::VirtualKey::F13), "F13"}, + {static_cast(winrt::Windows::System::VirtualKey::F14), "F14"}, + {static_cast(winrt::Windows::System::VirtualKey::F15), "F15"}, + {static_cast(winrt::Windows::System::VirtualKey::F16), "F16"}, + {static_cast(winrt::Windows::System::VirtualKey::F17), "F17"}, + {static_cast(winrt::Windows::System::VirtualKey::F18), "F18"}, + {static_cast(winrt::Windows::System::VirtualKey::F19), "F19"}, + {static_cast(winrt::Windows::System::VirtualKey::F20), "F20"}, + {static_cast(winrt::Windows::System::VirtualKey::F21), "F21"}, + {static_cast(winrt::Windows::System::VirtualKey::F22), "F22"}, + {static_cast(winrt::Windows::System::VirtualKey::F23), "F23"}, + {static_cast(winrt::Windows::System::VirtualKey::F24), "F24"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationView), "NavigationView"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationMenu), "NavigationMenu"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationUp), "NavigationUp"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationDown), "NavigationDown"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationLeft), "NavigationLeft"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationRight), "NavigationRight"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationAccept), "NavigationAccept"}, + {static_cast(winrt::Windows::System::VirtualKey::NavigationCancel), "NavigationCancel"}, + {static_cast(winrt::Windows::System::VirtualKey::NumberKeyLock), "NumberKeyLock"}, + {static_cast(winrt::Windows::System::VirtualKey::Scroll), "Scroll"}, + {static_cast(winrt::Windows::System::VirtualKey::LeftShift), "LeftShift"}, + {static_cast(winrt::Windows::System::VirtualKey::RightShift), "RightShift"}, + {static_cast(winrt::Windows::System::VirtualKey::LeftControl), "LeftControl"}, + {static_cast(winrt::Windows::System::VirtualKey::RightControl), "RightControl"}, + {static_cast(winrt::Windows::System::VirtualKey::LeftMenu), "LeftMenu"}, + {static_cast(winrt::Windows::System::VirtualKey::RightMenu), "RightMenu"}, + {static_cast(winrt::Windows::System::VirtualKey::GoBack), "GoBack"}, + {static_cast(winrt::Windows::System::VirtualKey::GoForward), "GoForward"}, + {static_cast(winrt::Windows::System::VirtualKey::Refresh), "Refresh"}, + {static_cast(winrt::Windows::System::VirtualKey::Stop), "Stop"}, + {static_cast(winrt::Windows::System::VirtualKey::Search), "Search"}, + {static_cast(winrt::Windows::System::VirtualKey::Favorites), "Favorites"}, + {static_cast(winrt::Windows::System::VirtualKey::GoHome), "GoHome"}}; diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index d0e40914d..ff7c02d1f 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -22,7 +22,6 @@ #include "core/system.h" #include "core/texture_replacements.h" #include "core/timers.h" -#include "cubeb_audio_stream.h" #include "fullscreen_ui.h" #include "game_list.h" #include "icon.h" @@ -39,6 +38,10 @@ #include #include +#ifndef _UWP +#include "cubeb_audio_stream.h" +#endif + #ifdef WITH_SDL2 #include "sdl_audio_stream.h" #endif @@ -53,6 +56,7 @@ #ifdef _WIN32 #include "common/windows_headers.h" +#include "xaudio2_audio_stream.h" #include #include #include @@ -173,6 +177,11 @@ void CommonHostInterface::InitializeUserDirectory() result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("shaders").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("textures").c_str(), false); + // Games directory for UWP because it's a pain to create them manually. +#ifdef _UWP + result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("games").c_str(), false); +#endif + if (!result) ReportError("Failed to create one or more user directories. This may cause issues at runtime."); } @@ -636,8 +645,10 @@ std::unique_ptr CommonHostInterface::CreateAudioStream(AudioBackend case AudioBackend::Null: return AudioStream::CreateNullAudioStream(); +#ifndef _UWP case AudioBackend::Cubeb: return CubebAudioStream::Create(); +#endif #ifdef _WIN32 case AudioBackend::XAudio2: @@ -997,7 +1008,7 @@ void CommonHostInterface::SetUserDirectory() } else { -#if defined(_WIN32) +#if defined(_WIN32) && !defined(_UWP) // On Windows, use My Documents\DuckStation. PWSTR documents_directory; if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory))) @@ -3221,7 +3232,7 @@ void CommonHostInterface::SetTimerResolutionIncreased(bool enabled) m_timer_resolution_increased = enabled; -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) if (enabled) timeBeginPeriod(1); else diff --git a/src/frontend-common/controller_interface.cpp b/src/frontend-common/controller_interface.cpp index a7fda44c8..47e78ccd7 100644 --- a/src/frontend-common/controller_interface.cpp +++ b/src/frontend-common/controller_interface.cpp @@ -111,6 +111,8 @@ static constexpr std::array(ControllerInterface::B #endif #ifdef _WIN32 TRANSLATABLE("ControllerInterface", "XInput"), +#endif +#ifdef WITH_DINPUT TRANSLATABLE("ControllerInterface", "DInput"), #endif #ifdef ANDROID @@ -155,9 +157,11 @@ ControllerInterface::Backend ControllerInterface::GetDefaultBackend() #include "sdl_controller_interface.h" #endif #ifdef _WIN32 -#include "dinput_controller_interface.h" #include "xinput_controller_interface.h" #endif +#ifdef WITH_DINPUT +#include "dinput_controller_interface.h" +#endif #ifdef WITH_EVDEV #include "evdev_controller_interface.h" #endif @@ -171,6 +175,8 @@ std::unique_ptr ControllerInterface::Create(Backend type) #ifdef _WIN32 if (type == Backend::XInput) return std::make_unique(); +#endif +#ifdef WITH_DINPUT if (type == Backend::DInput) return std::make_unique(); #endif diff --git a/src/frontend-common/controller_interface.h b/src/frontend-common/controller_interface.h index 717a97361..cd6930a02 100644 --- a/src/frontend-common/controller_interface.h +++ b/src/frontend-common/controller_interface.h @@ -23,6 +23,8 @@ public: #endif #ifdef _WIN32 XInput, +#endif +#ifdef WITH_DINPUT DInput, #endif #ifdef ANDROID diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index 72a5754b2..6674d4c77 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -251,7 +251,11 @@ bool D3D11HostDisplay::CreateRenderDevice(const WindowInfo& wi, std::string_view create_flags |= D3D11_CREATE_DEVICE_DEBUG; ComPtr temp_dxgi_factory; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(temp_dxgi_factory.GetAddressOf())); +#else + HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(temp_dxgi_factory.GetAddressOf())); +#endif if (FAILED(hr)) { Log_ErrorPrintf("Failed to create DXGI factory: 0x%08X", hr); @@ -385,6 +389,9 @@ bool D3D11HostDisplay::DoneRenderContextCurrent() bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode) { + HRESULT hr; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) if (m_window_info.type != WindowInfo::Type::Win32) return false; @@ -423,7 +430,7 @@ bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode) swap_chain_desc.BufferDesc.Height, m_using_flip_model_swap_chain ? "flip-discard" : "discard", swap_chain_desc.Windowed ? "windowed" : "full-screen"); - HRESULT hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf()); + hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf()); if (FAILED(hr) && m_using_flip_model_swap_chain) { Log_WarningPrintf("Failed to create a flip-discard swap chain, trying discard."); @@ -448,6 +455,42 @@ bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode) if (FAILED(hr)) Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed"); } +#else + if (m_window_info.type != WindowInfo::Type::WinRT) + return false; + + ComPtr factory2; + hr = m_dxgi_factory.As(&factory2); + if (FAILED(hr)) + { + Log_ErrorPrintf("Failed to get DXGI factory: %08X", hr); + return false; + } + + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; + swap_chain_desc.Width = m_window_info.surface_width; + swap_chain_desc.Height = m_window_info.surface_height; + swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.BufferCount = 3; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; + + m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode); + if (m_using_allow_tearing) + swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + + ComPtr swap_chain1; + hr = factory2->CreateSwapChainForCoreWindow(m_device.Get(), static_cast(m_window_info.window_handle), + &swap_chain_desc, nullptr, swap_chain1.GetAddressOf()); + if (FAILED(hr)) + { + Log_ErrorPrintf("CreateSwapChainForCoreWindow failed: 0x%08X", hr); + return false; + } + + m_swap_chain = swap_chain1; +#endif return CreateSwapChainRTV(); } @@ -476,18 +519,22 @@ bool D3D11HostDisplay::CreateSwapChainRTV() m_window_info.surface_width = backbuffer_desc.Width; m_window_info.surface_height = backbuffer_desc.Height; + Log_InfoPrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height); - BOOL fullscreen = FALSE; - DXGI_SWAP_CHAIN_DESC desc; - if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && - SUCCEEDED(m_swap_chain->GetDesc(&desc))) + if (m_window_info.type == WindowInfo::Type::Win32) { - m_window_info.surface_refresh_rate = static_cast(desc.BufferDesc.RefreshRate.Numerator) / - static_cast(desc.BufferDesc.RefreshRate.Denominator); - } - else - { - m_window_info.surface_refresh_rate = 0.0f; + BOOL fullscreen = FALSE; + DXGI_SWAP_CHAIN_DESC desc; + if (SUCCEEDED(m_swap_chain->GetFullscreenState(&fullscreen, nullptr)) && fullscreen && + SUCCEEDED(m_swap_chain->GetDesc(&desc))) + { + m_window_info.surface_refresh_rate = static_cast(desc.BufferDesc.RefreshRate.Numerator) / + static_cast(desc.BufferDesc.RefreshRate.Denominator); + } + else + { + m_window_info.surface_refresh_rate = 0.0f; + } } return true; @@ -870,7 +917,11 @@ void D3D11HostDisplay::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 he HostDisplay::AdapterAndModeList D3D11HostDisplay::StaticGetAdapterAndModeList() { ComPtr dxgi_factory; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(dxgi_factory.GetAddressOf())); +#else + HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(dxgi_factory.GetAddressOf())); +#endif if (FAILED(hr)) return {}; diff --git a/src/frontend-common/frontend-common.props b/src/frontend-common/frontend-common.props index 8cd4b58d0..6cddcadca 100644 --- a/src/frontend-common/frontend-common.props +++ b/src/frontend-common/frontend-common.props @@ -12,7 +12,7 @@ - + WITH_SDL2=1;WITH_DINPUT=1;WITH_DISCORD_PRESENCE=1;%(PreprocessorDefinitions) $(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\discord-rpc\include;%(AdditionalIncludeDirectories) @@ -22,5 +22,5 @@ - - \ No newline at end of file + + diff --git a/src/frontend-common/frontend-common.vcxproj b/src/frontend-common/frontend-common.vcxproj index bc3f13a9f..dbb02a4a4 100644 --- a/src/frontend-common/frontend-common.vcxproj +++ b/src/frontend-common/frontend-common.vcxproj @@ -4,9 +4,13 @@ - + + true + - + + true + @@ -14,7 +18,12 @@ - + + true + + + true + @@ -31,9 +40,15 @@ - - - + + true + + + true + + + true + @@ -41,9 +56,13 @@ - + + true + - + + true + @@ -51,7 +70,12 @@ - + + true + + + true + @@ -68,9 +92,15 @@ - - - + + true + + + true + + + true + @@ -84,4 +114,4 @@ - + \ No newline at end of file diff --git a/src/frontend-common/frontend-common.vcxproj.filters b/src/frontend-common/frontend-common.vcxproj.filters index 73d9bff4e..980b960cf 100644 --- a/src/frontend-common/frontend-common.vcxproj.filters +++ b/src/frontend-common/frontend-common.vcxproj.filters @@ -36,6 +36,7 @@ + @@ -73,8 +74,9 @@ + - + \ No newline at end of file diff --git a/src/frontend-common/http_downloader_uwp.cpp b/src/frontend-common/http_downloader_uwp.cpp new file mode 100644 index 000000000..4ea7b64e5 --- /dev/null +++ b/src/frontend-common/http_downloader_uwp.cpp @@ -0,0 +1,166 @@ +#include "http_downloader_uwp.h" +#include "common/assert.h" +#include "common/log.h" +#include "common/string_util.h" +#include "common/timer.h" +#include +Log_SetChannel(HTTPDownloaderWinHttp); + +#include +#include +#include + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Web::Http; + +namespace FrontendCommon { + +HTTPDownloaderUWP::HTTPDownloaderUWP(std::string user_agent) : HTTPDownloader(), m_user_agent(std::move(user_agent)) {} + +HTTPDownloaderUWP::~HTTPDownloaderUWP() = default; + +std::unique_ptr HTTPDownloader::Create(const char* user_agent) +{ + std::string user_agent_str; + if (user_agent) + user_agent_str = user_agent; + + return std::make_unique(user_agent ? std::string(user_agent) : std::string()); +} + +HTTPDownloader::Request* HTTPDownloaderUWP::InternalCreateRequest() +{ + Request* req = new Request(); + return req; +} + +void HTTPDownloaderUWP::InternalPollRequests() +{ + // noop - uses async +} + +bool HTTPDownloaderUWP::StartRequest(HTTPDownloader::Request* request) +{ + Request* req = static_cast(request); + + try + { + const std::wstring url_wide(StringUtil::UTF8StringToWideString(req->url)); + const Uri uri(url_wide); + + if (!m_user_agent.empty() && + !req->client.DefaultRequestHeaders().UserAgent().TryParseAdd(StringUtil::UTF8StringToWideString(m_user_agent))) + { + Log_WarningPrintf("Failed to set user agent to '%s'", m_user_agent.c_str()); + } + + if (req->type == Request::Type::Post) + { + const winrt::Windows::Storage::Streams::Buffer post_buf(static_cast(req->post_data.size())); + std::memcpy(post_buf.data(), req->post_data.data(), req->post_data.size()); + + const HttpBufferContent post_content(post_buf); + req->request_async = req->client.PostAsync(uri, post_content); + } + else + { + req->request_async = req->client.GetAsync(uri); + } + + req->request_async.Completed( + [req](const IAsyncOperationWithProgress& operation, AsyncStatus status) { + if (status == AsyncStatus::Completed) + { + Log_DevPrintf("Request for '%s' completed start portion", req->url.c_str()); + try + { + req->state.store(Request::State::Receiving); + req->start_time = Common::Timer::GetValue(); + + const HttpResponseMessage response(req->request_async.get()); + req->status_code = static_cast(response.StatusCode()); + + const IHttpContent content(response.Content()); + req->receive_async = content.ReadAsBufferAsync(); + req->receive_async.Completed( + [req]( + const IAsyncOperationWithProgress& inner_operation, + AsyncStatus inner_status) { + if (inner_status == AsyncStatus::Completed) + { + const winrt::Windows::Storage::Streams::IBuffer buffer(inner_operation.get()); + if (buffer && buffer.Length() > 0) + { + req->data.resize(buffer.Length()); + std::memcpy(req->data.data(), buffer.data(), req->data.size()); + } + + Log_DevPrintf("End of request '%s', %zu bytes received", req->url.c_str(), req->data.size()); + req->state.store(Request::State::Complete); + } + else if (inner_status == AsyncStatus::Canceled) + { + // don't do anything, the request has been freed + } + else + { + Log_ErrorPrintf("Request for '%s' failed during recieve phase: %08X", req->url.c_str(), + inner_operation.ErrorCode().value); + req->status_code = -1; + req->state.store(Request::State::Complete); + } + }); + } + catch (const winrt::hresult_error& err) + { + Log_ErrorPrintf("Failed to receive HTTP request for '%s': %08X %s", req->url.c_str(), err.code(), + StringUtil::WideStringToUTF8String(err.message()).c_str()); + req->status_code = -1; + req->state.store(Request::State::Complete); + } + + req->receive_async = nullptr; + } + else if (status == AsyncStatus::Canceled) + { + // don't do anything, the request has been freed + } + else + { + Log_ErrorPrintf("Request for '%s' failed during start phase: %08X", req->url.c_str(), + operation.ErrorCode().value); + req->status_code = -1; + req->state.store(Request::State::Complete); + } + + req->request_async = nullptr; + }); + } + catch (const winrt::hresult_error& err) + { + Log_ErrorPrintf("Failed to start HTTP request for '%s': %08X %s", req->url.c_str(), err.code(), + StringUtil::WideStringToUTF8String(err.message()).c_str()); + req->callback(-1, req->data); + delete req; + return false; + } + + Log_DevPrintf("Started HTTP request for '%s'", req->url.c_str()); + req->state = Request::State::Started; + req->start_time = Common::Timer::GetValue(); + return true; +} + +void HTTPDownloaderUWP::CloseRequest(HTTPDownloader::Request* request) +{ + Request* req = static_cast(request); + if (req->request_async) + req->request_async.Cancel(); + if (req->receive_async) + req->receive_async.Cancel(); + + req->client.Close(); + delete req; +} + +} // namespace FrontendCommon \ No newline at end of file diff --git a/src/frontend-common/http_downloader_uwp.h b/src/frontend-common/http_downloader_uwp.h new file mode 100644 index 000000000..28d82d9da --- /dev/null +++ b/src/frontend-common/http_downloader_uwp.h @@ -0,0 +1,37 @@ +#pragma once +#include "http_downloader.h" + +#include "common/windows_headers.h" + +#include + +namespace FrontendCommon { + +class HTTPDownloaderUWP final : public HTTPDownloader +{ +public: + HTTPDownloaderUWP(std::string user_agent); + ~HTTPDownloaderUWP() override; + +protected: + Request* InternalCreateRequest() override; + void InternalPollRequests() override; + bool StartRequest(HTTPDownloader::Request* request) override; + void CloseRequest(HTTPDownloader::Request* request) override; + +private: + struct Request : HTTPDownloader::Request + { + std::wstring object_name; + winrt::Windows::Web::Http::HttpClient client; + winrt::Windows::Foundation::IAsyncOperationWithProgress + request_async{nullptr}; + winrt::Windows::Foundation::IAsyncOperationWithProgress + receive_async{}; + }; + + std::string m_user_agent; +}; + +} // namespace FrontendCommon \ No newline at end of file diff --git a/src/frontend-common/inhibit_screensaver.cpp b/src/frontend-common/inhibit_screensaver.cpp index e0650d53d..16e681298 100644 --- a/src/frontend-common/inhibit_screensaver.cpp +++ b/src/frontend-common/inhibit_screensaver.cpp @@ -4,7 +4,7 @@ #include Log_SetChannel(FrontendCommon); -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) #include "common/windows_headers.h" static bool SetScreensaverInhibitWin32(bool inhibit, const WindowInfo& wi) @@ -93,7 +93,7 @@ static bool SetScreensaverInhibit(bool inhibit, const WindowInfo& wi) { switch (wi.type) { -#ifdef _WIN32 +#if defined(_WIN32) && !defined(_UWP) case WindowInfo::Type::Win32: return SetScreensaverInhibitWin32(inhibit, wi); #endif diff --git a/src/frontend-common/xinput_controller_interface.cpp b/src/frontend-common/xinput_controller_interface.cpp index aa4059246..13d139f91 100644 --- a/src/frontend-common/xinput_controller_interface.cpp +++ b/src/frontend-common/xinput_controller_interface.cpp @@ -23,6 +23,7 @@ ControllerInterface::Backend XInputControllerInterface::GetBackend() const bool XInputControllerInterface::Initialize(CommonHostInterface* host_interface) { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) m_xinput_module = LoadLibraryW(L"xinput1_4"); if (!m_xinput_module) { @@ -45,6 +46,10 @@ bool XInputControllerInterface::Initialize(CommonHostInterface* host_interface) reinterpret_cast(GetProcAddress(m_xinput_module, "XInputGetState")); m_xinput_set_state = reinterpret_cast(GetProcAddress(m_xinput_module, "XInputSetState")); +#else + m_xinput_get_state = XInputGetState; + m_xinput_set_state = XInputSetState; +#endif if (!m_xinput_get_state || !m_xinput_set_state) { Log_ErrorPrintf("Failed to get XInput function pointers.");